14 #include <linux/dvb/dmx.h>
15 #include <linux/dvb/frontend.h>
16 #include <sys/ioctl.h>
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
106 { 2, TRANSMISSION_MODE_2K,
"2K" },
108 { 8, TRANSMISSION_MODE_8K,
"8K" },
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" },
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
315 virtual void Action(
void);
327 bool Locked(
int TimeoutMs = 0);
391 while (t->bondedTuner !=
this)
394 t->bondedTuner = NULL;
409 return diseqc->Commands();
413 bool VoltOff = dtp.Polarization() ==
'V' || dtp.Polarization() ==
'R';
425 if (t->device->Priority() >
IDLEPRIORITY || ConsiderOccupied && t->device->Occupied()) {
426 if (strcmp(BondingParams, t->GetBondedMaster()->GetBondingParams()) != 0)
472 if (BondedMaster ==
this) {
501 if (isLocked || !TimeoutMs)
514 dvb_frontend_event Event;
515 while (ioctl(
fd_frontend, FE_GET_EVENT, &Event) == 0)
524 if (ioctl(
fd_frontend, FE_READ_STATUS, &Status) != -1)
540 if (ioctl(
fd_frontend, FE_READ_SIGNAL_STRENGTH, &Signal) != -1)
545 uint16_t MaxSignal = 0xFFFF;
551 MaxSignal = 670;
break;
553 int s = int(Signal) * 100 / MaxSignal;
556 #ifdef DEBUG_SIGNALSTRENGTH
562 #define LOCK_THRESHOLD 5 // indicates that all 5 FE_HAS_* flags are set
569 if ((Status & FE_HAS_LOCK) == 0) {
570 if ((Status & FE_HAS_SIGNAL) == 0)
572 if ((Status & FE_HAS_CARRIER) == 0)
574 if ((Status & FE_HAS_VITERBI) == 0)
576 if ((Status & FE_HAS_SYNC) == 0)
580 #ifdef DEBUG_SIGNALQUALITY
587 if (errno != EINTR) {
589 #ifdef DEBUG_SIGNALQUALITY
595 #ifdef DEBUG_SIGNALQUALITY
602 if (errno != EINTR) {
604 #ifdef DEBUG_SIGNALQUALITY
610 #ifdef DEBUG_SIGNALQUALITY
615 if (ioctl(
fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &Unc) != -1)
617 if (errno != EINTR) {
619 #ifdef DEBUG_SIGNALQUALITY
625 uint16_t MinSnr = 0x0000;
626 uint16_t MaxSnr = 0xFFFF;
643 int a = int(
constrain(Snr, MinSnr, MaxSnr)) * 100 / (MaxSnr - MinSnr);
644 int b = 100 - (Unc * 10 + (Ber / 256) * 5);
650 #ifdef DEBUG_SIGNALQUALITY
651 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);
660 while (f && f < 1000000)
674 struct dvb_diseqc_master_cmd cmd;
675 const char *CurrentAction = NULL;
677 cmd.msg_len =
sizeof(cmd.msg);
689 default:
esyslog(
"ERROR: unknown diseqc command %d", da);
706 int ds = SYS_UNDEFINED;
710 ds = SYS_DVBC_ANNEX_AC;
711 else if (Channel->
IsSat())
713 else if (Channel->
IsTerr())
716 esyslog(
"ERROR: can't determine frontend type for channel %d", Channel->
Number());
722 #define MAXFRONTENDCMDS 16
723 #define SETCMD(c, d) { Frontend[CmdSeq.num].cmd = (c);\
724 Frontend[CmdSeq.num].u.data = (d);\
725 if (CmdSeq.num++ > MAXFRONTENDCMDS) {\
726 esyslog("ERROR: too many tuning commands on frontend %d/%d", adapter, frontend);\
731 memset(&Frontend, 0,
sizeof(Frontend));
732 dtv_properties CmdSeq;
733 memset(&CmdSeq, 0,
sizeof(CmdSeq));
734 CmdSeq.props = Frontend;
736 if (ioctl(
fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
746 if (frontendType == SYS_UNDEFINED)
749 SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
750 if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2) {
754 frequency -= diseqc->Lof();
772 int tone = SEC_TONE_OFF;
781 int volt = (dtp.Polarization() ==
'V' || dtp.Polarization() ==
'R') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
784 volt = SEC_VOLTAGE_13;
789 frequency = abs(frequency);
792 SETCMD(DTV_FREQUENCY, frequency * 1000UL);
793 SETCMD(DTV_MODULATION, dtp.Modulation());
795 SETCMD(DTV_INNER_FEC, dtp.CoderateH());
796 SETCMD(DTV_INVERSION, dtp.Inversion());
797 if (frontendType == SYS_DVBS2) {
799 SETCMD(DTV_PILOT, PILOT_AUTO);
800 SETCMD(DTV_ROLLOFF, dtp.RollOff());
806 SETCMD(DTV_ROLLOFF, ROLLOFF_35);
812 else if (frontendType == SYS_DVBC_ANNEX_AC || frontendType == SYS_DVBC_ANNEX_B) {
815 SETCMD(DTV_INVERSION, dtp.Inversion());
817 SETCMD(DTV_INNER_FEC, dtp.CoderateH());
818 SETCMD(DTV_MODULATION, dtp.Modulation());
823 else if (frontendType == SYS_DVBT || frontendType ==
SYS_DVBT2) {
826 SETCMD(DTV_INVERSION, dtp.Inversion());
827 SETCMD(DTV_BANDWIDTH_HZ, dtp.Bandwidth());
828 SETCMD(DTV_CODE_RATE_HP, dtp.CoderateH());
829 SETCMD(DTV_CODE_RATE_LP, dtp.CoderateL());
830 SETCMD(DTV_MODULATION, dtp.Modulation());
831 SETCMD(DTV_TRANSMISSION_MODE, dtp.Transmission());
832 SETCMD(DTV_GUARD_INTERVAL, dtp.Guard());
833 SETCMD(DTV_HIERARCHY, dtp.Hierarchy());
846 else if (frontendType == SYS_ATSC) {
849 SETCMD(DTV_INVERSION, dtp.Inversion());
850 SETCMD(DTV_MODULATION, dtp.Modulation());
856 esyslog(
"ERROR: attempt to set channel with unknown DVB frontend type");
860 if (ioctl(
fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
870 bool LostLock =
false;
871 fe_status_t Status = (fe_status_t)0;
873 fe_status_t NewStatus;
897 if (Status & FE_REINIT) {
904 else if (Status & FE_HAS_LOCK) {
966 #define ST(s) if (strchr(s, type))
981 default:
return NULL;
1041 if (fd_frontend >= 0) {
1069 int fd = open(FileName, Mode);
1070 if (fd < 0 && ReportError)
1078 if (access(FileName, F_OK) == 0) {
1079 int f = open(FileName, O_RDONLY);
1084 else if (errno != ENODEV && errno != EINVAL)
1087 else if (errno != ENOENT)
1095 dsyslog(
"probing %s", *FileName);
1097 if (dp->Probe(Adapter, Frontend))
1100 dsyslog(
"creating cDvbDevice");
1131 while ((a = DvbDir.
Next()) != NULL) {
1135 if (AdapterDir.
Ok()) {
1137 while ((f = AdapterDir.
Next()) != NULL) {
1149 if (Nodes.
Size() > 0) {
1151 for (
int i = 0; i < Nodes.
Size(); i++) {
1154 if (2 == sscanf(Nodes[i],
"%d %d", &Adapter, &Frontend)) {
1155 if (
Exists(Adapter, Frontend)) {
1158 if (
Probe(Adapter, Frontend))
1170 isyslog(
"found %d DVB device%s", Found, Found > 1 ?
"s" :
"");
1172 isyslog(
"no DVB device found");
1179 if (ioctl(fd_frontend, FE_GET_INFO, &
frontendInfo) < 0) {
1184 dtv_properties CmdSeq;
1187 memset(&Frontend, 0,
sizeof(Frontend));
1188 memset(&CmdSeq, 0,
sizeof(CmdSeq));
1190 SETCMD(DTV_API_VERSION, 0);
1191 if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
1199 bool LegacyMode =
true;
1201 memset(&Frontend, 0,
sizeof(Frontend));
1202 memset(&CmdSeq, 0,
sizeof(CmdSeq));
1205 int Result = ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq);
1207 for (uint i = 0; i < Frontend[0].u.buffer.len; i++) {
1217 esyslog(
"ERROR: can't query delivery systems on frontend %d/%d - falling back to legacy mode",
adapter,
frontend);
1251 ms =
"unknown modulations";
1275 int ErrorDevice = 0;
1278 if (
cDvbDevice *DvbDevice1 = dynamic_cast<cDvbDevice *>(Device1)) {
1279 if (
cDvbDevice *DvbDevice2 = dynamic_cast<cDvbDevice *>(Device2)) {
1280 if (!DvbDevice1->Bond(DvbDevice2))
1284 ErrorDevice = d + 1;
1287 ErrorDevice = i + 1;
1289 esyslog(
"ERROR: device '%d' in device bondings '%s' is not a cDvbDevice", ErrorDevice, Bondings);
1294 ErrorDevice = d + 1;
1297 ErrorDevice = i + 1;
1299 esyslog(
"ERROR: unknown device '%d' in device bondings '%s'", ErrorDevice, Bondings);
1320 if (Device !=
this) {
1330 esyslog(
"ERROR: can't bond device %d with device %d (only DVB-S(2) devices can be bonded)",
CardIndex() + 1, Device->
CardIndex() + 1);
1347 while (d->bondedDevice !=
this)
1348 d = d->bondedDevice;
1350 d->bondedDevice = NULL;
1373 dmx_pes_filter_params pesFilterParams;
1374 memset(&pesFilterParams, 0,
sizeof(pesFilterParams));
1376 if (Handle->
handle < 0) {
1378 if (Handle->
handle < 0) {
1383 pesFilterParams.pid = Handle->
pid;
1384 pesFilterParams.input = DMX_IN_FRONTEND;
1385 pesFilterParams.output = DMX_OUT_TS_TAP;
1386 pesFilterParams.pes_type= DMX_PES_OTHER;
1387 pesFilterParams.flags = DMX_IMMEDIATE_START;
1388 if (ioctl(Handle->
handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
1393 else if (!Handle->
used) {
1396 pesFilterParams.pid = 0x1FFF;
1397 pesFilterParams.input = DMX_IN_FRONTEND;
1398 pesFilterParams.output = DMX_OUT_DECODER;
1399 pesFilterParams.pes_type= DMX_PES_OTHER;
1400 pesFilterParams.flags = DMX_IMMEDIATE_START;
1401 CHECK(ioctl(Handle->
handle, DMX_SET_PES_FILTER, &pesFilterParams));
1413 int f = open(FileName, O_RDWR | O_NONBLOCK);
1415 dmx_sct_filter_params sctFilterParams;
1416 memset(&sctFilterParams, 0,
sizeof(sctFilterParams));
1417 sctFilterParams.pid = Pid;
1418 sctFilterParams.timeout = 0;
1419 sctFilterParams.flags = DMX_IMMEDIATE_START;
1420 sctFilterParams.filter.filter[0] = Tid;
1421 sctFilterParams.filter.mask[0] = Mask;
1422 if (ioctl(f, DMX_SET_FILTER, &sctFilterParams) >= 0)
1425 esyslog(
"ERROR: can't set filter (pid=%d, tid=%02X, mask=%02X): %m", Pid, Tid, Mask);
1430 esyslog(
"ERROR: can't open filter handle on '%s'", *FileName);
1465 dtp.Modulation() == QPSK && !(
frontendInfo.caps & FE_CAN_QPSK) ||
1466 dtp.Modulation() == QAM_16 && !(
frontendInfo.caps & FE_CAN_QAM_16) ||
1467 dtp.Modulation() == QAM_32 && !(
frontendInfo.caps & FE_CAN_QAM_32) ||
1468 dtp.Modulation() == QAM_64 && !(
frontendInfo.caps & FE_CAN_QAM_64) ||
1469 dtp.Modulation() == QAM_128 && !(
frontendInfo.caps & FE_CAN_QAM_128) ||
1470 dtp.Modulation() == QAM_256 && !(
frontendInfo.caps & FE_CAN_QAM_256) ||
1471 dtp.Modulation() == QAM_AUTO && !(
frontendInfo.caps & FE_CAN_QAM_AUTO) ||
1472 dtp.Modulation() == VSB_8 && !(
frontendInfo.caps & FE_CAN_8VSB) ||
1473 dtp.Modulation() == VSB_16 && !(
frontendInfo.caps & FE_CAN_16VSB) ||
1484 bool result =
false;
1486 bool needsDetachReceivers =
false;
1490 result = hasPriority;
1499 needsDetachReceivers =
true;
1519 needsDetachReceivers |= d->Receiving();
1527 if (NeedsDetachReceivers)
1528 *NeedsDetachReceivers = needsDetachReceivers;
1617 d->cDevice::DetachAllReceivers();
1629 DvbDeviceProbes.
Add(
this);
1634 DvbDeviceProbes.
Del(
this,
false);
1639 uint32_t SubsystemId = 0;
1642 if (stat(FileName, &st) == 0) {
1646 while ((e = d.
Next()) != NULL) {
1647 if (strstr(e->d_name,
"frontend")) {
1649 if (FILE *f = fopen(FileName,
"r")) {
1651 char *s = ReadLine.
Read(f);
1655 if (s && 2 == sscanf(s,
"%u:%u", &Major, &Minor)) {
1656 if (((Major << 8) | Minor) == st.st_rdev) {
1657 FileName =
cString::sprintf(
"/sys/class/dvb/%s/device/subsystem_vendor", e->d_name);
1658 if ((f = fopen(FileName,
"r")) != NULL) {
1659 if (
char *s = ReadLine.
Read(f))
1660 SubsystemId = strtoul(s, NULL, 0) << 16;
1663 FileName =
cString::sprintf(
"/sys/class/dvb/%s/device/subsystem_device", e->d_name);
1664 if ((f = fopen(FileName,
"r")) != NULL) {
1665 if (
char *s = ReadLine.
Read(f))
1666 SubsystemId |= strtoul(s, NULL, 0);