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