vdr 2.6.1
dvbdevice.c
Go to the documentation of this file.
1/*
2 * dvbdevice.c: The DVB device tuner interface
3 *
4 * See the main source file 'vdr.c' for copyright information and
5 * how to reach the author.
6 *
7 * $Id: dvbdevice.c 5.1 2021/06/09 09:12:25 kls Exp $
8 */
9
10#include "dvbdevice.h"
11#include <ctype.h>
12#include <errno.h>
13#include <limits.h>
14#include <linux/dvb/dmx.h>
15#include <linux/dvb/frontend.h>
16#include <sys/ioctl.h>
17#include <sys/mman.h>
18#include "channels.h"
19#include "diseqc.h"
20#include "dvbci.h"
21#include "menuitems.h"
22#include "sourceparams.h"
23
24static int DvbApiVersion = 0x0000; // the version of the DVB driver actually in use (will be determined by the first device created)
25
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
34
35#define SCR_RANDOM_TIMEOUT 500 // ms (add random value up to this when tuning SCR device to avoid lockups)
36
37#define TSBUFFERSIZE MEGABYTE(16)
38
39// --- DVB Parameter Maps ----------------------------------------------------
40
42 { 0, PILOT_OFF, trNOOP("off") },
43 { 1, PILOT_ON, trNOOP("on") },
44 { 999, PILOT_AUTO, trNOOP("auto") },
45 { -1, 0, NULL }
46 };
47
49 { 0, INVERSION_OFF, trNOOP("off") },
50 { 1, INVERSION_ON, trNOOP("on") },
51 { 999, INVERSION_AUTO, trNOOP("auto") },
52 { -1, 0, NULL }
53 };
54
56 { 5, 5000000, "5 MHz" },
57 { 6, 6000000, "6 MHz" },
58 { 7, 7000000, "7 MHz" },
59 { 8, 8000000, "8 MHz" },
60 { 10, 10000000, "10 MHz" },
61 { 1712, 1712000, "1.712 MHz" },
62 { -1, 0, NULL }
63 };
64
66 { 0, FEC_NONE, trNOOP("none") },
67 { 12, FEC_1_2, "1/2" },
68 { 23, FEC_2_3, "2/3" },
69 { 34, FEC_3_4, "3/4" },
70 { 35, FEC_3_5, "3/5" },
71 { 45, FEC_4_5, "4/5" },
72 { 56, FEC_5_6, "5/6" },
73 { 67, FEC_6_7, "6/7" },
74 { 78, FEC_7_8, "7/8" },
75 { 89, FEC_8_9, "8/9" },
76 { 910, FEC_9_10, "9/10" },
77 { 999, FEC_AUTO, trNOOP("auto") },
78 { -1, 0, NULL }
79 };
80
82 { 16, QAM_16, "QAM16" },
83 { 32, QAM_32, "QAM32" },
84 { 64, QAM_64, "QAM64" },
85 { 128, QAM_128, "QAM128" },
86 { 256, QAM_256, "QAM256" },
87 { 2, QPSK, "QPSK" },
88 { 5, PSK_8, "8PSK" },
89 { 6, APSK_16, "16APSK" },
90 { 7, APSK_32, "32APSK" },
91 { 10, VSB_8, "VSB8" },
92 { 11, VSB_16, "VSB16" },
93 { 12, DQPSK, "DQPSK" },
94 { 999, QAM_AUTO, trNOOP("auto") },
95 { -1, 0, NULL }
96 };
97
98#define DVB_SYSTEM_1 0 // see also nit.c
99#define DVB_SYSTEM_2 1
100
102 { 0, DVB_SYSTEM_1, "DVB-S" },
103 { 1, DVB_SYSTEM_2, "DVB-S2" },
104 { -1, 0, NULL }
105 };
106
108 { 0, DVB_SYSTEM_1, "DVB-T" },
109 { 1, DVB_SYSTEM_2, "DVB-T2" },
110 { -1, 0, NULL }
111 };
112
114 { 1, TRANSMISSION_MODE_1K, "1K" },
115 { 2, TRANSMISSION_MODE_2K, "2K" },
116 { 4, TRANSMISSION_MODE_4K, "4K" },
117 { 8, TRANSMISSION_MODE_8K, "8K" },
118 { 16, TRANSMISSION_MODE_16K, "16K" },
119 { 32, TRANSMISSION_MODE_32K, "32K" },
120 { 999, TRANSMISSION_MODE_AUTO, trNOOP("auto") },
121 { -1, 0, NULL }
122 };
123
125 { 4, GUARD_INTERVAL_1_4, "1/4" },
126 { 8, GUARD_INTERVAL_1_8, "1/8" },
127 { 16, GUARD_INTERVAL_1_16, "1/16" },
128 { 32, GUARD_INTERVAL_1_32, "1/32" },
129 { 128, GUARD_INTERVAL_1_128, "1/128" },
130 { 19128, GUARD_INTERVAL_19_128, "19/128" },
131 { 19256, GUARD_INTERVAL_19_256, "19/256" },
132 { 999, GUARD_INTERVAL_AUTO, trNOOP("auto") },
133 { -1, 0, NULL }
134 };
135
137 { 0, HIERARCHY_NONE, trNOOP("none") },
138 { 1, HIERARCHY_1, "1" },
139 { 2, HIERARCHY_2, "2" },
140 { 4, HIERARCHY_4, "4" },
141 { 999, HIERARCHY_AUTO, trNOOP("auto") },
142 { -1, 0, NULL }
143 };
144
146 { 0, ROLLOFF_AUTO, trNOOP("auto") },
147 { 20, ROLLOFF_20, "0.20" },
148 { 25, ROLLOFF_25, "0.25" },
149 { 35, ROLLOFF_35, "0.35" },
150 { -1, 0, NULL }
151 };
152
153int UserIndex(int Value, const tDvbParameterMap *Map)
154{
155 const tDvbParameterMap *map = Map;
156 while (map && map->userValue != -1) {
157 if (map->userValue == Value)
158 return map - Map;
159 map++;
160 }
161 return -1;
162}
163
164int DriverIndex(int Value, const tDvbParameterMap *Map)
165{
166 const tDvbParameterMap *map = Map;
167 while (map && map->userValue != -1) {
168 if (map->driverValue == Value)
169 return map - Map;
170 map++;
171 }
172 return -1;
173}
174
175int MapToUser(int Value, const tDvbParameterMap *Map, const char **String)
176{
177 int n = DriverIndex(Value, Map);
178 if (n >= 0) {
179 if (String)
180 *String = tr(Map[n].userString);
181 return Map[n].userValue;
182 }
183 return -1;
184}
185
186const char *MapToUserString(int Value, const tDvbParameterMap *Map)
187{
188 int n = DriverIndex(Value, Map);
189 if (n >= 0)
190 return Map[n].userString;
191 return "???";
192}
193
194int MapToDriver(int Value, const tDvbParameterMap *Map)
195{
196 int n = UserIndex(Value, Map);
197 if (n >= 0)
198 return Map[n].driverValue;
199 return -1;
200}
201
202// --- cDvbTransponderParameters ---------------------------------------------
203
205{
206 Parse(Parameters);
207}
208
209int cDvbTransponderParameters::PrintParameter(char *p, char Name, int Value) const
210{
211 return Value >= 0 && Value != 999 ? sprintf(p, "%c%d", Name, Value) : 0;
212}
213
215{
216#define ST(s) if (strchr(s, Type) && (strchr(s, '0' + system + 1) || strchr(s, '*')))
217 char buffer[64];
218 char *q = buffer;
219 *q = 0;
220 ST(" S *") q += sprintf(q, "%c", polarization);
221 ST(" T*") q += PrintParameter(q, 'B', MapToUser(bandwidth, BandwidthValues));
222 ST(" CST*") q += PrintParameter(q, 'C', MapToUser(coderateH, CoderateValues));
223 ST(" T*") q += PrintParameter(q, 'D', MapToUser(coderateL, CoderateValues));
224 ST(" T*") q += PrintParameter(q, 'G', MapToUser(guard, GuardValues));
225 ST("ACST*") q += PrintParameter(q, 'I', MapToUser(inversion, InversionValues));
226 ST("ACST*") q += PrintParameter(q, 'M', MapToUser(modulation, ModulationValues));
227 ST(" S 2") q += PrintParameter(q, 'N', MapToUser(pilot, PilotValues));
228 ST(" S 2") q += PrintParameter(q, 'O', MapToUser(rollOff, RollOffValues));
229 ST(" ST2") q += PrintParameter(q, 'P', streamId);
230 ST(" T2") q += PrintParameter(q, 'Q', t2systemId);
231 ST(" ST*") q += PrintParameter(q, 'S', MapToUser(system, SystemValuesSat)); // we only need the numerical value, so Sat or Terr doesn't matter
233 ST(" T2") q += PrintParameter(q, 'X', sisoMiso);
234 ST(" T*") q += PrintParameter(q, 'Y', MapToUser(hierarchy, HierarchyValues));
235 return buffer;
236}
237
238const char *cDvbTransponderParameters::ParseParameter(const char *s, int &Value, const tDvbParameterMap *Map)
239{
240 if (*++s) {
241 char *p = NULL;
242 errno = 0;
243 int n = strtol(s, &p, 10);
244 if (!errno && p != s) {
245 Value = Map ? MapToDriver(n, Map) : n;
246 if (Value >= 0)
247 return p;
248 }
249 }
250 esyslog("ERROR: invalid value for parameter '%c'", *(s - 1));
251 return NULL;
252}
253
255{
256 polarization = 0;
257 inversion = INVERSION_AUTO;
258 bandwidth = 8000000;
259 coderateH = FEC_AUTO;
260 coderateL = FEC_AUTO;
261 modulation = QPSK;
263 transmission = TRANSMISSION_MODE_AUTO;
264 guard = GUARD_INTERVAL_AUTO;
265 hierarchy = HIERARCHY_AUTO;
266 rollOff = ROLLOFF_AUTO;
267 streamId = 0;
268 t2systemId = 0;
269 sisoMiso = 0;
270 pilot = PILOT_AUTO;
271 while (s && *s) {
272 switch (toupper(*s)) {
273 case 'B': s = ParseParameter(s, bandwidth, BandwidthValues); break;
274 case 'C': s = ParseParameter(s, coderateH, CoderateValues); break;
275 case 'D': s = ParseParameter(s, coderateL, CoderateValues); break;
276 case 'G': s = ParseParameter(s, guard, GuardValues); break;
277 case 'H': polarization = 'H'; s++; break;
278 case 'I': s = ParseParameter(s, inversion, InversionValues); break;
279 case 'L': polarization = 'L'; s++; break;
280 case 'M': s = ParseParameter(s, modulation, ModulationValues); break;
281 case 'N': s = ParseParameter(s, pilot, PilotValues); break;
282 case 'O': s = ParseParameter(s, rollOff, RollOffValues); break;
283 case 'P': s = ParseParameter(s, streamId); break;
284 case 'Q': s = ParseParameter(s, t2systemId); break;
285 case 'R': polarization = 'R'; s++; break;
286 case 'S': s = ParseParameter(s, system, SystemValuesSat); break; // we only need the numerical value, so Sat or Terr doesn't matter
287 case 'T': s = ParseParameter(s, transmission, TransmissionValues); break;
288 case 'V': polarization = 'V'; s++; break;
289 case 'X': s = ParseParameter(s, sisoMiso); break;
290 case 'Y': s = ParseParameter(s, hierarchy, HierarchyValues); break;
291 default: esyslog("ERROR: unknown parameter key '%c'", *s);
292 return false;
293 }
294 }
295 return true;
296}
297
298// --- cDvbFrontend ----------------------------------------------------------
299
300const char *DeliverySystemNames[] = {
301 "???",
302 "DVB-C",
303 "DVB-C",
304 "DVB-T",
305 "DSS",
306 "DVB-S",
307 "DVB-S2",
308 "DVB-H",
309 "ISDBT",
310 "ISDBS",
311 "ISDBC",
312 "ATSC",
313 "ATSCMH",
314 "DTMB",
315 "CMMB",
316 "DAB",
317 "DVB-T2",
318 "TURBO",
319 "DVB-C",
320 "DVB-C2",
321 NULL
322 };
323
324static const int DeliverySystemNamesMax = sizeof(DeliverySystemNames) / sizeof(DeliverySystemNames[0]) - 2; // -1 to get the maximum allowed index & -1 for the NULL => -2
325
326static const char *GetDeliverySystemName(int Index)
327{
328 if (Index > DeliverySystemNamesMax)
329 Index = 0;
330 return DeliverySystemNames[Index];
331};
332
333#define MAXFRONTENDCMDS 16
334#define SETCMD(c, d) { Props[CmdSeq.num].cmd = (c);\
335 Props[CmdSeq.num].u.data = (d);\
336 if (CmdSeq.num++ > MAXFRONTENDCMDS) {\
337 esyslog("ERROR: too many tuning commands on frontend %d/%d", adapter, frontend);\
338 return false;\
339 }\
340 }
341
343private:
346 uint32_t subsystemId;
347 dvb_frontend_info frontendInfo;
350 bool QueryDeliverySystems(void);
351public:
352 cDvbFrontend(int Adapter, int Frontend);
354 int Open(void);
355 void Close(void);
356 const char *FrontendName(void) { return frontendInfo.name; }
357 bool ProvidesDeliverySystem(int DeliverySystem) const;
358 bool ProvidesModulation(int System, int StreamId, int Modulation) const;
359 int NumDeliverySystems(void) const { return deliverySystems.Size(); }
360 int NumModulations(void) const { return numModulations; }
361 uint32_t SubsystemId(void) const { return subsystemId; }
362 };
363
364cDvbFrontend::cDvbFrontend(int Adapter, int Frontend)
365{
366 adapter = Adapter;
367 frontend = Frontend;
368 fd_frontend = -1;
370 memset(&frontendInfo, 0, sizeof(frontendInfo));
371 strn0cpy(frontendInfo.name, "???", sizeof(frontendInfo.name));
372 numModulations = 0;
373 Open();
375 Close();
376}
377
379{
380 Close();
381}
382
384{
385 Close();
386 fd_frontend = DvbOpen(DEV_DVB_FRONTEND, adapter, frontend, O_RDWR | O_NONBLOCK, true);
387 return fd_frontend;
388}
389
391{
392 if (fd_frontend >= 0) {
393 if (close(fd_frontend) != 0)
394 esyslog("ERROR: frontend %d/%d", adapter, frontend);
395 fd_frontend = -1;
396 }
397}
398
399bool cDvbFrontend::ProvidesDeliverySystem(int DeliverySystem) const
400{
401 for (int i = 0; i < deliverySystems.Size(); i++) {
402 if (deliverySystems[i] == DeliverySystem)
403 return true;
404 }
405 return false;
406}
407
408bool cDvbFrontend::ProvidesModulation(int System, int StreamId, int Modulation) const
409{
410 if (StreamId != 0 && !(frontendInfo.caps & FE_CAN_MULTISTREAM))
411 return false;
412 if (Modulation == QPSK && !(frontendInfo.caps & FE_CAN_QPSK) ||
413 Modulation == QAM_16 && !(frontendInfo.caps & FE_CAN_QAM_16) ||
414 Modulation == QAM_32 && !(frontendInfo.caps & FE_CAN_QAM_32) ||
415 Modulation == QAM_64 && !(frontendInfo.caps & FE_CAN_QAM_64) ||
416 Modulation == QAM_128 && !(frontendInfo.caps & FE_CAN_QAM_128) ||
417 Modulation == QAM_256 && !(frontendInfo.caps & FE_CAN_QAM_256) ||
418 Modulation == QAM_AUTO && !(frontendInfo.caps & FE_CAN_QAM_AUTO) ||
419 Modulation == VSB_8 && !(frontendInfo.caps & FE_CAN_8VSB) ||
420 Modulation == VSB_16 && !(frontendInfo.caps & FE_CAN_16VSB) ||
421 Modulation == PSK_8 && !(frontendInfo.caps & FE_CAN_TURBO_FEC) && System == SYS_DVBS) // "turbo fec" is a non standard FEC used by North American broadcasters - this is a best guess to de
422 return false;
423 return true;
424}
425
427{
429 numModulations = 0;
430 if (ioctl(fd_frontend, FE_GET_INFO, &frontendInfo) < 0) {
431 LOG_ERROR;
432 return false;
433 }
434 dtv_property Props[1];
435 dtv_properties CmdSeq;
436 // Determine the version of the running DVB API:
437 if (!DvbApiVersion) {
438 memset(&Props, 0, sizeof(Props));
439 memset(&CmdSeq, 0, sizeof(CmdSeq));
440 CmdSeq.props = Props;
441 SETCMD(DTV_API_VERSION, 0);
442 if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
443 LOG_ERROR;
444 return false;
445 }
446 DvbApiVersion = Props[0].u.data;
447 isyslog("DVB API version is 0x%04X (VDR was built with 0x%04X)", DvbApiVersion, DVBAPIVERSION);
448 }
449 // Determine the types of delivery systems this device provides:
450 bool LegacyMode = true;
451 if (DvbApiVersion >= 0x0505) {
452 memset(&Props, 0, sizeof(Props));
453 memset(&CmdSeq, 0, sizeof(CmdSeq));
454 CmdSeq.props = Props;
456 int Result = ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq);
457 if (Result == 0) {
458 for (uint i = 0; i < Props[0].u.buffer.len; i++) {
459 // activate this line to simulate a multi-frontend device if you only have a single-frontend device with DVB-S and DVB-S2:
460 //if (frontend == 0 && Props[0].u.buffer.data[i] != SYS_DVBS || frontend == 1 && Props[0].u.buffer.data[i] != SYS_DVBS2)
461 deliverySystems.Append(Props[0].u.buffer.data[i]);
462 }
463 LegacyMode = false;
464 }
465 else {
466 esyslog("ERROR: can't query delivery systems on frontend %d/%d - falling back to legacy mode", adapter, frontend);
467 }
468 }
469 if (LegacyMode) {
470 // Legacy mode (DVB-API < 5.5):
471 switch (frontendInfo.type) {
472 case FE_QPSK: deliverySystems.Append(SYS_DVBS);
474 deliverySystems.Append(SYS_DVBS2);
475 break;
476 case FE_OFDM: deliverySystems.Append(SYS_DVBT);
479 break;
480 case FE_QAM: deliverySystems.Append(SYS_DVBC_ANNEX_AC); break;
481 case FE_ATSC: deliverySystems.Append(SYS_ATSC); break;
482 default: esyslog("ERROR: unknown frontend type %d on frontend %d/%d", frontendInfo.type, adapter, frontend);
483 }
484 }
485 if (deliverySystems.Size() > 0) {
486 cString ds("");
487 for (int i = 0; i < deliverySystems.Size(); i++)
488 ds = cString::sprintf("%s%s%s", *ds, i ? "," : "", GetDeliverySystemName(deliverySystems[i]));
489 cString ms("");
490 if (frontendInfo.caps & FE_CAN_QPSK) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QPSK, ModulationValues)); }
491 if (frontendInfo.caps & FE_CAN_QAM_16) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_16, ModulationValues)); }
492 if (frontendInfo.caps & FE_CAN_QAM_32) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_32, ModulationValues)); }
493 if (frontendInfo.caps & FE_CAN_QAM_64) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_64, ModulationValues)); }
494 if (frontendInfo.caps & FE_CAN_QAM_128) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_128, ModulationValues)); }
495 if (frontendInfo.caps & FE_CAN_QAM_256) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_256, ModulationValues)); }
496 if (frontendInfo.caps & FE_CAN_8VSB) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(VSB_8, ModulationValues)); }
497 if (frontendInfo.caps & FE_CAN_16VSB) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(VSB_16, ModulationValues)); }
498 if (frontendInfo.caps & FE_CAN_TURBO_FEC) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", "TURBO_FEC"); }
499 if (!**ms)
500 ms = "unknown modulations";
501 isyslog("frontend %d/%d provides %s with %s (\"%s\")", adapter, frontend, *ds, *ms, frontendInfo.name);
502 return true;
503 }
504 else
505 esyslog("ERROR: frontend %d/%d doesn't provide any delivery systems", adapter, frontend);
506 return false;
507}
508
509// --- cDvbTuner -------------------------------------------------------------
510
511#define TUNER_POLL_TIMEOUT 10 // ms
512
514{
515 int ds = SYS_UNDEFINED;
516 if (Channel->IsAtsc())
517 ds = SYS_ATSC;
518 else if (Channel->IsCable())
519 ds = SYS_DVBC_ANNEX_AC;
520 else if (Channel->IsSat())
521 ds = Dtp->System() == DVB_SYSTEM_1 ? SYS_DVBS : SYS_DVBS2;
522 else if (Channel->IsTerr())
523 ds = Dtp->System() == DVB_SYSTEM_1 ? SYS_DVBT : SYS_DVBT2;
524 else
525 esyslog("ERROR: can't determine frontend type for channel %d (%s)", Channel->Number(), Channel->Name());
526 return ds;
527}
528
529class cDvbTuner : public cThread {
530private:
535 mutable int fd_frontend;
537 mutable int frontend;
545 mutable uint32_t lastUncValue;
546 mutable uint32_t lastUncDelta;
547 mutable time_t lastUncChange;
553 const cScr *scr;
556 mutable cMutex mutex;
561 cString GetBondingParams(const cChannel *Channel = NULL) const;
563 bool IsBondedMaster(void) const { return !bondedTuner || bondedMaster; }
564 void ClearEventQueue(void) const;
565 bool GetFrontendStatus(fe_status_t &Status) const;
567 void ExecuteDiseqc(const cDiseqc *Diseqc, int *Frequency);
568 void ResetToneAndVoltage(void);
569 bool SetFrontend(void);
570 virtual void Action(void);
571public:
572 cDvbTuner(const cDvbDevice *Device, int Adapter, int Frontend);
573 virtual ~cDvbTuner();
574 bool ProvidesDeliverySystem(int DeliverySystem) const;
575 bool ProvidesModulation(int System, int StreamId, int Modulation) const;
576 bool ProvidesFrontend(const cChannel *Channel, bool Activate = false) const;
577 int Frontend(void) const { return frontend; }
578 int FrontendType(void) const { return frontendType; }
579 const char *FrontendName(void) { return dvbFrontend->FrontendName(); }
581 bool Bond(cDvbTuner *Tuner);
582 void UnBond(void);
583 bool BondingOk(const cChannel *Channel, bool ConsiderOccupied = false) const;
584 const cChannel *GetTransponder(void) const { return &channel; }
585 uint32_t SubsystemId(void) const { return dvbFrontend->SubsystemId(); }
586 bool IsTunedTo(const cChannel *Channel) const;
587 void SetChannel(const cChannel *Channel);
588 bool Locked(int TimeoutMs = 0);
589 const cPositioner *Positioner(void) const { return positioner; }
590 bool GetSignalStats(int &Valid, double *Strength = NULL, double *Cnr = NULL, double *BerPre = NULL, double *BerPost = NULL, double *Per = NULL, int *Status = NULL) const;
591 int GetSignalStrength(void) const;
592 int GetSignalQuality(void) const;
593 };
594
596
597cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Adapter, int Frontend)
598{
599 frontendType = SYS_UNDEFINED;
600 device = Device;
601 fd_frontend = -1;
602 adapter = Adapter;
604 dvbFrontend = NULL;
605 tuneTimeout = 0;
606 lockTimeout = 0;
608 lastUncValue = 0;
609 lastUncDelta = 0;
610 lastUncChange = 0;
611 lastDiseqc = NULL;
612 diseqcOffset = 0;
613 lastSource = 0;
614 positioner = NULL;
615 scr = NULL;
616 lnbPowerTurnedOn = false;
618 bondedTuner = NULL;
619 bondedMaster = false;
624 cString FrontendNumbers = cString::sprintf("%d", frontend);
625 // Check for multiple frontends:
626 if (frontend == 0) {
627 for (int i = 1; ; i++) {
628 if (access(DvbName(DEV_DVB_FRONTEND, adapter, i), F_OK) == 0) {
629 if (access(DvbName(DEV_DVB_DEMUX, adapter, i), F_OK) != 0) {
630 fe = new cDvbFrontend(adapter, i);
633 //numModulations += fe->NumModulations(); // looks like in multi frontend devices all frontends report the same modulations
634 FrontendNumbers = cString::sprintf("%s+%d", *FrontendNumbers, i);
635 }
636 }
637 else
638 break;
639 }
640 }
641 // Open default frontend:
644 SetDescription("frontend %d/%s tuner", adapter, *FrontendNumbers);
645 Start();
646}
647
649{
653 Cancel(3);
654 UnBond();
655 /* looks like this irritates the SCR switch, so let's leave it out for now
656 if (lastDiseqc && lastDiseqc->IsScr()) {
657 unsigned int Frequency = 0;
658 ExecuteDiseqc(lastDiseqc, &Frequency);
659 }
660 */
661 for (int i = 0; i < dvbFrontends.Size(); i++)
662 delete dvbFrontends[i];
663}
664
665bool cDvbTuner::ProvidesDeliverySystem(int DeliverySystem) const
666{
667 for (int i = 0; i < dvbFrontends.Size(); i++) {
668 if (dvbFrontends[i]->ProvidesDeliverySystem(DeliverySystem))
669 return true;
670 }
671 return false;
672}
673
674bool cDvbTuner::ProvidesModulation(int System, int StreamId, int Modulation) const
675{
676 for (int i = 0; i < dvbFrontends.Size(); i++) {
677 if (dvbFrontends[i]->ProvidesModulation(System, StreamId, Modulation))
678 return true;
679 }
680 return false;
681}
682
683bool cDvbTuner::ProvidesFrontend(const cChannel *Channel, bool Activate) const
684{
686 int DeliverySystem = GetRequiredDeliverySystem(Channel, &dtp);
687 for (int i = 0; i < dvbFrontends.Size(); i++) {
688 if (dvbFrontends[i]->ProvidesDeliverySystem(DeliverySystem) && dvbFrontends[i]->ProvidesModulation(dtp.System(), dtp.StreamId(), dtp.Modulation())) {
689 if (Activate && dvbFrontend != dvbFrontends[i]) {
690 cMutexLock MutexLock(&mutex);
694 frontend = i;
695 dsyslog("using frontend %d/%d", adapter, frontend);
696 lastUncValue = 0;
697 lastUncDelta = 0;
698 lastUncChange = 0;
699 }
700 return true;
701 }
702 }
703 return false;
704}
705
707{
708 cMutexLock MutexLock(&bondMutex);
709 if (!bondedTuner) {
711 bondedMaster = false; // makes sure we don't disturb an existing master
712 bondedTuner = Tuner->bondedTuner ? Tuner->bondedTuner : Tuner;
713 Tuner->bondedTuner = this;
714 dsyslog("tuner %d/%d bonded with tuner %d/%d", adapter, frontend, bondedTuner->adapter, bondedTuner->frontend);
715 return true;
716 }
717 else
718 esyslog("ERROR: tuner %d/%d already bonded with tuner %d/%d, can't bond with tuner %d/%d", adapter, frontend, bondedTuner->adapter, bondedTuner->frontend, Tuner->adapter, Tuner->frontend);
719 return false;
720}
721
723{
724 cMutexLock MutexLock(&bondMutex);
725 if (cDvbTuner *t = bondedTuner) {
726 dsyslog("tuner %d/%d unbonded from tuner %d/%d", adapter, frontend, bondedTuner->adapter, bondedTuner->frontend);
727 while (t->bondedTuner != this)
728 t = t->bondedTuner;
729 if (t == bondedTuner)
730 t->bondedTuner = NULL;
731 else
732 t->bondedTuner = bondedTuner;
733 bondedMaster = false; // another one will automatically become master whenever necessary
734 bondedTuner = NULL;
735 }
736}
737
739{
740 if (!Channel)
741 Channel = &channel;
743 if (Setup.DiSEqC) {
744 if (const cDiseqc *diseqc = Diseqcs.Get(device->DeviceNumber() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization(), NULL))
745 return diseqc->Commands();
746 }
747 else {
748 bool ToneOff = Channel->Frequency() < Setup.LnbSLOF;
749 bool VoltOff = dtp.Polarization() == 'V' || dtp.Polarization() == 'R';
750 return cString::sprintf("%c %c", ToneOff ? 't' : 'T', VoltOff ? 'v' : 'V');
751 }
752 return "";
753}
754
755bool cDvbTuner::BondingOk(const cChannel *Channel, bool ConsiderOccupied) const
756{
757 cMutexLock MutexLock(&bondMutex);
758 if (cDvbTuner *t = bondedTuner) {
759 cString BondingParams = GetBondingParams(Channel);
760 do {
761 if (t->device->Priority() > IDLEPRIORITY || ConsiderOccupied && t->device->Occupied()) {
762 if (strcmp(BondingParams, t->GetBondedMaster()->GetBondingParams()) != 0)
763 return false;
764 }
765 t = t->bondedTuner;
766 } while (t != bondedTuner);
767 }
768 return true;
769}
770
772{
773 if (!bondedTuner)
774 return this; // an unbonded tuner is always "master"
775 cMutexLock MutexLock(&bondMutex);
776 if (bondedMaster)
777 return this;
778 // This tuner is bonded, but it's not the master, so let's see if there is a master at all:
779 if (cDvbTuner *t = bondedTuner) {
780 while (t != this) {
781 if (t->bondedMaster)
782 return t;
783 t = t->bondedTuner;
784 }
785 }
786 // None of the other bonded tuners is master, so make this one the master:
787 bondedMaster = true;
788 dsyslog("tuner %d/%d is now bonded master", adapter, frontend);
789 return this;
790}
791
792bool cDvbTuner::IsTunedTo(const cChannel *Channel) const
793{
794 if (tunerStatus == tsIdle)
795 return false; // not tuned to
796 if (channel.Source() != Channel->Source() || channel.Transponder() != Channel->Transponder() || channel.Srate() != Channel->Srate())
797 return false; // sufficient mismatch
798 // Polarization is already checked as part of the Transponder.
799 return strcmp(channel.Parameters(), Channel->Parameters()) == 0;
800}
801
802void cDvbTuner::SetChannel(const cChannel *Channel)
803{
804 if (Channel) {
805 if (bondedTuner) {
806 cMutexLock MutexLock(&bondMutex);
807 cDvbTuner *BondedMaster = GetBondedMaster();
808 if (BondedMaster == this) {
809 if (strcmp(GetBondingParams(Channel), GetBondingParams()) != 0) {
810 // switching to a completely different band, so set all others to idle:
811 for (cDvbTuner *t = bondedTuner; t && t != this; t = t->bondedTuner)
812 t->SetChannel(NULL);
813 }
814 }
815 else if (strcmp(GetBondingParams(Channel), BondedMaster->GetBondingParams()) != 0)
816 BondedMaster->SetChannel(Channel);
817 }
818 cMutexLock MutexLock(&mutex);
819 if (!IsTunedTo(Channel))
821 diseqcOffset = 0;
822 channel = *Channel;
825 }
826 else {
827 cMutexLock MutexLock(&mutex);
830 }
832 cDevice::PrimaryDevice()->DelLivePids(); // 'device' is const, so we must do it this way
833}
834
835bool cDvbTuner::Locked(int TimeoutMs)
836{
837 bool isLocked = (tunerStatus >= tsLocked);
838 if (isLocked || !TimeoutMs)
839 return isLocked;
840
841 cMutexLock MutexLock(&mutex);
842 if (TimeoutMs && tunerStatus < tsLocked)
843 locked.TimedWait(mutex, TimeoutMs);
844 return tunerStatus >= tsLocked;
845}
846
848{
849 cPoller Poller(fd_frontend);
850 if (Poller.Poll(TUNER_POLL_TIMEOUT)) {
851 dvb_frontend_event Event;
852 while (ioctl(fd_frontend, FE_GET_EVENT, &Event) == 0)
853 ; // just to clear the event queue - we'll read the actual status below
854 }
855}
856
857bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
858{
860 Status = (fe_status_t)0; // initialize here to fix buggy drivers
861 while (1) {
862 if (ioctl(fd_frontend, FE_READ_STATUS, &Status) != -1)
863 return true;
864 if (errno != EINTR)
865 break;
866 }
867 return false;
868}
869
870//#define DEBUG_SIGNALSTATS
871//#define DEBUG_SIGNALSTRENGTH
872//#define DEBUG_SIGNALQUALITY
873
874bool cDvbTuner::GetSignalStats(int &Valid, double *Strength, double *Cnr, double *BerPre, double *BerPost, double *Per, int *Status) const
875{
877 fe_status_t FeStatus = (fe_status_t)0; // initialize here to fix buggy drivers
878 dtv_property Props[MAXFRONTENDCMDS];
879 dtv_properties CmdSeq;
880 memset(&Props, 0, sizeof(Props));
881 memset(&CmdSeq, 0, sizeof(CmdSeq));
882 CmdSeq.props = Props;
883 Valid = DTV_STAT_VALID_NONE;
884 if (ioctl(fd_frontend, FE_READ_STATUS, &FeStatus) != 0) {
885 esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
886 return false;
887 }
888 if (Status) {
889 *Status = DTV_STAT_HAS_NONE;
890 if (FeStatus & FE_HAS_SIGNAL) *Status |= DTV_STAT_HAS_SIGNAL;
891 if (FeStatus & FE_HAS_CARRIER) *Status |= DTV_STAT_HAS_CARRIER;
892 if (FeStatus & FE_HAS_VITERBI) *Status |= DTV_STAT_HAS_VITERBI;
893 if (FeStatus & FE_HAS_SYNC) *Status |= DTV_STAT_HAS_SYNC;
894 if (FeStatus & FE_HAS_LOCK) *Status |= DTV_STAT_HAS_LOCK;
895 Valid |= DTV_STAT_VALID_STATUS;
896 }
897 if (Strength) SETCMD(DTV_STAT_SIGNAL_STRENGTH, 0);
898 if (Cnr) SETCMD(DTV_STAT_CNR, 0);
899 if (BerPre) { SETCMD(DTV_STAT_PRE_ERROR_BIT_COUNT, 0);
900 SETCMD(DTV_STAT_PRE_TOTAL_BIT_COUNT, 0); }
901 if (BerPost) { SETCMD(DTV_STAT_POST_ERROR_BIT_COUNT, 0);
902 SETCMD(DTV_STAT_POST_TOTAL_BIT_COUNT, 0); }
903 if (Per) { SETCMD(DTV_STAT_ERROR_BLOCK_COUNT, 0);
904 SETCMD(DTV_STAT_TOTAL_BLOCK_COUNT, 0); }
905 if (CmdSeq.num && ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
906 esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
907 return false;
908 }
909 int i = 0;
910 if (Strength) {
911 if (Props[i].u.st.len > 0) {
912 switch (Props[i].u.st.stat[0].scale) {
913 case FE_SCALE_DECIBEL: *Strength = double(Props[i].u.st.stat[0].svalue) / 1000;
915 break;
916 default: ;
917 }
918 }
919 i++;
920 }
921 if (Cnr) {
922 if (Props[i].u.st.len > 0) {
923 switch (Props[i].u.st.stat[0].scale) {
924 case FE_SCALE_DECIBEL: *Cnr = double(Props[i].u.st.stat[0].svalue) / 1000;
925 Valid |= DTV_STAT_VALID_CNR;
926 break;
927 default: ;
928 }
929 }
930 i++;
931 }
932 if (BerPre) {
933 if (Props[i].u.st.len > 0 && Props[i + 1].u.st.len > 0) {
934 if (Props[i].u.st.stat[0].scale == FE_SCALE_COUNTER && Props[i + 1].u.st.stat[0].scale == FE_SCALE_COUNTER) {
935 uint64_t ebc = Props[i].u.st.stat[0].uvalue; // error bit count
936 uint64_t tbc = Props[i + 1].u.st.stat[0].uvalue; // total bit count
937 if (tbc > 0) {
938 *BerPre = double(ebc) / tbc;
939 Valid |= DTV_STAT_VALID_BERPRE;
940 }
941 }
942 }
943 i += 2;
944 }
945 if (BerPost) {
946 if (Props[i].u.st.len > 0 && Props[i + 1].u.st.len > 0) {
947 if (Props[i].u.st.stat[0].scale == FE_SCALE_COUNTER && Props[i + 1].u.st.stat[0].scale == FE_SCALE_COUNTER) {
948 uint64_t ebc = Props[i].u.st.stat[0].uvalue; // error bit count
949 uint64_t tbc = Props[i + 1].u.st.stat[0].uvalue; // total bit count
950 if (tbc > 0) {
951 *BerPost = double(ebc) / tbc;
952 Valid |= DTV_STAT_VALID_BERPOST;
953 }
954 }
955 }
956 i += 2;
957 }
958 if (Per) {
959 if (Props[i].u.st.len > 0 && Props[i + 1].u.st.len > 0) {
960 if (Props[i].u.st.stat[0].scale == FE_SCALE_COUNTER && Props[i + 1].u.st.stat[0].scale == FE_SCALE_COUNTER) {
961 uint64_t ebc = Props[i].u.st.stat[0].uvalue; // error block count
962 uint64_t tbc = Props[i + 1].u.st.stat[0].uvalue; // total block count
963 if (tbc > 0) {
964 *Per = double(ebc) / tbc;
965 Valid |= DTV_STAT_VALID_PER;
966 }
967 }
968 }
969 i += 2;
970 }
971#ifdef DEBUG_SIGNALSTATS
972 fprintf(stderr, "FE %d/%d: API5 %04X", adapter, frontend, Valid);
973 if ((Valid & DTV_STAT_VALID_STATUS) != 0) fprintf(stderr, " STAT=%04X", *Status);
974 if ((Valid & DTV_STAT_VALID_STRENGTH) != 0) fprintf(stderr, " STR=%1.1fdBm", *Strength);
975 if ((Valid & DTV_STAT_VALID_CNR) != 0) fprintf(stderr, " CNR=%1.1fdB", *Cnr);
976 if ((Valid & DTV_STAT_VALID_BERPRE) != 0) fprintf(stderr, " BERPRE=%1.1e", *BerPre);
977 if ((Valid & DTV_STAT_VALID_BERPOST) != 0) fprintf(stderr, " BERPOST=%1.1e", *BerPost);
978 if ((Valid & DTV_STAT_VALID_PER) != 0) fprintf(stderr, " PER=%1.1e", *Per);
979 fprintf(stderr, "\n");
980#endif
981 return Valid != DTV_STAT_VALID_NONE;
982}
983
984int dB1000toPercent(int dB1000, int Low, int High)
985{
986 // Convert the given value, which is in 1/1000 dBm, to a percentage in the
987 // range 0..100. Anything below Low is considered 0%, and anything above
988 // High counts as 100%.
989 if (dB1000 < Low)
990 return 0;
991 if (dB1000 > High)
992 return 100;
993 // return 100 - 100 * (High - dB1000) / (High - Low); // linear conversion
994 // return 100 - 100 * sqr(dB1000 - High) / sqr(Low - High); // quadratic conversion, see https://www.adriangranados.com/blog/dbm-to-percent-conversion
995 double v = 10.0 * (dB1000 - High) / (Low - High); // avoids the sqr() function
996 return 100 - v * v;
997}
998
999#define REF_S1(q1) (mod == QPSK) ? q1 : 0
1000#define REF_S2(q1, q2, q3, q4) (mod == QPSK) ? q1 : (mod == PSK_8) ? q2 : (mod == APSK_16) ? q3 : (mod == APSK_32) ? q4 : 0
1001#define REF_T1(q1, q2, q3) (mod == QPSK) ? q1 : (mod == QAM_16) ? q2 : (mod == QAM_64) ? q3 : 0
1002#define REF_T2(q1, q2, q3, q4) (mod == QPSK) ? q1 : (mod == QAM_16) ? q2 : (mod == QAM_64) ? q3 : (mod == QAM_256) ? q4 : 0
1003#define REF_C1(q1, q2, q3, q4, q5) (mod == QAM_16) ? q1 : (mod == QAM_32) ? q2 : (mod == QAM_64) ? q3 : (mod == QAM_128) ? q4 : (mod == QAM_256) ? q5: 0
1004
1005int StrengthToSSI(const cChannel *Channel, int Strength, int FeModulation, int FeCoderateH, int FeFec)
1006{
1007 // Strength in 0.001dBm (dBm x 1000)
1008 cDvbTransponderParameters dtp(Channel->Parameters());
1009 int ssi = 0; // 0-100
1010 int mod = (FeModulation >= 0) ? FeModulation : dtp.Modulation();
1011 int cod = (FeCoderateH >= 0) ? FeCoderateH : dtp.CoderateH(); // DVB-T
1012 int fec = (FeFec >= 0) ? FeFec : dtp.CoderateH();
1013 if (Channel->IsTerr()) {
1014 int pref = 0;
1015 // NorDig Unified Ver. 2.6 - 3.4.4.6 Page 43 ff.
1016 // reference values : pref-15dBm = 0%, pref+35dBm = 100%
1017 if (dtp.System() == DVB_SYSTEM_1) { // DVB-T
1018 fec = cod; // adjustment for DVB-T
1019 if (mod == QAM_AUTO) mod = QPSK;
1020 switch (fec) { // dBm: Q4 Q16 Q64
1021 case FEC_1_2: pref = REF_T1(-93, -87, -82); break;
1022 default:
1023 case FEC_2_3: pref = REF_T1(-91, -85, -80); break;
1024 case FEC_3_4: pref = REF_T1(-90, -84, -78); break;
1025 case FEC_5_6: pref = REF_T1(-89, -83, -77); break;
1026 case FEC_7_8: pref = REF_T1(-88, -82, -76); break;
1027 }
1028 }
1029 else { // DVB-T2
1030 if (mod == QAM_AUTO) mod = QAM_64;
1031 switch (fec) { // dBm: Q4 Q16 Q64 Q256
1032 case FEC_1_2: pref = REF_T2(-96, -91, -86, -82); break;
1033 default:
1034 case FEC_3_5: pref = REF_T2(-95, -89, -85, -80); break;
1035 case FEC_2_3: pref = REF_T2(-94, -88, -83, -78); break;
1036 case FEC_3_4: pref = REF_T2(-93, -87, -82, -76); break;
1037 case FEC_4_5: pref = REF_T2(-92, -86, -81, -75); break;
1038 case FEC_5_6: pref = REF_T2(-92, -86, -80, -74); break;
1039 }
1040 }
1041 if (pref) {
1042 int prel = (Strength / 1000) - pref;
1043 ssi = (prel < -15) ? 0 :
1044 (prel < 0) ? (prel + 15) * 2 / 3 : // 0% - 10%
1045 (prel < 20) ? prel * 4 + 10 : // 10% - 90%
1046 (prel < 35) ? (prel - 20) * 2 / 3 + 90 : // 90% - 100%
1047 100;
1048#ifdef DEBUG_SIGNALSTRENGTH
1049 fprintf(stderr, "SSI-T: STR:%d, Pref:%d, Prel:%d, ssi:%d%%(sys:%d, mod:%d, fec:%d)\n", Strength, pref, prel, ssi, dtp.System(), mod, fec);
1050#endif
1051 }
1052 }
1053 else if (Channel->IsCable()) { // ! COMPLETELY UNTESTED !
1054 // Formula: pref(dB) = -174.0 + NoiseFigure + SymRef + CnRef
1055 // NoiseFigure = 6.5 dB; -> Tuner specific - range: 3.5 .. 9.0 dB
1056 // SymRef = 10*log(6900000) = 68.5 dB; -> for Symbolrate of 6900 kSym/sec (TV: 6900, 6750 or 6111 kSym/sec)
1057 // ==> pref(dB) = -174.0 + 6.5 + 68.5 + CnRef[modulation]{20,23,26,29,32}; (+/- 3 dB tuner specific)
1058 if (mod == QAM_AUTO) mod = QAM_256;
1059 // Q16 Q32 Q64 Q128 Q256
1060 int pref = REF_C1(-79, -76, -73, -70, -67);
1061 if (pref) {
1062 int prel = (Strength / 1000) - pref;
1063 ssi = (prel < -15) ? 0 :
1064 (prel < 0) ? (prel + 15) * 2 / 3 : // 0% - 10%
1065 (prel < 20) ? prel * 4 + 10 : // 10% - 90%
1066 (prel < 35) ? (prel - 20) * 2 / 3 + 90 : // 90% - 100%
1067 100;
1068#ifdef DEBUG_SIGNALSTRENGTH
1069 fprintf(stderr, "SSI-C: STR:%d, Pref:%d, Prel:%d, ssi:%d%%(mod:%d)\n", Strength, pref, prel, ssi, mod);
1070#endif
1071 }
1072 }
1073 else if (Channel->IsSat())
1074 ssi = dB1000toPercent(Strength, -95000, -20000); // defaults
1075 return ssi;
1076}
1077
1078// Due to missing values or the different meanings of the reported error rate, ber_sqi is currently not taken into account
1079#define IGNORE_BER 1
1080#define BER_ERROR_FREE (1000*1000*1000) // 1/10^-9
1081
1082int SignalToSQI(const cChannel *Channel, int Signal, int Ber, int FeModulation, int FeCoderateH, int FeFec)
1083{
1084#if IGNORE_BER
1085 Ber = BER_ERROR_FREE; // assume/pretend to be biterror free
1086#endif
1087 // Signal in 0.001dB (dB x 1000)
1088 cDvbTransponderParameters dtp(Channel->Parameters());
1089 int sqi = 0; // 0-100
1090 int mod = (FeModulation >= 0) ? FeModulation : dtp.Modulation();
1091 int cod = (FeCoderateH >= 0) ? FeCoderateH : dtp.CoderateH(); // DVB-T
1092 int fec = (FeFec >= 0) ? FeFec : dtp.CoderateH();
1093 if (Channel->IsTerr()) { // QEF: BER 10^-6
1094 int cnref = 0;
1095 // NorDig Unified Ver. 2.6 - 3.4.4.7 Page 45 ff.
1096 // reference values for QEF (BER 10^-11 at MPEG2 demux input)
1097 if (dtp.System() == DVB_SYSTEM_1) { // DVB-T
1098 fec = cod; // adjustment for DVB-T
1099 if (mod == QAM_AUTO) mod = QPSK;
1100 switch (fec) { // 0.1 dB Q4 Q16 Q64 (Hierarchy=None)
1101 case FEC_1_2: cnref = REF_T1(51, 108, 165); break;
1102 default:
1103 case FEC_2_3: cnref = REF_T1(69, 131, 187); break;
1104 case FEC_3_4: cnref = REF_T1(79, 146, 202); break;
1105 case FEC_5_6: cnref = REF_T1(89, 156, 216); break;
1106 case FEC_7_8: cnref = REF_T1(97, 160, 225); break;
1107 }
1108 }
1109 else { // DVB-T2
1110 if (mod == QAM_AUTO) mod = QAM_64;
1111 switch (fec) { // 0.1 dB Q4 Q16 Q64 Q256
1112 case FEC_1_2: cnref = REF_T2(35, 87, 130, 170); break;
1113 default:
1114 case FEC_3_5: cnref = REF_T2(47, 101, 148, 194); break;
1115 case FEC_2_3: cnref = REF_T2(56, 114, 162, 208); break;
1116 case FEC_3_4: cnref = REF_T2(66, 125, 177, 229); break;
1117 case FEC_4_5: cnref = REF_T2(72, 133, 187, 243); break;
1118 case FEC_5_6: cnref = REF_T2(77, 138, 194, 251); break;
1119 }
1120 }
1121 if (cnref) {
1122 int cnrel = (Signal/100) - cnref; // 0.1 dB
1123 int ber_sqi = 100; // 100%
1124 int cnr_sqi = 0; // 0%
1125 if (dtp.System() == DVB_SYSTEM_1) { // DVB-T
1126 ber_sqi = (Ber < 1000) ? 0 : // > 10^-3
1127 (Ber >= 10000000) ? 100 : // <= 10^-7
1128 (int)(20 * log10(Ber)) - 40; // 20*log10(1/BER)-40 -> 20% .. 100%
1129 // scale: -7dB/+3dB to reference-value
1130 cnr_sqi = (cnrel < -70) ? 0 :
1131 (cnrel < +30) ? (100 + (cnrel - 30)) :
1132 100;
1133 sqi = (cnr_sqi * ber_sqi) / 100;
1134 // alternative: stretched scale: cnref-7dB = 0%, 30dB = 100%
1135 // sqi = dB1000toPercent(Signal, (100*cnref)-7000, 30000);
1136 }
1137 else { // DVB-T2
1138 ber_sqi = (Ber < 10000) ? 0 : // > 10^-4
1139 (Ber >= 10000000) ? 100 * 100 / 6 : // <= 10^-7 : 16.67% -> SQI 0% .. 100%
1140 (100 * 100 / 15); // 6.67% -> SQI 0% .. 40% || 100%
1141 // scale: -3dB/+3dB to reference-value
1142 sqi = (cnrel < -30) ? 0 :
1143 (cnrel <= +30) ? (cnrel + 30) * ber_sqi / 1000 : // (0 .. 6) * 16,67 || 6.67
1144 100;
1145 // alternative: stretched scale: cnref-3dB = 0%, 32dB = 100%
1146 // sqi = dB1000toPercent(Signal, (100*cnref)-3000, 32000);
1147 }
1148#ifdef DEBUG_SIGNALQUALITY
1149 fprintf(stderr, "SQI-T: SIG:%d, BER:%d, CNref:%d, CNrel:%d, bersqi:%d, sqi:%d%%(sys:%d, mod:%d, fec:%d)\n", Signal, Ber, cnref, cnrel, ber_sqi, sqi, dtp.System(), mod, fec);
1150#endif
1151 }
1152 }
1153 else if (Channel->IsCable()) { // ! COMPLETELY UNTESTED !
1154 if (mod == QAM_AUTO) mod = QAM_256;
1155 // 0.1 dB Q16 Q32 Q64 Q128 Q256
1156 int cnref = REF_C1(200, 230, 260, 290, 320); // minimum for BER<10^-4
1157 if (cnref) {
1158 int cnrel = (Signal / 100) - cnref; // 0.1 dB
1159 int ber_sqi = (Ber < 1000) ? 0 : // > 10^-3
1160 (Ber >= 10000000) ? 100 : // <= 10^-7
1161 (int)(20 * log10(Ber)) - 40; // 20*log10(1/BER)-40 -> 20% .. 100%
1162 // scale: -7dB/+3dB to reference-value
1163 int cnr_sqi = (cnrel < -70) ? 0 :
1164 (cnrel < +30) ? (100 + (cnrel - 30)) :
1165 100;
1166 sqi = (cnr_sqi * ber_sqi) / 100;
1167 // alternative: stretched scale: cnref-7dB = 0%, 40dB = 100%
1168 // sqi = dB1000toPercent(Signal, (100*cnref)-7000, 40000);
1169#ifdef DEBUG_SIGNALQUALITY
1170 dsyslog("SQI-C: SIG:%d, BER:%d, CNref:%d, CNrel:%d, bersqi:%d, sqi:%d%%(sys:%d, mod:%d, fec:%d)\n", Signal, Ber, cnref, cnrel, ber_sqi, sqi, dtp.System(), mod, fec);
1171#endif
1172 }
1173 }
1174 else if (Channel->IsSat()) {
1175 int cnref = 0;
1176 if (dtp.System() == DVB_SYSTEM_1) { // DVB-S
1177 if (mod == QAM_AUTO) mod = QPSK;
1178 switch (fec) { // 0.1 dB: Q4 : 10^-7
1179 case FEC_1_2: cnref = REF_S1(38); break;
1180 default:
1181 case FEC_2_3: cnref = REF_S1(56); break;
1182 case FEC_3_4: cnref = REF_S1(67); break;
1183 case FEC_5_6: cnref = REF_S1(77); break;
1184 case FEC_7_8: cnref = REF_S1(84); break;
1185 }
1186 if (cnref) {
1187 //cnrel = (Signal/100) - cnref; // 0.1 dB
1188 // scale: cnref-4dB = 0%, 15dB = 100%
1189 sqi = dB1000toPercent(Signal, (100*cnref)-4000, 15000);
1190#ifdef DEBUG_SIGNALQUALITY
1191 dsyslog("SQI-S1: SIG:%d, BER:%d, CNref:%d, sqi:%d%%(mod:%d, fec:%d)\n", Signal, Ber, cnref, sqi, mod, fec);
1192#endif
1193 }
1194 }
1195 else { // DVB-S2
1196 if (mod == QAM_AUTO) mod = QAM_64;
1197 switch (fec) { // 0.1 dB Q4 Q8 16A* 32A*
1198 //case FEC_1_4: cnref = REF_S2(-14, 65, 90, 126); break;
1199 //case FEC_1_3: cnref = REF_S2( -2, 65, 90, 126); break;
1200 case FEC_2_5: cnref = REF_S2( 7, 65, 90, 126); break;
1201 case FEC_1_2: cnref = REF_S2( 20, 65, 90, 126); break;
1202 case FEC_3_5: cnref = REF_S2( 32, 65, 90, 126); break;
1203 default:
1204 case FEC_2_3: cnref = REF_S2( 41, 76, 90, 126); break;
1205 case FEC_3_4: cnref = REF_S2( 50, 66, 102, 126); break;
1206 case FEC_4_5: cnref = REF_S2( 57, 89, 110, 136); break;
1207 case FEC_5_6: cnref = REF_S2( 62, 104, 116, 143); break;
1208 case FEC_8_9: cnref = REF_S2( 72, 117, 129, 157); break;
1209 case FEC_9_10: cnref = REF_S2( 74, 120, 131, 161); break;
1210 }
1211 if (cnref) {
1212 // cnrel = (Signal/100) - cnref; // 0.1 dB
1213 // scale: cnref-4dB = 0%, 20dB = 100%
1214 sqi = dB1000toPercent(Signal, (100*cnref)-4000, 20000);
1215#ifdef DEBUG_SIGNALQUALITY
1216 dsyslog("SQI-S2: SIG:%d, BER:%d, CNref:%d, sqi:%d%%(mod:%d, fec:%d)\n", Signal, Ber, cnref, sqi, mod, fec);
1217#endif
1218 }
1219 }
1220 }
1221 return sqi;
1222}
1223
1225{
1227 // Try DVB API 5:
1228 for (int i = 0; i < 1; i++) { // just a trick to break out with 'continue' ;-)
1229 dtv_property Props[MAXFRONTENDCMDS];
1230 dtv_properties CmdSeq;
1231 memset(&Props, 0, sizeof(Props));
1232 memset(&CmdSeq, 0, sizeof(CmdSeq));
1233 CmdSeq.props = Props;
1234 SETCMD(DTV_STAT_SIGNAL_STRENGTH, 0);
1235 SETCMD(DTV_MODULATION, 0);
1236 SETCMD(DTV_CODE_RATE_HP, 0); // DVB-T only
1237 SETCMD(DTV_INNER_FEC, 0);
1238 if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
1239 esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
1240 return -1;
1241 }
1242 int FeMod = (Props[1].u.st.len > 0) ? (int)Props[1].u.data : -1;
1243 int FeCod = (Props[2].u.st.len > 0) ? (int)Props[2].u.data : -1;
1244 int FeFec = (Props[3].u.st.len > 0) ? (int)Props[3].u.data : -1;
1245 int Signal = 0;
1246 if (Props[0].u.st.len > 0) {
1247 switch (Props[0].u.st.stat[0].scale) {
1248 case FE_SCALE_DECIBEL: Signal = StrengthToSSI(&channel, Props[0].u.st.stat[0].svalue, FeMod, FeCod, FeFec);
1249 break;
1250 case FE_SCALE_RELATIVE: Signal = 100 * Props[0].u.st.stat[0].uvalue / 0xFFFF;
1251 break;
1252 default: ;
1253 }
1254#ifdef DEBUG_SIGNALSTRENGTH
1255 fprintf(stderr, "FE %d/%d: API5 %d %08X %.1f S = %d\n", adapter, frontend, Props[0].u.st.stat[0].scale, int(Props[0].u.st.stat[0].svalue), int(Props[0].u.st.stat[0].svalue) / 1000.0, Signal);
1256#endif
1257 }
1258 else
1259 continue;
1260 return Signal;
1261 }
1262 // Fall back to DVB API 3:
1263 uint16_t Signal;
1264 while (1) {
1265 if (ioctl(fd_frontend, FE_READ_SIGNAL_STRENGTH, &Signal) != -1)
1266 break;
1267 if (errno != EINTR)
1268 return -1;
1269 }
1270 uint16_t MaxSignal = 0xFFFF; // Let's assume the default is using the entire range.
1271 // Use the subsystemId to identify individual devices in case they need
1272 // special treatment to map their Signal value into the range 0...0xFFFF.
1273 switch (dvbFrontend->SubsystemId()) {
1274 case 0x13C21019: // TT-budget S2-3200 (DVB-S/DVB-S2)
1275 case 0x1AE40001: // TechniSat SkyStar HD2 (DVB-S/DVB-S2)
1276 MaxSignal = 670; break;
1277 }
1278 int s = int(Signal) * 100 / MaxSignal;
1279 if (s > 100)
1280 s = 100;
1281#ifdef DEBUG_SIGNALSTRENGTH
1282 fprintf(stderr, "FE %d/%d: API3 %08X S = %04X %04X %3d%%\n", adapter, frontend, dvbFrontend->SubsystemId(), MaxSignal, Signal, s);
1283#endif
1284 return s;
1285}
1286
1287#define LOCK_THRESHOLD 5 // indicates that all 5 FE_HAS_* flags are set
1288
1290{
1291 // Try DVB API 5:
1292 for (int i = 0; i < 1; i++) { // just a trick to break out with 'continue' ;-)
1293 dtv_property Props[MAXFRONTENDCMDS];
1294 dtv_properties CmdSeq;
1295 memset(&Props, 0, sizeof(Props));
1296 memset(&CmdSeq, 0, sizeof(CmdSeq));
1297 CmdSeq.props = Props;
1298 SETCMD(DTV_STAT_CNR, 0);
1299 SETCMD(DTV_MODULATION, 0);
1300 SETCMD(DTV_CODE_RATE_HP, 0); // DVB-T only
1301 SETCMD(DTV_INNER_FEC, 0);
1302 SETCMD(DTV_STAT_POST_ERROR_BIT_COUNT, 0);
1303 SETCMD(DTV_STAT_POST_TOTAL_BIT_COUNT, 0);
1304 if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
1305 esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
1306 return -1;
1307 }
1308 int FeMod = (Props[1].u.st.len > 0) ? (int)Props[1].u.data : -1;
1309 int FeCod = (Props[2].u.st.len > 0) ? (int)Props[2].u.data : -1;
1310 int FeFec = (Props[3].u.st.len > 0) ? (int)Props[3].u.data : -1;
1311 int Ber = BER_ERROR_FREE; // 1/10^-9
1312 if (Props[4].u.st.len > 0 && Props[4].u.st.stat[0].scale == FE_SCALE_COUNTER && Props[5].u.st.len > 0 && Props[5].u.st.stat[0].scale == FE_SCALE_COUNTER) {
1313 uint64_t ebc = Props[4].u.st.stat[0].uvalue; // error bit count
1314 uint64_t tbc = Props[5].u.st.stat[0].uvalue; // total bit count
1315 if (ebc > 0) {
1316 uint64_t BerRev = tbc / ebc; // reversed, for integer arithmetic
1317 if (BerRev < BER_ERROR_FREE)
1318 Ber = (int)BerRev;
1319 }
1320 }
1321 int Cnr = 0;
1322 if (Props[0].u.st.len > 0) {
1323 switch (Props[0].u.st.stat[0].scale) {
1324 case FE_SCALE_DECIBEL: Cnr = SignalToSQI(&channel, Props[0].u.st.stat[0].svalue, Ber, FeMod, FeCod, FeFec);
1325 break;
1326 case FE_SCALE_RELATIVE: Cnr = 100 * Props[0].u.st.stat[0].uvalue / 0xFFFF;
1327 break;
1328 default: ;
1329 }
1330#ifdef DEBUG_SIGNALQUALITY
1331 fprintf(stderr, "FE %d/%d: API5 %d %08X %.1f Q = %d\n", adapter, frontend, Props[0].u.st.stat[0].scale, int(Props[0].u.st.stat[0].svalue), int(Props[0].u.st.stat[0].svalue) / 1000.0, Cnr);
1332#endif
1333 }
1334 else
1335 continue;
1336 return Cnr;
1337 }
1338 // Fall back to DVB API 3:
1339 fe_status_t Status;
1340 if (GetFrontendStatus(Status)) {
1341 // Actually one would expect these checks to be done from FE_HAS_SIGNAL to FE_HAS_LOCK, but some drivers (like the stb0899) are broken, so FE_HAS_LOCK is the only one that (hopefully) is generally reliable...
1342 if ((Status & FE_HAS_LOCK) == 0) {
1343 if ((Status & FE_HAS_SIGNAL) == 0)
1344 return 0;
1345 if ((Status & FE_HAS_CARRIER) == 0)
1346 return 1;
1347 if ((Status & FE_HAS_VITERBI) == 0)
1348 return 2;
1349 if ((Status & FE_HAS_SYNC) == 0)
1350 return 3;
1351 return 4;
1352 }
1353#ifdef DEBUG_SIGNALQUALITY
1354 bool HasSnr = true;
1355#endif
1356 uint16_t Snr;
1357 while (1) {
1358 if (ioctl(fd_frontend, FE_READ_SNR, &Snr) != -1)
1359 break;
1360 if (errno != EINTR) {
1361 Snr = 0xFFFF;
1362#ifdef DEBUG_SIGNALQUALITY
1363 HasSnr = false;
1364#endif
1365 break;
1366 }
1367 }
1368#ifdef DEBUG_SIGNALQUALITY
1369 bool HasBer = true;
1370#endif
1371 uint32_t Ber;
1372 while (1) {
1373 if (ioctl(fd_frontend, FE_READ_BER, &Ber) != -1)
1374 break;
1375 if (errno != EINTR) {
1376 Ber = 0;
1377#ifdef DEBUG_SIGNALQUALITY
1378 HasBer = false;
1379#endif
1380 break;
1381 }
1382 }
1383#ifdef DEBUG_SIGNALQUALITY
1384 bool HasUnc = true;
1385#endif
1386 uint32_t Unc;
1387 while (1) {
1388 if (ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &Unc) != -1) {
1389 if (Unc != lastUncValue) {
1390#ifdef DEBUG_SIGNALQUALITY
1391 fprintf(stderr, "FE %d/%d: API3 UNC = %u %u %u\n", adapter, frontend, Unc, lastUncValue, lastUncDelta);
1392#endif
1393 lastUncDelta = (Unc >= lastUncValue) ? Unc - lastUncValue : lastUncValue - Unc;
1394 lastUncValue = Unc;
1395 lastUncChange = time(NULL);
1396 }
1397 // The number of uncorrected blocks is a counter, which is normally
1398 // at a constant value and only increases if there are new uncorrected
1399 // blocks. So a change in the Unc value indicates reduced signal quality.
1400 // Whenever the Unc counter changes, we take the delta between the old
1401 // and new value into account for calculating the overall signal quality.
1402 // The impact of Unc is considered for 2 seconds, and after that it is
1403 // bisected with every passing second in order to phase it out. Otherwise
1404 // once one or more uncorrected blocks occur, the signal quality would
1405 // be considered low even if there haven't been any more uncorrected bocks
1406 // for quite a while.
1407 Unc = lastUncDelta;
1408 if (Unc > 0) {
1409 int t = time(NULL) - lastUncChange - 2;
1410 if (t > 0)
1411 Unc >>= min(t, int(sizeof(Unc) * 8 - 1));
1412 if (Unc == 0)
1413 lastUncDelta = 0;
1414#ifdef DEBUG_SIGNALQUALITY
1415 fprintf(stderr, "FE %d/%d: API3 UNC = %u\n", adapter, frontend, Unc);
1416#endif
1417 }
1418 break;
1419 }
1420 if (errno != EINTR) {
1421 Unc = 0;
1422#ifdef DEBUG_SIGNALQUALITY
1423 HasUnc = false;
1424#endif
1425 break;
1426 }
1427 }
1428 uint16_t MinSnr = 0x0000;
1429 uint16_t MaxSnr = 0xFFFF; // Let's assume the default is using the entire range.
1430 // Use the subsystemId to identify individual devices in case they need
1431 // special treatment to map their Snr value into the range 0...0xFFFF.
1432 switch (dvbFrontend->SubsystemId()) {
1433 case 0x13C21019: // TT-budget S2-3200 (DVB-S/DVB-S2)
1434 case 0x1AE40001: // TechniSat SkyStar HD2 (DVB-S/DVB-S2)
1435 if (frontendType == SYS_DVBS2) {
1436 MinSnr = 10;
1437 MaxSnr = 70;
1438 }
1439 else
1440 MaxSnr = 200;
1441 break;
1442 case 0x20130245: // PCTV Systems PCTV 73ESE
1443 case 0x2013024F: // PCTV Systems nanoStick T2 290e
1444 MaxSnr = 255; break;
1445 }
1446 int a = int(constrain(Snr, MinSnr, MaxSnr)) * 100 / (MaxSnr - MinSnr);
1447 int b = 100 - (Unc * 10 + (Ber / 256) * 5);
1448 if (b < 0)
1449 b = 0;
1450 int q = LOCK_THRESHOLD + a * b * (100 - LOCK_THRESHOLD) / 100 / 100;
1451 if (q > 100)
1452 q = 100;
1453#ifdef DEBUG_SIGNALQUALITY
1454 fprintf(stderr, "FE %d/%d: API3 %08X Q = %04X %04X %d %5d %5d %3d%%\n", adapter, frontend, dvbFrontend->SubsystemId(), MaxSnr, Snr, HasSnr, HasBer ? int(Ber) : -1, HasUnc ? int(Unc) : -1, q);
1455#endif
1456 return q;
1457 }
1458 return -1;
1459}
1460
1461static unsigned int FrequencyToHz(unsigned int f)
1462{
1463 while (f && f < 1000000)
1464 f *= 1000;
1465 return f;
1466}
1467
1469{
1470 if (!positioner) {
1473 }
1474 return positioner;
1475}
1476
1477void cDvbTuner::ExecuteDiseqc(const cDiseqc *Diseqc, int *Frequency)
1478{
1479 if (!lnbPowerTurnedOn) {
1480 CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power
1481 lnbPowerTurnedOn = true;
1482 }
1483 static cMutex Mutex;
1484 if (Diseqc->IsScr())
1485 Mutex.Lock();
1486 struct dvb_diseqc_master_cmd cmd;
1487 const char *CurrentAction = NULL;
1488 cPositioner *Positioner = NULL;
1489 bool Break = false;
1490 for (int i = 0; !Break; i++) {
1491 cmd.msg_len = sizeof(cmd.msg);
1492 cDiseqc::eDiseqcActions da = Diseqc->Execute(&CurrentAction, cmd.msg, &cmd.msg_len, scr, Frequency);
1493 if (da == cDiseqc::daNone) {
1494 diseqcOffset = 0;
1495 break;
1496 }
1497 bool d = i >= diseqcOffset;
1498 switch (da) {
1499 case cDiseqc::daToneOff: if (d) CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break;
1500 case cDiseqc::daToneOn: if (d) CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break;
1501 case cDiseqc::daVoltage13: if (d) CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break;
1502 case cDiseqc::daVoltage18: if (d) CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break;
1503 case cDiseqc::daMiniA: if (d) CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break;
1504 case cDiseqc::daMiniB: if (d) CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break;
1505 case cDiseqc::daCodes: if (d) CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd)); break;
1506 case cDiseqc::daPositionN: if ((Positioner = GetPositioner()) != NULL) {
1507 if (d) {
1509 Break = Positioner->IsMoving();
1510 }
1511 }
1512 break;
1513 case cDiseqc::daPositionA: if ((Positioner = GetPositioner()) != NULL) {
1514 if (d) {
1516 Break = Positioner->IsMoving();
1517 }
1518 }
1519 break;
1520 case cDiseqc::daScr:
1521 case cDiseqc::daWait: break;
1522 default: esyslog("ERROR: unknown diseqc command %d", da);
1523 }
1524 if (Break)
1525 diseqcOffset = i + 1;
1526 }
1528 if (scr && !Break)
1529 ResetToneAndVoltage(); // makes sure we don't block the bus!
1530 if (Diseqc->IsScr())
1531 Mutex.Unlock();
1532}
1533
1535{
1536 CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, bondedTuner ? SEC_VOLTAGE_OFF : SEC_VOLTAGE_13));
1537 CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF));
1538}
1539
1541{
1542 dtv_property Props[MAXFRONTENDCMDS];
1543 memset(&Props, 0, sizeof(Props));
1544 dtv_properties CmdSeq;
1545 memset(&CmdSeq, 0, sizeof(CmdSeq));
1546 CmdSeq.props = Props;
1547 SETCMD(DTV_CLEAR, 0);
1548 if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
1549 esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
1550 return false;
1551 }
1552 CmdSeq.num = 0;
1553
1555
1556 // Determine the required frontend type:
1558 if (frontendType == SYS_UNDEFINED)
1559 return false;
1560
1561 SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
1562 if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2) {
1563 int frequency = channel.Frequency();
1564 if (Setup.DiSEqC) {
1565 if (const cDiseqc *diseqc = Diseqcs.Get(device->DeviceNumber() + 1, channel.Source(), frequency, dtp.Polarization(), &scr)) {
1566 frequency -= diseqc->Lof();
1567 if (diseqc != lastDiseqc || diseqc->IsScr() || diseqc->Position() >= 0 && channel.Source() != lastSource) {
1568 if (IsBondedMaster()) {
1569 ExecuteDiseqc(diseqc, &frequency);
1570 if (frequency == 0)
1571 return false;
1572 }
1573 else
1575 lastDiseqc = diseqc;
1577 }
1578 }
1579 else {
1580 esyslog("ERROR: no DiSEqC parameters found for channel %d (%s)", channel.Number(), channel.Name());
1581 return false;
1582 }
1583 }
1584 else {
1585 int tone = SEC_TONE_OFF;
1586 if (frequency < Setup.LnbSLOF) {
1587 frequency -= Setup.LnbFrequLo;
1588 tone = SEC_TONE_OFF;
1589 }
1590 else {
1591 frequency -= Setup.LnbFrequHi;
1592 tone = SEC_TONE_ON;
1593 }
1594 int volt = (dtp.Polarization() == 'V' || dtp.Polarization() == 'R') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
1595 if (!IsBondedMaster()) {
1596 tone = SEC_TONE_OFF;
1597 volt = SEC_VOLTAGE_13;
1598 }
1599 CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
1600 CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
1601 }
1602 frequency = abs(frequency); // Allow for C-band, where the frequency is less than the LOF
1603
1604 // DVB-S/DVB-S2 (common parts)
1605 SETCMD(DTV_FREQUENCY, frequency * 1000UL);
1606 SETCMD(DTV_MODULATION, dtp.Modulation());
1607 SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL);
1608 SETCMD(DTV_INNER_FEC, dtp.CoderateH());
1609 SETCMD(DTV_INVERSION, dtp.Inversion());
1610 if (frontendType == SYS_DVBS2) {
1611 // DVB-S2
1612 SETCMD(DTV_PILOT, dtp.Pilot());
1613 SETCMD(DTV_ROLLOFF, dtp.RollOff());
1614 if (DvbApiVersion >= 0x0508)
1616 }
1617 else {
1618 // DVB-S
1619 SETCMD(DTV_ROLLOFF, ROLLOFF_35); // DVB-S always has a ROLLOFF of 0.35
1620 }
1621
1624 }
1625 else if (frontendType == SYS_DVBC_ANNEX_AC || frontendType == SYS_DVBC_ANNEX_B) {
1626 // DVB-C
1627 SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
1628 SETCMD(DTV_INVERSION, dtp.Inversion());
1629 SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL);
1630 SETCMD(DTV_INNER_FEC, dtp.CoderateH());
1631 SETCMD(DTV_MODULATION, dtp.Modulation());
1632
1635 }
1636 else if (frontendType == SYS_DVBT || frontendType == SYS_DVBT2) {
1637 // DVB-T/DVB-T2 (common parts)
1638 SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
1639 SETCMD(DTV_INVERSION, dtp.Inversion());
1640 SETCMD(DTV_BANDWIDTH_HZ, dtp.Bandwidth());
1641 SETCMD(DTV_CODE_RATE_HP, dtp.CoderateH());
1642 SETCMD(DTV_CODE_RATE_LP, dtp.CoderateL());
1643 SETCMD(DTV_MODULATION, dtp.Modulation());
1644 SETCMD(DTV_TRANSMISSION_MODE, dtp.Transmission());
1645 SETCMD(DTV_GUARD_INTERVAL, dtp.Guard());
1646 SETCMD(DTV_HIERARCHY, dtp.Hierarchy());
1647 if (frontendType == SYS_DVBT2) {
1648 // DVB-T2
1649 SETCMD(DTV_INNER_FEC, dtp.CoderateH());
1650 if (DvbApiVersion >= 0x0508) {
1652 }
1653 else if (DvbApiVersion >= 0x0503)
1655 }
1658 }
1659 else if (frontendType == SYS_ATSC) {
1660 // ATSC
1661 SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
1662 SETCMD(DTV_INVERSION, dtp.Inversion());
1663 SETCMD(DTV_MODULATION, dtp.Modulation());
1664
1667 }
1668 else {
1669 esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
1670 return false;
1671 }
1672 SETCMD(DTV_TUNE, 0);
1673 if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
1674 esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
1675 return false;
1676 }
1677 return true;
1678}
1679
1681{
1682 cTimeMs Timer;
1683 bool LostLock = false;
1684 fe_status_t Status = (fe_status_t)0;
1685 while (Running()) {
1686 int WaitTime = 1000;
1687 fe_status_t NewStatus;
1688 if (GetFrontendStatus(NewStatus))
1689 Status = NewStatus;
1690 cMutexLock MutexLock(&mutex);
1691 switch (tunerStatus) {
1692 case tsIdle:
1693 break; // we want the TimedWait() below!
1694 case tsSet:
1696 continue;
1697 case tsPositioning:
1698 if (positioner) {
1699 if (positioner->IsMoving())
1700 break; // we want the TimedWait() below!
1701 else if (diseqcOffset) {
1702 lastDiseqc = NULL;
1703 tunerStatus = tsSet; // have it process the rest of the DiSEqC sequence
1704 continue;
1705 }
1706 }
1708 device->SectionHandler()->SetStatus(true); // may have been turned off when retuning
1709 Timer.Set(tuneTimeout + (scr ? rand() % SCR_RANDOM_TIMEOUT : 0));
1710 if (positioner)
1711 continue;
1712 // otherwise run directly into tsTuned...
1713 case tsTuned:
1714 if (Timer.TimedOut()) {
1716 lastDiseqc = NULL;
1717 lastSource = 0;
1718 if (time(NULL) - lastTimeoutReport > 60) { // let's not get too many of these
1719 if (channel.Number()) // no need to log this for transponders that are announced in the NIT but are not currently broadcasting
1720 isyslog("frontend %d/%d timed out while tuning to channel %d (%s), tp %d", adapter, frontend, channel.Number(), channel.Name(), channel.Transponder());
1721 lastTimeoutReport = time(NULL);
1722 }
1723 continue;
1724 }
1725 WaitTime = 100; // allows for a quick change from tsTuned to tsLocked
1726 // run into tsLocked...
1727 case tsLocked:
1728 if (Status & FE_REINIT) {
1730 lastDiseqc = NULL;
1731 lastSource = 0;
1732 isyslog("frontend %d/%d was reinitialized", adapter, frontend);
1734 continue;
1735 }
1736 else if (Status & FE_HAS_LOCK) {
1737 if (LostLock) {
1738 isyslog("frontend %d/%d regained lock on channel %d (%s), tp %d", adapter, frontend, channel.Number(), channel.Name(), channel.Transponder());
1739 LostLock = false;
1740 }
1741 if (device->SdtFilter()->TransponderWrong()) {
1742 isyslog("frontend %d/%d is not receiving transponder %d for channel %d (%s) - retuning", adapter, frontend, channel.Transponder(), channel.Number(), channel.Name());
1743 device->SectionHandler()->SetStatus(false);
1745 lastDiseqc = NULL;
1746 lastSource = 0;
1747 continue;
1748 }
1750 locked.Broadcast();
1752 }
1753 else if (tunerStatus == tsLocked) {
1754 LostLock = true;
1755 isyslog("frontend %d/%d lost lock on channel %d (%s), tp %d", adapter, frontend, channel.Number(), channel.Name(), channel.Transponder());
1757 Timer.Set(lockTimeout);
1759 continue;
1760 }
1761 break;
1762 default: esyslog("ERROR: unknown tuner status %d", tunerStatus);
1763 }
1764 newSet.TimedWait(mutex, WaitTime);
1765 }
1766}
1767
1768// --- cDvbSourceParam -------------------------------------------------------
1769
1771private:
1775public:
1776 cDvbSourceParam(char Source, const char *Description);
1777 virtual void SetData(cChannel *Channel);
1778 virtual void GetData(cChannel *Channel);
1779 virtual cOsdItem *GetOsdItem(void);
1780 };
1781
1782cDvbSourceParam::cDvbSourceParam(char Source, const char *Description)
1783:cSourceParam(Source, Description)
1784{
1785 param = 0;
1786 srate = 0;
1787}
1788
1790{
1791 srate = Channel->Srate();
1792 dtp.Parse(Channel->Parameters());
1793 param = 0;
1794}
1795
1797{
1798 Channel->SetTransponderData(Channel->Source(), Channel->Frequency(), srate, dtp.ToString(Source()), true);
1799}
1800
1802{
1803 char type = Source();
1804 const tDvbParameterMap *SystemValues = type == 'S' ? SystemValuesSat : SystemValuesTerr;
1805#undef ST
1806#define ST(s) if (strchr(s, type))
1807 switch (param++) {
1808 case 0: ST(" S ") return new cMenuEditChrItem( tr("Polarization"), &dtp.polarization, "HVLR"); else return GetOsdItem();
1809 case 1: ST(" ST") return new cMenuEditMapItem( tr("System"), &dtp.system, SystemValues); else return GetOsdItem();
1810 case 2: ST(" CS ") return new cMenuEditIntItem( tr("Srate"), &srate); else return GetOsdItem();
1811 case 3: ST("ACST") return new cMenuEditMapItem( tr("Inversion"), &dtp.inversion, InversionValues); else return GetOsdItem();
1812 case 4: ST(" CST") return new cMenuEditMapItem( tr("CoderateH"), &dtp.coderateH, CoderateValues); else return GetOsdItem();
1813 case 5: ST(" T") return new cMenuEditMapItem( tr("CoderateL"), &dtp.coderateL, CoderateValues); else return GetOsdItem();
1814 case 6: ST("ACST") return new cMenuEditMapItem( tr("Modulation"), &dtp.modulation, ModulationValues); else return GetOsdItem();
1815 case 7: ST(" T") return new cMenuEditMapItem( tr("Bandwidth"), &dtp.bandwidth, BandwidthValues); else return GetOsdItem();
1816 case 8: ST(" T") return new cMenuEditMapItem( tr("Transmission"), &dtp.transmission, TransmissionValues); else return GetOsdItem();
1817 case 9: ST(" T") return new cMenuEditMapItem( tr("Guard"), &dtp.guard, GuardValues); else return GetOsdItem();
1818 case 10: ST(" T") return new cMenuEditMapItem( tr("Hierarchy"), &dtp.hierarchy, HierarchyValues); else return GetOsdItem();
1819 case 11: ST(" S ") return new cMenuEditMapItem( tr("Rolloff"), &dtp.rollOff, RollOffValues); else return GetOsdItem();
1820 case 12: ST(" ST") return new cMenuEditIntItem( tr("StreamId"), &dtp.streamId, 0, 255); else return GetOsdItem();
1821 case 13: ST(" S ") return new cMenuEditMapItem( tr("Pilot"), &dtp.pilot, PilotValues); else return GetOsdItem();
1822 case 14: ST(" T") return new cMenuEditIntItem( tr("T2SystemId"), &dtp.t2systemId, 0, 65535); else return GetOsdItem();
1823 case 15: ST(" T") return new cMenuEditIntItem( tr("SISO/MISO"), &dtp.sisoMiso, 0, 1); else return GetOsdItem();
1824 default: return NULL;
1825 }
1826 return NULL;
1827}
1828
1829// --- cDvbDevice ------------------------------------------------------------
1830
1831bool cDvbDevice::useDvbDevices = true;
1834
1835cDvbDevice::cDvbDevice(int Adapter, int Frontend)
1836{
1837 adapter = Adapter;
1839 ciAdapter = NULL;
1840 dvbTuner = NULL;
1841 bondedDevice = NULL;
1843 tsBuffer = NULL;
1844
1845 // Common Interface:
1846
1848 if (fd_ca >= 0)
1850 checkTsBuffer = false;
1851
1852 // The DVR device (will be opened and closed as needed):
1853
1854 fd_dvr = -1;
1855
1856 // We only check the devices that must be present - the others will be checked before accessing them://XXX
1857
1858 dvbTuner = new cDvbTuner(this, adapter, frontend);
1859
1861}
1862
1864{
1866 delete dvbTuner;
1867 delete ciAdapter;
1868 UnBond();
1869 // We're not explicitly closing any device files here, since this sometimes
1870 // caused segfaults. Besides, the program is about to terminate anyway...
1871}
1872
1873cString DvbName(const char *Name, int Adapter, int Frontend)
1874{
1875 return cString::sprintf("%s/%s%d/%s%d", DEV_DVB_BASE, DEV_DVB_ADAPTER, Adapter, Name, Frontend);
1876}
1877
1878int DvbOpen(const char *Name, int Adapter, int Frontend, int Mode, bool ReportError)
1879{
1880 cString FileName = DvbName(Name, Adapter, Frontend);
1881 int fd = open(FileName, Mode);
1882 if (fd < 0 && ReportError)
1883 LOG_ERROR_STR(*FileName);
1884 return fd;
1885}
1886
1888{
1889 return dvbTuner ? dvbTuner->Frontend() : frontend;
1890}
1891
1892bool cDvbDevice::Exists(int Adapter, int Frontend)
1893{
1895 if (access(FileName, F_OK) == 0) {
1896 int f = open(FileName, O_RDONLY);
1897 if (f >= 0) {
1898 close(f);
1899 return true;
1900 }
1901 else if (errno != ENODEV && errno != EINVAL)
1902 LOG_ERROR_STR(*FileName);
1903 }
1904 else if (errno != ENOENT)
1905 LOG_ERROR_STR(*FileName);
1906 return false;
1907}
1908
1909bool cDvbDevice::Probe(int Adapter, int Frontend)
1910{
1912 dsyslog("probing %s", *FileName);
1913 for (cDvbDeviceProbe *dp = DvbDeviceProbes.First(); dp; dp = DvbDeviceProbes.Next(dp)) {
1914 if (dp->Probe(Adapter, Frontend))
1915 return true; // a plugin has created the actual device
1916 }
1917 dsyslog("creating cDvbDevice");
1918 new cDvbDevice(Adapter, Frontend); // it's a "budget" device
1919 return true;
1920}
1921
1923{
1924 if (dvbTuner)
1926 return "";
1927}
1928
1930{
1931 if (dvbTuner)
1932 return dvbTuner->FrontendName();
1933 return "";
1934}
1935
1937{
1938 new cDvbSourceParam('A', "ATSC");
1939 new cDvbSourceParam('C', "DVB-C");
1940 new cDvbSourceParam('S', "DVB-S");
1941 new cDvbSourceParam('T', "DVB-T");
1942 cStringList Nodes;
1943 cReadDir DvbDir(DEV_DVB_BASE);
1944 if (DvbDir.Ok()) {
1945 struct dirent *a;
1946 while ((a = DvbDir.Next()) != NULL) {
1947 if (strstr(a->d_name, DEV_DVB_ADAPTER) == a->d_name) {
1948 int Adapter = strtol(a->d_name + strlen(DEV_DVB_ADAPTER), NULL, 10);
1949 cReadDir AdapterDir(AddDirectory(DEV_DVB_BASE, a->d_name));
1950 if (AdapterDir.Ok()) {
1951 struct dirent *f;
1952 while ((f = AdapterDir.Next()) != NULL) {
1953 if (strstr(f->d_name, DEV_DVB_FRONTEND) == f->d_name) {
1954 int Frontend = strtol(f->d_name + strlen(DEV_DVB_FRONTEND), NULL, 10);
1955 if (access(DvbName(DEV_DVB_DEMUX, Adapter, Frontend), F_OK) == 0) { // we only create devices for actual demuxes
1956 dsyslog("detected /dev/dvb/adapter%d/frontend%d", Adapter, Frontend);
1957 Nodes.Append(strdup(cString::sprintf("%2d %2d", Adapter, Frontend)));
1958 }
1959 }
1960 }
1961 }
1962 }
1963 }
1964 }
1965 int Found = 0;
1966 int Used = 0;
1967 if (Nodes.Size() > 0) {
1968 Nodes.Sort();
1969 for (int i = 0; i < Nodes.Size(); i++) {
1970 int Adapter;
1971 int Frontend;
1972 if (2 == sscanf(Nodes[i], "%d %d", &Adapter, &Frontend)) {
1973 if (Exists(Adapter, Frontend)) {
1974 if (Found < MAXDEVICES) {
1975 Found++;
1977 if (Probe(Adapter, Frontend))
1978 Used++;
1979 }
1980 else {
1981 dsyslog("skipped /dev/dvb/adapter%d/frontend%d", Adapter, Frontend);
1982 NextCardIndex(1); // skips this one
1983 }
1984 }
1985 }
1986 }
1987 }
1988 }
1989 if (Found > 0) {
1990 isyslog("found %d DVB device%s", Found, Found > 1 ? "s" : "");
1991 if (Used != Found)
1992 isyslog("using only %d DVB device%s", Used, Used != 1 ? "s" : "");
1993 }
1994 else
1995 isyslog("no DVB device found");
1996 return Found > 0;
1997}
1998
1999bool cDvbDevice::BondDevices(const char *Bondings)
2000{
2001 UnBondDevices();
2002 if (Bondings) {
2003 cSatCableNumbers SatCableNumbers(MAXDEVICES, Bondings);
2004 for (int i = 0; i < cDevice::NumDevices(); i++) {
2005 int d = SatCableNumbers.FirstDeviceIndex(i);
2006 if (d >= 0) {
2007 int ErrorDevice = 0;
2008 if (cDevice *Device1 = cDevice::GetDevice(i)) {
2009 if (cDevice *Device2 = cDevice::GetDevice(d)) {
2010 if (cDvbDevice *DvbDevice1 = dynamic_cast<cDvbDevice *>(Device1)) {
2011 if (cDvbDevice *DvbDevice2 = dynamic_cast<cDvbDevice *>(Device2)) {
2012 if (!DvbDevice1->Bond(DvbDevice2))
2013 return false; // Bond() has already logged the error
2014 }
2015 else
2016 ErrorDevice = d + 1;
2017 }
2018 else
2019 ErrorDevice = i + 1;
2020 if (ErrorDevice) {
2021 esyslog("ERROR: device '%d' in device bondings '%s' is not a cDvbDevice", ErrorDevice, Bondings);
2022 return false;
2023 }
2024 }
2025 else
2026 ErrorDevice = d + 1;
2027 }
2028 else
2029 ErrorDevice = i + 1;
2030 if (ErrorDevice) {
2031 esyslog("ERROR: unknown device '%d' in device bondings '%s'", ErrorDevice, Bondings);
2032 return false;
2033 }
2034 }
2035 }
2036 }
2037 return true;
2038}
2039
2041{
2042 for (int i = 0; i < cDevice::NumDevices(); i++) {
2043 if (cDvbDevice *d = dynamic_cast<cDvbDevice *>(cDevice::GetDevice(i)))
2044 d->UnBond();
2045 }
2046}
2047
2049{
2050 cMutexLock MutexLock(&bondMutex);
2051 if (!bondedDevice) {
2052 if (Device != this) {
2053 if ((ProvidesDeliverySystem(SYS_DVBS) || ProvidesDeliverySystem(SYS_DVBS2)) && (Device->ProvidesDeliverySystem(SYS_DVBS) || Device->ProvidesDeliverySystem(SYS_DVBS2))) {
2054 if (dvbTuner && Device->dvbTuner && dvbTuner->Bond(Device->dvbTuner)) {
2055 bondedDevice = Device->bondedDevice ? Device->bondedDevice : Device;
2056 Device->bondedDevice = this;
2057 dsyslog("device %d bonded with device %d", DeviceNumber() + 1, bondedDevice->DeviceNumber() + 1);
2058 return true;
2059 }
2060 }
2061 else
2062 esyslog("ERROR: can't bond device %d with device %d (only DVB-S(2) devices can be bonded)", DeviceNumber() + 1, Device->DeviceNumber() + 1);
2063 }
2064 else
2065 esyslog("ERROR: can't bond device %d with itself", DeviceNumber() + 1);
2066 }
2067 else
2068 esyslog("ERROR: device %d already bonded with device %d, can't bond with device %d", DeviceNumber() + 1, bondedDevice->DeviceNumber() + 1, Device->DeviceNumber() + 1);
2069 return false;
2070}
2071
2073{
2074 cMutexLock MutexLock(&bondMutex);
2075 if (cDvbDevice *d = bondedDevice) {
2076 if (dvbTuner)
2077 dvbTuner->UnBond();
2078 dsyslog("device %d unbonded from device %d", DeviceNumber() + 1, bondedDevice->DeviceNumber() + 1);
2079 while (d->bondedDevice != this)
2080 d = d->bondedDevice;
2081 if (d == bondedDevice)
2082 d->bondedDevice = NULL;
2083 else
2084 d->bondedDevice = bondedDevice;
2085 bondedDevice = NULL;
2086 }
2087}
2088
2089bool cDvbDevice::BondingOk(const cChannel *Channel, bool ConsiderOccupied) const
2090{
2091 cMutexLock MutexLock(&bondMutex);
2092 if (bondedDevice || Positioner())
2093 return dvbTuner && dvbTuner->BondingOk(Channel, ConsiderOccupied);
2094 return true;
2095}
2096
2098{
2099 return ciAdapter;
2100}
2101
2102bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
2103{
2104 if (Handle->pid) {
2105 dmx_pes_filter_params pesFilterParams;
2106 memset(&pesFilterParams, 0, sizeof(pesFilterParams));
2107 if (On) {
2108 if (Handle->handle < 0) {
2109 Handle->handle = DvbOpen(DEV_DVB_DEMUX, adapter, frontend, O_RDWR | O_NONBLOCK, true);
2110 if (Handle->handle < 0) {
2111 LOG_ERROR;
2112 return false;
2113 }
2114 }
2115 pesFilterParams.pid = Handle->pid;
2116 pesFilterParams.input = DMX_IN_FRONTEND;
2117 pesFilterParams.output = DMX_OUT_TS_TAP;
2118 pesFilterParams.pes_type= DMX_PES_OTHER;
2119 pesFilterParams.flags = DMX_IMMEDIATE_START;
2120 if (ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
2121 LOG_ERROR;
2122 return false;
2123 }
2124 }
2125 else if (!Handle->used) {
2126 CHECK(ioctl(Handle->handle, DMX_STOP));
2127 if (Type <= ptTeletext) {
2128 pesFilterParams.pid = 0x1FFF;
2129 pesFilterParams.input = DMX_IN_FRONTEND;
2130 pesFilterParams.output = DMX_OUT_DECODER;
2131 pesFilterParams.pes_type= DMX_PES_OTHER;
2132 pesFilterParams.flags = DMX_IMMEDIATE_START;
2133 CHECK(ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams));
2134 }
2135 close(Handle->handle);
2136 Handle->handle = -1;
2137 }
2138 }
2139 return true;
2140}
2141
2142#define RB_NUM_SECTIONS 8 // default: 2 sections = 8192 bytes
2143
2144int cDvbDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask)
2145{
2147 int f = open(FileName, O_RDWR | O_NONBLOCK);
2148 if (f >= 0) {
2149 if (Pid == EITPID) { // increase ringbuffer size for EIT
2150 if (ioctl(f, DMX_SET_BUFFER_SIZE, MAX_SECTION_SIZE * RB_NUM_SECTIONS) < 0)
2151 dsyslog("OpenFilter (pid=%d, tid=%02X): ioctl DMX_SET_BUFFER_SIZE failed", Pid, Tid);
2152 }
2153 dmx_sct_filter_params sctFilterParams;
2154 memset(&sctFilterParams, 0, sizeof(sctFilterParams));
2155 sctFilterParams.pid = Pid;
2156 sctFilterParams.timeout = 0;
2157 sctFilterParams.flags = DMX_IMMEDIATE_START;
2158 sctFilterParams.filter.filter[0] = Tid;
2159 sctFilterParams.filter.mask[0] = Mask;
2160 if (ioctl(f, DMX_SET_FILTER, &sctFilterParams) >= 0)
2161 return f;
2162 else {
2163 esyslog("ERROR: can't set filter (pid=%d, tid=%02X, mask=%02X): %m", Pid, Tid, Mask);
2164 close(f);
2165 }
2166 }
2167 else
2168 esyslog("ERROR: can't open filter handle on '%s'", *FileName);
2169 return -1;
2170}
2171
2173{
2174 close(Handle);
2175}
2176
2177bool cDvbDevice::ProvidesDeliverySystem(int DeliverySystem) const
2178{
2179 return dvbTuner->ProvidesDeliverySystem(DeliverySystem);
2180}
2181
2182bool cDvbDevice::ProvidesSource(int Source) const
2183{
2184 int type = Source & cSource::st_Mask;
2185 return type == cSource::stNone
2186 || type == cSource::stAtsc && ProvidesDeliverySystem(SYS_ATSC)
2187 || type == cSource::stCable && (ProvidesDeliverySystem(SYS_DVBC_ANNEX_AC) || ProvidesDeliverySystem(SYS_DVBC_ANNEX_B))
2188 || type == cSource::stSat && (ProvidesDeliverySystem(SYS_DVBS) || ProvidesDeliverySystem(SYS_DVBS2))
2190}
2191
2193{
2194 if (!ProvidesSource(Channel->Source()))
2195 return false; // doesn't provide source
2196 if (!dvbTuner->ProvidesFrontend(Channel))
2197 return false; // requires modulation system which frontend doesn't provide
2198 cDvbTransponderParameters dtp(Channel->Parameters());
2199 if (!cSource::IsSat(Channel->Source()) ||
2200 (!Setup.DiSEqC || Diseqcs.Get(DeviceNumber() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization(), NULL)))
2201 return DeviceHooksProvidesTransponder(Channel);
2202 return false;
2203}
2204
2205bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
2206{
2207 bool result = false;
2208 bool hasPriority = Priority == IDLEPRIORITY || Priority > this->Priority();
2209 bool needsDetachReceivers = false;
2211
2212 if (ProvidesTransponder(Channel)) {
2213 result = hasPriority;
2214 if (Priority > IDLEPRIORITY) {
2215 if (Receiving()) {
2216 if (dvbTuner->IsTunedTo(Channel)) {
2217 if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0)) || Channel->Dpid(0) && !HasPid(Channel->Dpid(0))) {
2218 if (CamSlot() && Channel->Ca() >= CA_ENCRYPTED_MIN) {
2219 if (CamSlot()->CanDecrypt(Channel))
2220 result = true;
2221 else
2222 needsDetachReceivers = true;
2223 }
2224 else
2225 result = true;
2226 }
2227 else
2228 result = true;
2229 }
2230 else
2231 needsDetachReceivers = Receiving();
2232 }
2233 if (result) {
2234 cMutexLock MutexLock(&bondMutex);
2235 if (!BondingOk(Channel)) {
2236 // This device is bonded, so we need to check the priorities of the others:
2237 for (cDvbDevice *d = bondedDevice; d && d != this; d = d->bondedDevice) {
2238 if (d->Priority() >= Priority) {
2239 result = false;
2240 break;
2241 }
2242 needsDetachReceivers |= d->Receiving();
2243 }
2245 needsDetachReceivers |= Receiving();
2246 }
2247 }
2248 }
2249 }
2250 if (NeedsDetachReceivers)
2251 *NeedsDetachReceivers = needsDetachReceivers;
2252 return result;
2253}
2254
2256{
2257 return dvbTuner != NULL && DeviceHooksProvidesEIT();
2258}
2259
2261{
2262 return dvbTuner ? dvbTuner->NumProvidedSystems() : 0;
2263}
2264
2266{
2267 return dvbTuner ? dvbTuner->Positioner() : NULL;
2268}
2269
2270bool cDvbDevice::SignalStats(int &Valid, double *Strength, double *Cnr, double *BerPre, double *BerPost, double *Per, int *Status) const
2271{
2272 return dvbTuner ? dvbTuner->GetSignalStats(Valid, Strength, Cnr, BerPre, BerPost, Per, Status) : false;
2273}
2274
2276{
2277 return dvbTuner ? dvbTuner->GetSignalStrength() : -1;
2278}
2279
2281{
2282 return dvbTuner ? dvbTuner->GetSignalQuality() : -1;
2283}
2284
2286{
2287 return dvbTuner ? dvbTuner->GetTransponder() : NULL;
2288}
2289
2291{
2292 return dvbTuner ? dvbTuner->IsTunedTo(Channel) : false;
2293}
2294
2296{
2297 return BondingOk(Channel, true) && cDevice::MaySwitchTransponder(Channel);
2298}
2299
2300bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
2301{
2302 if (dvbTuner->ProvidesFrontend(Channel, true)) {
2303 dvbTuner->SetChannel(Channel);
2304 return true;
2305 }
2306 return false;
2307}
2308
2309bool cDvbDevice::HasLock(int TimeoutMs) const
2310{
2311 return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false;
2312}
2313
2315{
2317}
2318
2320{
2321 CloseDvr();
2322 fd_dvr = DvbOpen(DEV_DVB_DVR, adapter, frontend, O_RDONLY | O_NONBLOCK, true);
2323 if (fd_dvr >= 0)
2325 return fd_dvr >= 0;
2326}
2327
2329{
2330 if (fd_dvr >= 0) {
2331 delete tsBuffer;
2332 tsBuffer = NULL;
2333 close(fd_dvr);
2334 fd_dvr = -1;
2335 }
2336}
2337
2339{
2340 if (tsBuffer) {
2341 if (cCamSlot *cs = CamSlot()) {
2342 if (cs->WantsTsData()) {
2343 int Available;
2344 Data = tsBuffer->Get(&Available, checkTsBuffer);
2345 if (!Data)
2346 Available = 0;
2347 Data = cs->Decrypt(Data, Available);
2348 tsBuffer->Skip(Available);
2349 checkTsBuffer = Data != NULL;
2350 return true;
2351 }
2352 }
2353 Data = tsBuffer->Get();
2354 return true;
2355 }
2356 return false;
2357}
2358
2360{
2361 cMutexLock MutexLock(&bondMutex);
2362 cDvbDevice *d = this;
2363 do {
2364 d->cDevice::DetachAllReceivers();
2365 d = d->bondedDevice;
2366 } while (d && d != this && needsDetachBondedReceivers);
2368}
2369
2370// --- cDvbDeviceProbe -------------------------------------------------------
2371
2373
2375{
2376 DvbDeviceProbes.Add(this);
2377}
2378
2380{
2381 DvbDeviceProbes.Del(this, false);
2382}
2383
2384uint32_t cDvbDeviceProbe::GetSubsystemId(int Adapter, int Frontend)
2385{
2386 uint32_t SubsystemId = 0;
2387 cString FileName = cString::sprintf("/dev/dvb/adapter%d/frontend%d", Adapter, Frontend);
2388 struct stat st;
2389 if (stat(FileName, &st) == 0) {
2390 cReadDir d("/sys/class/dvb");
2391 if (d.Ok()) {
2392 struct dirent *e;
2393 while ((e = d.Next()) != NULL) {
2394 if (strstr(e->d_name, "frontend")) {
2395 FileName = cString::sprintf("/sys/class/dvb/%s/dev", e->d_name);
2396 if (FILE *f = fopen(FileName, "r")) {
2397 cReadLine ReadLine;
2398 char *s = ReadLine.Read(f);
2399 fclose(f);
2400 unsigned Major;
2401 unsigned Minor;
2402 if (s && 2 == sscanf(s, "%u:%u", &Major, &Minor)) {
2403 if (((Major << 8) | Minor) == st.st_rdev) {
2404 FileName = cString::sprintf("/sys/class/dvb/%s/device/subsystem_vendor", e->d_name);
2405 if ((f = fopen(FileName, "r")) != NULL) {
2406 if (char *s = ReadLine.Read(f))
2407 SubsystemId = strtoul(s, NULL, 0) << 16;
2408 fclose(f);
2409 }
2410 else {
2411 FileName = cString::sprintf("/sys/class/dvb/%s/device/idVendor", e->d_name);
2412 if ((f = fopen(FileName, "r")) != NULL) {
2413 if (char *s = ReadLine.Read(f))
2414 SubsystemId = strtoul(s, NULL, 16) << 16;
2415 fclose(f);
2416 }
2417 }
2418 FileName = cString::sprintf("/sys/class/dvb/%s/device/subsystem_device", e->d_name);
2419 if ((f = fopen(FileName, "r")) != NULL) {
2420 if (char *s = ReadLine.Read(f))
2421 SubsystemId |= strtoul(s, NULL, 0);
2422 fclose(f);
2423 }
2424 else {
2425 FileName = cString::sprintf("/sys/class/dvb/%s/device/idProduct", e->d_name);
2426 if ((f = fopen(FileName, "r")) != NULL) {
2427 if (char *s = ReadLine.Read(f))
2428 SubsystemId |= strtoul(s, NULL, 16);
2429 fclose(f);
2430 }
2431 }
2432 break;
2433 }
2434 }
2435 }
2436 }
2437 }
2438 }
2439 }
2440 return SubsystemId;
2441}
#define CA_ENCRYPTED_MIN
Definition: channels.h:44
Definition: ci.h:232
int Vpid(void) const
Definition: channels.h:153
int Source(void) const
Definition: channels.h:151
int Number(void) const
Definition: channels.h:178
const char * Name(void) const
Definition: channels.c:107
int Dpid(int i) const
Definition: channels.h:160
int Apid(int i) const
Definition: channels.h:159
const char * Parameters(void) const
Definition: channels.h:181
int Ca(int Index=0) const
Definition: channels.h:172
int Frequency(void) const
Returns the actual frequency, as given in 'channels.conf'.
Definition: channels.h:148
bool IsSat(void) const
Definition: channels.h:187
bool IsCable(void) const
Definition: channels.h:186
bool SetTransponderData(int Source, int Frequency, int Srate, const char *Parameters, bool Quiet=false)
Definition: channels.c:180
bool IsTerr(void) const
Definition: channels.h:188
int Transponder(void) const
Returns the transponder frequency in MHz, plus the polarization in case of sat.
Definition: channels.c:146
int Srate(void) const
Definition: channels.h:152
bool IsAtsc(void) const
Definition: channels.h:185
bool TimedWait(cMutex &Mutex, int TimeoutMs)
Definition: thread.c:132
void Broadcast(void)
Definition: thread.c:150
static int NextCardIndex(int n=0)
Calculates the next card index.
Definition: device.c:153
void StopSectionHandler(void)
A device that has called StartSectionHandler() must call this function (typically in its destructor) ...
Definition: device.c:673
static bool UseDevice(int n)
Tells whether the device with the given card index shall be used in this instance of VDR.
Definition: device.h:141
bool IsPrimaryDevice(void) const
Definition: device.h:220
void StartSectionHandler(void)
A derived device that provides section data must call this function (typically in its constructor) to...
Definition: device.c:662
@ ptTeletext
Definition: device.h:398
bool HasPid(int Pid) const
Returns true if this device is currently receiving the given PID.
Definition: device.c:542
static cDevice * PrimaryDevice(void)
Returns the primary device.
Definition: device.h:148
virtual bool MaySwitchTransponder(const cChannel *Channel) const
Returns true if it is ok to switch to the Channel's transponder on this device, without disturbing an...
Definition: device.c:802
static cDevice * GetDevice(int Index)
Gets the device with the given Index.
Definition: device.c:228
bool DeviceHooksProvidesEIT(void) const
Definition: device.c:732
int DeviceNumber(void) const
Returns the number of this device (0 ... numDevices - 1).
Definition: device.c:165
bool Receiving(bool Dummy=false) const
Returns true if we are currently receiving. The parameter has no meaning (for backwards compatibility...
Definition: device.c:1678
const cSdtFilter * SdtFilter(void) const
Definition: device.h:461
cSectionHandler * SectionHandler(void) const
Definition: device.h:462
static int NumDevices(void)
Returns the total number of devices.
Definition: device.h:129
int Priority(void) const
Returns the priority of the current receiving session (-MAXPRIORITY..MAXPRIORITY),...
Definition: device.c:1660
cCamSlot * CamSlot(void) const
Returns the CAM slot that is currently used with this device, or NULL if no CAM slot is in use.
Definition: device.h:481
void DelLivePids(void)
Deletes the live viewing PIDs.
Definition: device.c:653
bool DeviceHooksProvidesTransponder(const cChannel *Channel) const
Definition: device.c:721
Definition: diseqc.h:62
eDiseqcActions Execute(const char **CurrentAction, uchar *Codes, uint8_t *MaxCodes, const cScr *Scr, int *Frequency) const
Parses the DiSEqC commands and returns the appropriate action code with every call.
Definition: diseqc.c:402
int Position(void) const
Indicates which positioning mode to use in order to move the dish to a given satellite position.
Definition: diseqc.h:126
bool IsScr(void) const
Returns true if this DiSEqC sequence uses Satellite Channel Routing.
Definition: diseqc.h:132
eDiseqcActions
Definition: diseqc.h:64
@ daVoltage18
Definition: diseqc.h:69
@ daToneOff
Definition: diseqc.h:66
@ daNone
Definition: diseqc.h:65
@ daScr
Definition: diseqc.h:74
@ daCodes
Definition: diseqc.h:75
@ daToneOn
Definition: diseqc.h:67
@ daMiniA
Definition: diseqc.h:70
@ daWait
Definition: diseqc.h:76
@ daPositionA
Definition: diseqc.h:73
@ daMiniB
Definition: diseqc.h:71
@ daPositionN
Definition: diseqc.h:72
@ daVoltage13
Definition: diseqc.h:68
const cDiseqc * Get(int Device, int Source, int Frequency, char Polarization, const cScr **Scr) const
Selects a DiSEqC entry suitable for the given Device and tuning parameters.
Definition: diseqc.c:447
static cDvbCiAdapter * CreateCiAdapter(cDevice *Device, int Fd)
Definition: dvbci.c:102
cDvbDeviceProbe(void)
Definition: dvbdevice.c:2374
static uint32_t GetSubsystemId(int Adapter, int Frontend)
Definition: dvbdevice.c:2384
virtual ~cDvbDeviceProbe()
Definition: dvbdevice.c:2379
The cDvbDevice implements a DVB device which can be accessed through the Linux DVB driver API.
Definition: dvbdevice.h:168
virtual cString DeviceType(void) const
Returns a string identifying the type of this device (like "DVB-S").
Definition: dvbdevice.c:1922
virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask)
Opens a file handle for the given filter data.
Definition: dvbdevice.c:2144
static void UnBondDevices(void)
Unbonds all devices.
Definition: dvbdevice.c:2040
virtual bool IsTunedToTransponder(const cChannel *Channel) const
Returns true if this device is currently tuned to the given Channel's transponder.
Definition: dvbdevice.c:2290
static void SetTransferModeForDolbyDigital(int Mode)
Definition: dvbdevice.c:2314
cDvbDevice(int Adapter, int Frontend)
Definition: dvbdevice.c:1835
virtual int SignalStrength(void) const
Returns the "strength" of the currently received signal.
Definition: dvbdevice.c:2275
cCiAdapter * ciAdapter
Definition: dvbdevice.h:227
virtual bool HasLock(int TimeoutMs=0) const
Returns true if the device has a lock on the requested transponder.
Definition: dvbdevice.c:2309
virtual void CloseFilter(int Handle)
Closes a file handle that has previously been opened by OpenFilter().
Definition: dvbdevice.c:2172
static int setTransferModeForDolbyDigital
Definition: dvbdevice.h:271
static bool Exists(int Adapter, int Frontend)
Checks whether the given adapter/frontend exists.
Definition: dvbdevice.c:1892
virtual bool SetPid(cPidHandle *Handle, int Type, bool On)
Does the actual PID setting on this device.
Definition: dvbdevice.c:2102
static bool BondDevices(const char *Bondings)
Bonds the devices as defined in the given Bondings string.
Definition: dvbdevice.c:1999
virtual int NumProvidedSystems(void) const
Returns the number of individual "delivery systems" this device provides.
Definition: dvbdevice.c:2260
virtual void DetachAllReceivers(void)
Detaches all receivers from this device.
Definition: dvbdevice.c:2359
bool checkTsBuffer
Definition: dvbdevice.h:185
virtual bool ProvidesEIT(void) const
Returns true if this device provides EIT data and thus wants to be tuned to the channels it can recei...
Definition: dvbdevice.c:2255
cDvbDevice * bondedDevice
Definition: dvbdevice.h:187
virtual bool ProvidesTransponder(const cChannel *Channel) const
Returns true if this device can provide the transponder of the given Channel (which implies that it c...
Definition: dvbdevice.c:2192
virtual bool MaySwitchTransponder(const cChannel *Channel) const
Returns true if it is ok to switch to the Channel's transponder on this device, without disturbing an...
Definition: dvbdevice.c:2295
virtual bool OpenDvr(void)
Opens the DVR of this device and prepares it to deliver a Transport Stream for use in a cReceiver.
Definition: dvbdevice.c:2319
virtual void CloseDvr(void)
Shuts down the DVR.
Definition: dvbdevice.c:2328
static bool Initialize(void)
Initializes the DVB devices.
Definition: dvbdevice.c:1936
void UnBond(void)
Removes this device from any bonding it might have with other devices.
Definition: dvbdevice.c:2072
int adapter
Definition: dvbdevice.h:181
static cMutex bondMutex
Definition: dvbdevice.h:186
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView)
Sets the device to the given channel (actual physical setup).
Definition: dvbdevice.c:2300
cDvbTuner * dvbTuner
Definition: dvbdevice.h:232
bool BondingOk(const cChannel *Channel, bool ConsiderOccupied=false) const
Returns true if this device is either not bonded to any other device, or the given Channel is on the ...
Definition: dvbdevice.c:2089
bool needsDetachBondedReceivers
Definition: dvbdevice.h:188
virtual ~cDvbDevice()
Definition: dvbdevice.c:1863
virtual bool SignalStats(int &Valid, double *Strength=NULL, double *Cnr=NULL, double *BerPre=NULL, double *BerPost=NULL, double *Per=NULL, int *Status=NULL) const
Returns statistics about the currently received signal (if available).
Definition: dvbdevice.c:2270
virtual cString DeviceName(void) const
Returns a string identifying the name of this device.
Definition: dvbdevice.c:1929
int Frontend(void) const
Definition: dvbdevice.c:1887
virtual bool GetTSPacket(uchar *&Data)
Gets exactly one TS packet from the DVR of this device and returns a pointer to it in Data.
Definition: dvbdevice.c:2338
virtual const cChannel * GetCurrentlyTunedTransponder(void) const
Returns a pointer to the currently tuned transponder.
Definition: dvbdevice.c:2285
bool Bond(cDvbDevice *Device)
Bonds this device with the given Device, making both of them use the same satellite cable and LNB.
Definition: dvbdevice.c:2048
virtual bool ProvidesDeliverySystem(int DeliverySystem) const
Definition: dvbdevice.c:2177
virtual bool ProvidesChannel(const cChannel *Channel, int Priority=IDLEPRIORITY, bool *NeedsDetachReceivers=NULL) const
Returns true if this device can provide the given channel.
Definition: dvbdevice.c:2205
virtual bool ProvidesSource(int Source) const
Returns true if this device can provide the given source.
Definition: dvbdevice.c:2182
virtual bool HasCi(void)
Returns true if this device has a Common Interface.
Definition: dvbdevice.c:2097
int frontend
Definition: dvbdevice.h:181
int fd_dvr
Definition: dvbdevice.h:184
virtual int SignalQuality(void) const
Returns the "quality" of the currently received signal.
Definition: dvbdevice.c:2280
static bool Probe(int Adapter, int Frontend)
Probes for existing DVB devices.
Definition: dvbdevice.c:1909
cTSBuffer * tsBuffer
< Controls how the DVB device handles Transfer Mode when replaying Dolby Digital audio.
Definition: dvbdevice.h:283
static bool useDvbDevices
Definition: dvbdevice.h:175
int Adapter(void) const
Definition: dvbdevice.h:192
virtual const cPositioner * Positioner(void) const
Returns a pointer to the positioner (if any) this device has used to move the satellite dish to the r...
Definition: dvbdevice.c:2265
int Open(void)
Definition: dvbdevice.c:383
uint32_t subsystemId
Definition: dvbdevice.c:346
int fd_frontend
Definition: dvbdevice.c:345
bool ProvidesModulation(int System, int StreamId, int Modulation) const
Definition: dvbdevice.c:408
uint32_t SubsystemId(void) const
Definition: dvbdevice.c:361
int NumDeliverySystems(void) const
Definition: dvbdevice.c:359
const char * FrontendName(void)
Definition: dvbdevice.c:356
cDvbFrontend(int Adapter, int Frontend)
Definition: dvbdevice.c:364
int NumModulations(void) const
Definition: dvbdevice.c:360
bool ProvidesDeliverySystem(int DeliverySystem) const
Definition: dvbdevice.c:399
int numModulations
Definition: dvbdevice.c:349
void Close(void)
Definition: dvbdevice.c:390
cVector< int > deliverySystems
Definition: dvbdevice.c:348
bool QueryDeliverySystems(void)
Definition: dvbdevice.c:426
dvb_frontend_info frontendInfo
Definition: dvbdevice.c:347
cDvbSourceParam(char Source, const char *Description)
Definition: dvbdevice.c:1782
cDvbTransponderParameters dtp
Definition: dvbdevice.c:1774
virtual void SetData(cChannel *Channel)
Sets all source specific parameters to those of the given Channel.
Definition: dvbdevice.c:1789
virtual cOsdItem * GetOsdItem(void)
Returns all the OSD items necessary for editing the source specific parameters of the channel that wa...
Definition: dvbdevice.c:1801
virtual void GetData(cChannel *Channel)
Copies all source specific parameters to the given Channel.
Definition: dvbdevice.c:1796
int StreamId(void) const
Definition: dvbdevice.h:138
cString ToString(char Type) const
Definition: dvbdevice.c:214
const char * ParseParameter(const char *s, int &Value, const tDvbParameterMap *Map=NULL)
Definition: dvbdevice.c:238
char Polarization(void) const
Definition: dvbdevice.h:127
int Guard(void) const
Definition: dvbdevice.h:135
bool Parse(const char *s)
Definition: dvbdevice.c:254
int Pilot(void) const
Definition: dvbdevice.h:141
int PrintParameter(char *p, char Name, int Value) const
Definition: dvbdevice.c:209
int RollOff(void) const
Definition: dvbdevice.h:137
int CoderateL(void) const
Definition: dvbdevice.h:131
int Transmission(void) const
Definition: dvbdevice.h:134
int Bandwidth(void) const
Definition: dvbdevice.h:129
int System(void) const
Definition: dvbdevice.h:133
int Inversion(void) const
Definition: dvbdevice.h:128
cDvbTransponderParameters(const char *Parameters=NULL)
Definition: dvbdevice.c:204
int Modulation(void) const
Definition: dvbdevice.h:132
int Hierarchy(void) const
Definition: dvbdevice.h:136
int CoderateH(void) const
Definition: dvbdevice.h:130
int diseqcOffset
Definition: dvbdevice.c:550
cPositioner * positioner
Definition: dvbdevice.c:552
void UnBond(void)
Definition: dvbdevice.c:722
int frontend
Definition: dvbdevice.c:537
void ExecuteDiseqc(const cDiseqc *Diseqc, int *Frequency)
Definition: dvbdevice.c:1477
time_t lastUncChange
Definition: dvbdevice.c:547
bool Bond(cDvbTuner *Tuner)
Definition: dvbdevice.c:706
cCondVar newSet
Definition: dvbdevice.c:558
int lockTimeout
Definition: dvbdevice.c:543
bool lnbPowerTurnedOn
Definition: dvbdevice.c:554
bool SetFrontend(void)
Definition: dvbdevice.c:1540
void ClearEventQueue(void) const
Definition: dvbdevice.c:847
cChannel channel
Definition: dvbdevice.c:548
eTunerStatus tunerStatus
Definition: dvbdevice.c:555
int NumProvidedSystems(void) const
Definition: dvbdevice.c:580
void SetChannel(const cChannel *Channel)
Definition: dvbdevice.c:802
cDvbTuner(const cDvbDevice *Device, int Adapter, int Frontend)
Definition: dvbdevice.c:597
bool BondingOk(const cChannel *Channel, bool ConsiderOccupied=false) const
Definition: dvbdevice.c:755
void ResetToneAndVoltage(void)
Definition: dvbdevice.c:1534
cPositioner * GetPositioner(void)
Definition: dvbdevice.c:1468
const cScr * scr
Definition: dvbdevice.c:553
cString GetBondingParams(const cChannel *Channel=NULL) const
Definition: dvbdevice.c:738
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: dvbdevice.c:1680
int frontendType
Definition: dvbdevice.c:533
bool Locked(int TimeoutMs=0)
Definition: dvbdevice.c:835
int GetSignalQuality(void) const
Definition: dvbdevice.c:1289
uint32_t lastUncValue
Definition: dvbdevice.c:545
int adapter
Definition: dvbdevice.c:536
bool ProvidesModulation(int System, int StreamId, int Modulation) const
Definition: dvbdevice.c:674
const cPositioner * Positioner(void) const
Definition: dvbdevice.c:589
uint32_t lastUncDelta
Definition: dvbdevice.c:546
cVector< cDvbFrontend * > dvbFrontends
Definition: dvbdevice.c:538
bool IsBondedMaster(void) const
Definition: dvbdevice.c:563
cDvbTuner * GetBondedMaster(void)
Definition: dvbdevice.c:771
int tuneTimeout
Definition: dvbdevice.c:542
uint32_t SubsystemId(void) const
Definition: dvbdevice.c:585
int Frontend(void) const
Definition: dvbdevice.c:577
bool GetSignalStats(int &Valid, double *Strength=NULL, double *Cnr=NULL, double *BerPre=NULL, double *BerPost=NULL, double *Per=NULL, int *Status=NULL) const
Definition: dvbdevice.c:874
bool bondedMaster
Definition: dvbdevice.c:560
int fd_frontend
Definition: dvbdevice.c:535
bool ProvidesFrontend(const cChannel *Channel, bool Activate=false) const
Definition: dvbdevice.c:683
int FrontendType(void) const
Definition: dvbdevice.c:578
int lastSource
Definition: dvbdevice.c:551
bool IsTunedTo(const cChannel *Channel) const
Definition: dvbdevice.c:792
@ tsPositioning
Definition: dvbdevice.c:532
cDvbFrontend * dvbFrontend
Definition: dvbdevice.c:539
static cMutex bondMutex
Definition: dvbdevice.c:531
const cDiseqc * lastDiseqc
Definition: dvbdevice.c:549
time_t lastTimeoutReport
Definition: dvbdevice.c:544
cMutex mutex
Definition: dvbdevice.c:556
const cDvbDevice * device
Definition: dvbdevice.c:534
int GetSignalStrength(void) const
Definition: dvbdevice.c:1224
bool ProvidesDeliverySystem(int DeliverySystem) const
Definition: dvbdevice.c:665
cCondVar locked
Definition: dvbdevice.c:557
cDvbTuner * bondedTuner
Definition: dvbdevice.c:559
bool GetFrontendStatus(fe_status_t &Status) const
Definition: dvbdevice.c:857
const cChannel * GetTransponder(void) const
Definition: dvbdevice.c:584
int numModulations
Definition: dvbdevice.c:541
int numDeliverySystems
Definition: dvbdevice.c:540
const char * FrontendName(void)
Definition: dvbdevice.c:579
virtual ~cDvbTuner()
Definition: dvbdevice.c:648
void Lock(void)
Definition: thread.c:222
void Unlock(void)
Definition: thread.c:228
bool Poll(int TimeoutMs=0)
Definition: tools.c:1539
A steerable satellite dish generally points to the south on the northern hemisphere,...
static cPositioner * GetPositioner(void)
Returns a previously created positioner.
Definition: positioner.c:133
void SetFrontend(int Frontend)
This function is called whenever the positioner is connected to a DVB frontend.
virtual bool IsMoving(void) const
Returns true if the dish is currently moving as a result of a call to GotoPosition() or GotoAngle().
Definition: positioner.c:127
virtual void GotoPosition(uint Number, int Longitude)
Move the dish to the satellite position stored under the given Number.
Definition: positioner.c:100
virtual void GotoAngle(int Longitude)
Move the dish to the given angular position.
Definition: positioner.c:107
struct dirent * Next(void)
Definition: tools.c:1562
bool Ok(void)
char * Read(FILE *f)
Definition: tools.c:1481
int FirstDeviceIndex(int DeviceIndex) const
Returns the first device index (starting at 0) that uses the same sat cable number as the device with...
Definition: config.c:116
Definition: diseqc.h:34
bool TransponderWrong(void) const
void SetStatus(bool On)
Definition: sections.c:147
int LnbSLOF
Definition: config.h:277
int LnbFrequLo
Definition: config.h:278
int DiSEqC
Definition: config.h:280
int LnbFrequHi
Definition: config.h:279
char Source(void) const
int Position(void)
Returns the orbital position of the satellite in case this is a DVB-S source (zero otherwise).
static bool IsSat(int Code)
void Sort(bool IgnoreCase=false)
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1149
Derived cDevice classes that can receive channels will have to provide Transport Stream (TS) packets ...
Definition: device.h:876
uchar * Get(int *Available=NULL, bool CheckAvailable=false)
Returns a pointer to the first TS packet in the buffer.
Definition: device.c:1926
void Skip(int Count)
If after a call to Get() more or less than TS_SIZE of the available data has been processed,...
Definition: device.c:1956
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
Definition: thread.c:304
void SetDescription(const char *Description,...) __attribute__((format(printf
Definition: thread.c:267
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
Definition: thread.c:354
void Set(int Ms=0)
Sets the timer.
Definition: tools.c:792
bool TimedOut(void) const
Definition: tools.c:797
int Size(void) const
virtual void Clear(void)
virtual void Append(T Data)
cSetup Setup
Definition: config.c:372
#define IDLEPRIORITY
Definition: config.h:47
#define DTV_STAT_HAS_NONE
Definition: device.h:112
#define DTV_STAT_HAS_VITERBI
Definition: device.h:115
#define DTV_STAT_VALID_STRENGTH
Definition: device.h:105
#define DTV_STAT_VALID_BERPRE
Definition: device.h:107
#define DTV_STAT_VALID_NONE
The cDevice class is the base from which actual devices can be derived.
Definition: device.h:104
#define DTV_STAT_VALID_PER
Definition: device.h:109
#define MAXDEVICES
Definition: device.h:29
#define DTV_STAT_HAS_CARRIER
Definition: device.h:114
#define DTV_STAT_VALID_CNR
Definition: device.h:106
#define DTV_STAT_HAS_LOCK
Definition: device.h:117
#define DTV_STAT_VALID_BERPOST
Definition: device.h:108
#define DTV_STAT_HAS_SYNC
Definition: device.h:116
#define DTV_STAT_HAS_SIGNAL
Definition: device.h:113
#define DTV_STAT_VALID_STATUS
Definition: device.h:110
cDiseqcs Diseqcs
Definition: diseqc.c:439
int DvbOpen(const char *Name, int Adapter, int Frontend, int Mode, bool ReportError)
Definition: dvbdevice.c:1878
int SignalToSQI(const cChannel *Channel, int Signal, int Ber, int FeModulation, int FeCoderateH, int FeFec)
Definition: dvbdevice.c:1082
static const int DeliverySystemNamesMax
Definition: dvbdevice.c:324
const tDvbParameterMap CoderateValues[]
Definition: dvbdevice.c:65
int dB1000toPercent(int dB1000, int Low, int High)
Definition: dvbdevice.c:984
#define DVB_SYSTEM_1
Definition: dvbdevice.c:98
const tDvbParameterMap PilotValues[]
Definition: dvbdevice.c:41
int StrengthToSSI(const cChannel *Channel, int Strength, int FeModulation, int FeCoderateH, int FeFec)
Definition: dvbdevice.c:1005
#define REF_T2(q1, q2, q3, q4)
Definition: dvbdevice.c:1002
const tDvbParameterMap GuardValues[]
Definition: dvbdevice.c:124
#define DVBC_LOCK_TIMEOUT
Definition: dvbdevice.c:29
const tDvbParameterMap SystemValuesSat[]
Definition: dvbdevice.c:101
#define SETCMD(c, d)
Definition: dvbdevice.c:334
const tDvbParameterMap HierarchyValues[]
Definition: dvbdevice.c:136
#define DVBC_TUNE_TIMEOUT
Definition: dvbdevice.c:28
int DriverIndex(int Value, const tDvbParameterMap *Map)
Definition: dvbdevice.c:164
#define REF_C1(q1, q2, q3, q4, q5)
Definition: dvbdevice.c:1003
cString DvbName(const char *Name, int Adapter, int Frontend)
Definition: dvbdevice.c:1873
#define SCR_RANDOM_TIMEOUT
Definition: dvbdevice.c:35
#define RB_NUM_SECTIONS
Definition: dvbdevice.c:2142
#define ST(s)
#define REF_S2(q1, q2, q3, q4)
Definition: dvbdevice.c:1000
#define TSBUFFERSIZE
Definition: dvbdevice.c:37
const tDvbParameterMap BandwidthValues[]
Definition: dvbdevice.c:55
#define DVB_SYSTEM_2
Definition: dvbdevice.c:99
static const char * GetDeliverySystemName(int Index)
Definition: dvbdevice.c:326
#define TUNER_POLL_TIMEOUT
Definition: dvbdevice.c:511
#define LOCK_THRESHOLD
Definition: dvbdevice.c:1287
cList< cDvbDeviceProbe > DvbDeviceProbes
Definition: dvbdevice.c:2372
#define MAXFRONTENDCMDS
Definition: dvbdevice.c:333
const tDvbParameterMap TransmissionValues[]
Definition: dvbdevice.c:113
const tDvbParameterMap InversionValues[]
Definition: dvbdevice.c:48
static unsigned int FrequencyToHz(unsigned int f)
Definition: dvbdevice.c:1461
#define ATSC_TUNE_TIMEOUT
Definition: dvbdevice.c:32
const tDvbParameterMap RollOffValues[]
Definition: dvbdevice.c:145
const tDvbParameterMap ModulationValues[]
Definition: dvbdevice.c:81
const tDvbParameterMap SystemValuesTerr[]
Definition: dvbdevice.c:107
#define REF_S1(q1)
Definition: dvbdevice.c:999
#define DVBT_TUNE_TIMEOUT
Definition: dvbdevice.c:30
int UserIndex(int Value, const tDvbParameterMap *Map)
Definition: dvbdevice.c:153
#define REF_T1(q1, q2, q3)
Definition: dvbdevice.c:1001
static int GetRequiredDeliverySystem(const cChannel *Channel, const cDvbTransponderParameters *Dtp)
Definition: dvbdevice.c:513
static int DvbApiVersion
Definition: dvbdevice.c:24
int MapToUser(int Value, const tDvbParameterMap *Map, const char **String)
Definition: dvbdevice.c:175
const char * MapToUserString(int Value, const tDvbParameterMap *Map)
Definition: dvbdevice.c:186
#define ATSC_LOCK_TIMEOUT
Definition: dvbdevice.c:33
#define DVBS_LOCK_TIMEOUT
Definition: dvbdevice.c:27
const char * DeliverySystemNames[]
Definition: dvbdevice.c:300
int MapToDriver(int Value, const tDvbParameterMap *Map)
Definition: dvbdevice.c:194
#define BER_ERROR_FREE
Definition: dvbdevice.c:1080
#define DVBS_TUNE_TIMEOUT
Definition: dvbdevice.c:26
#define DVBT_LOCK_TIMEOUT
Definition: dvbdevice.c:31
@ SYS_DVBT2
Definition: dvbdevice.h:52
#define DEV_DVB_BASE
Definition: dvbdevice.h:71
#define DEV_DVB_DVR
Definition: dvbdevice.h:75
#define DEV_DVB_DEMUX
Definition: dvbdevice.h:76
@ TRANSMISSION_MODE_16K
Definition: dvbdevice.h:43
@ TRANSMISSION_MODE_1K
Definition: dvbdevice.h:42
@ TRANSMISSION_MODE_32K
Definition: dvbdevice.h:44
#define DTV_ENUM_DELSYS
Definition: dvbdevice.h:57
@ GUARD_INTERVAL_19_128
Definition: dvbdevice.h:48
@ GUARD_INTERVAL_19_256
Definition: dvbdevice.h:49
@ GUARD_INTERVAL_1_128
Definition: dvbdevice.h:47
#define DEV_DVB_ADAPTER
Definition: dvbdevice.h:72
#define DTV_STREAM_ID
Definition: dvbdevice.h:64
#define DEV_DVB_CA
Definition: dvbdevice.h:79
@ FE_CAN_2G_MODULATION
Definition: dvbdevice.h:27
@ TRANSMISSION_MODE_4K
Definition: dvbdevice.h:30
@ FE_CAN_MULTISTREAM
Definition: dvbdevice.h:62
#define DVBAPIVERSION
Definition: dvbdevice.h:17
@ FE_CAN_TURBO_FEC
Definition: dvbdevice.h:36
#define DTV_DVBT2_PLP_ID_LEGACY
Definition: dvbdevice.h:65
#define DEV_DVB_FRONTEND
Definition: dvbdevice.h:74
#define tr(s)
Definition: i18n.h:85
#define trNOOP(s)
Definition: i18n.h:88
#define MAX_SECTION_SIZE
#define EITPID
T constrain(T v, T l, T h)
#define LOG_ERROR_STR(s)
unsigned char uchar
#define CHECK(s)
#define dsyslog(a...)
T min(T a, T b)
#define esyslog(a...)
#define LOG_ERROR
char * strn0cpy(char *dest, const char *src, size_t n)
Definition: tools.c:131
#define isyslog(a...)
cString AddDirectory(const char *DirName, const char *FileName)
Definition: tools.c:402
unsigned char u_char
const char * userString
Definition: dvbdevice.h:84