vdr  2.2.0
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 3.14 2015/01/14 12:09:19 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 DVBS_TUNE_TIMEOUT 9000 //ms
27 #define DVBS_LOCK_TIMEOUT 2000 //ms
28 #define DVBC_TUNE_TIMEOUT 9000 //ms
29 #define DVBC_LOCK_TIMEOUT 2000 //ms
30 #define DVBT_TUNE_TIMEOUT 9000 //ms
31 #define DVBT_LOCK_TIMEOUT 2000 //ms
32 #define ATSC_TUNE_TIMEOUT 9000 //ms
33 #define ATSC_LOCK_TIMEOUT 2000 //ms
34 
35 #define SCR_RANDOM_TIMEOUT 500 // ms (add random value up to this when tuning SCR device to avoid lockups)
36 
37 // --- DVB Parameter Maps ----------------------------------------------------
38 
40  { 0, PILOT_OFF, trNOOP("off") },
41  { 1, PILOT_ON, trNOOP("on") },
42  { 999, PILOT_AUTO, trNOOP("auto") },
43  { -1, 0, NULL }
44  };
45 
47  { 0, INVERSION_OFF, trNOOP("off") },
48  { 1, INVERSION_ON, trNOOP("on") },
49  { 999, INVERSION_AUTO, trNOOP("auto") },
50  { -1, 0, NULL }
51  };
52 
54  { 5, 5000000, "5 MHz" },
55  { 6, 6000000, "6 MHz" },
56  { 7, 7000000, "7 MHz" },
57  { 8, 8000000, "8 MHz" },
58  { 10, 10000000, "10 MHz" },
59  { 1712, 1712000, "1.712 MHz" },
60  { -1, 0, NULL }
61  };
62 
64  { 0, FEC_NONE, trNOOP("none") },
65  { 12, FEC_1_2, "1/2" },
66  { 23, FEC_2_3, "2/3" },
67  { 34, FEC_3_4, "3/4" },
68  { 35, FEC_3_5, "3/5" },
69  { 45, FEC_4_5, "4/5" },
70  { 56, FEC_5_6, "5/6" },
71  { 67, FEC_6_7, "6/7" },
72  { 78, FEC_7_8, "7/8" },
73  { 89, FEC_8_9, "8/9" },
74  { 910, FEC_9_10, "9/10" },
75  { 999, FEC_AUTO, trNOOP("auto") },
76  { -1, 0, NULL }
77  };
78 
80  { 16, QAM_16, "QAM16" },
81  { 32, QAM_32, "QAM32" },
82  { 64, QAM_64, "QAM64" },
83  { 128, QAM_128, "QAM128" },
84  { 256, QAM_256, "QAM256" },
85  { 2, QPSK, "QPSK" },
86  { 5, PSK_8, "8PSK" },
87  { 6, APSK_16, "16APSK" },
88  { 7, APSK_32, "32APSK" },
89  { 10, VSB_8, "VSB8" },
90  { 11, VSB_16, "VSB16" },
91  { 12, DQPSK, "DQPSK" },
92  { 999, QAM_AUTO, trNOOP("auto") },
93  { -1, 0, NULL }
94  };
95 
96 #define DVB_SYSTEM_1 0 // see also nit.c
97 #define DVB_SYSTEM_2 1
98 
100  { 0, DVB_SYSTEM_1, "DVB-S" },
101  { 1, DVB_SYSTEM_2, "DVB-S2" },
102  { -1, 0, NULL }
103  };
104 
106  { 0, DVB_SYSTEM_1, "DVB-T" },
107  { 1, DVB_SYSTEM_2, "DVB-T2" },
108  { -1, 0, NULL }
109  };
110 
112  { 1, TRANSMISSION_MODE_1K, "1K" },
113  { 2, TRANSMISSION_MODE_2K, "2K" },
114  { 4, TRANSMISSION_MODE_4K, "4K" },
115  { 8, TRANSMISSION_MODE_8K, "8K" },
116  { 16, TRANSMISSION_MODE_16K, "16K" },
117  { 32, TRANSMISSION_MODE_32K, "32K" },
118  { 999, TRANSMISSION_MODE_AUTO, trNOOP("auto") },
119  { -1, 0, NULL }
120  };
121 
123  { 4, GUARD_INTERVAL_1_4, "1/4" },
124  { 8, GUARD_INTERVAL_1_8, "1/8" },
125  { 16, GUARD_INTERVAL_1_16, "1/16" },
126  { 32, GUARD_INTERVAL_1_32, "1/32" },
127  { 128, GUARD_INTERVAL_1_128, "1/128" },
128  { 19128, GUARD_INTERVAL_19_128, "19/128" },
129  { 19256, GUARD_INTERVAL_19_256, "19/256" },
130  { 999, GUARD_INTERVAL_AUTO, trNOOP("auto") },
131  { -1, 0, NULL }
132  };
133 
135  { 0, HIERARCHY_NONE, trNOOP("none") },
136  { 1, HIERARCHY_1, "1" },
137  { 2, HIERARCHY_2, "2" },
138  { 4, HIERARCHY_4, "4" },
139  { 999, HIERARCHY_AUTO, trNOOP("auto") },
140  { -1, 0, NULL }
141  };
142 
144  { 0, ROLLOFF_AUTO, trNOOP("auto") },
145  { 20, ROLLOFF_20, "0.20" },
146  { 25, ROLLOFF_25, "0.25" },
147  { 35, ROLLOFF_35, "0.35" },
148  { -1, 0, NULL }
149  };
150 
151 int UserIndex(int Value, const tDvbParameterMap *Map)
152 {
153  const tDvbParameterMap *map = Map;
154  while (map && map->userValue != -1) {
155  if (map->userValue == Value)
156  return map - Map;
157  map++;
158  }
159  return -1;
160 }
161 
162 int DriverIndex(int Value, const tDvbParameterMap *Map)
163 {
164  const tDvbParameterMap *map = Map;
165  while (map && map->userValue != -1) {
166  if (map->driverValue == Value)
167  return map - Map;
168  map++;
169  }
170  return -1;
171 }
172 
173 int MapToUser(int Value, const tDvbParameterMap *Map, const char **String)
174 {
175  int n = DriverIndex(Value, Map);
176  if (n >= 0) {
177  if (String)
178  *String = tr(Map[n].userString);
179  return Map[n].userValue;
180  }
181  return -1;
182 }
183 
184 const char *MapToUserString(int Value, const tDvbParameterMap *Map)
185 {
186  int n = DriverIndex(Value, Map);
187  if (n >= 0)
188  return Map[n].userString;
189  return "???";
190 }
191 
192 int MapToDriver(int Value, const tDvbParameterMap *Map)
193 {
194  int n = UserIndex(Value, Map);
195  if (n >= 0)
196  return Map[n].driverValue;
197  return -1;
198 }
199 
200 // --- cDvbTransponderParameters ---------------------------------------------
201 
203 {
204  polarization = 0;
205  inversion = INVERSION_AUTO;
206  bandwidth = 8000000;
207  coderateH = FEC_AUTO;
208  coderateL = FEC_AUTO;
209  modulation = QPSK;
211  transmission = TRANSMISSION_MODE_AUTO;
212  guard = GUARD_INTERVAL_AUTO;
213  hierarchy = HIERARCHY_AUTO;
214  rollOff = ROLLOFF_AUTO;
215  streamId = 0;
216  t2systemId = 0;
217  sisoMiso = 0;
218  pilot = PILOT_AUTO;
219  lcn = 0;
220  Parse(Parameters);
221 }
222 
223 int cDvbTransponderParameters::PrintParameter(char *p, char Name, int Value) const
224 {
225  return Value >= 0 && Value != 999 ? sprintf(p, "%c%d", Name, Value) : 0;
226 }
227 
229 {
230 #define ST(s) if (strchr(s, Type) && (strchr(s, '0' + system + 1) || strchr(s, '*')))
231  char buffer[64];
232  char *q = buffer;
233  *q = 0;
234  ST(" S *") q += sprintf(q, "%c", polarization);
235  ST(" C T*") q += PrintParameter(q, 'A', lcn);
236  ST(" T*") q += PrintParameter(q, 'B', MapToUser(bandwidth, BandwidthValues));
237  ST(" CST*") q += PrintParameter(q, 'C', MapToUser(coderateH, CoderateValues));
238  ST(" T*") q += PrintParameter(q, 'D', MapToUser(coderateL, CoderateValues));
239  ST(" T*") q += PrintParameter(q, 'G', MapToUser(guard, GuardValues));
240  ST("ACST*") q += PrintParameter(q, 'I', MapToUser(inversion, InversionValues));
241  ST("ACST*") q += PrintParameter(q, 'M', MapToUser(modulation, ModulationValues));
242  ST(" S 2") q += PrintParameter(q, 'N', MapToUser(pilot, PilotValues));
243  ST(" S 2") q += PrintParameter(q, 'O', MapToUser(rollOff, RollOffValues));
244  ST(" ST2") q += PrintParameter(q, 'P', streamId);
245  ST(" T2") q += PrintParameter(q, 'Q', t2systemId);
246  ST(" ST*") q += PrintParameter(q, 'S', MapToUser(system, SystemValuesSat)); // we only need the numerical value, so Sat or Terr doesn't matter
247  ST(" T*") q += PrintParameter(q, 'T', MapToUser(transmission, TransmissionValues));
248  ST(" T2") q += PrintParameter(q, 'X', sisoMiso);
249  ST(" T*") q += PrintParameter(q, 'Y', MapToUser(hierarchy, HierarchyValues));
250  return buffer;
251 }
252 
253 const char *cDvbTransponderParameters::ParseParameter(const char *s, int &Value, const tDvbParameterMap *Map)
254 {
255  if (*++s) {
256  char *p = NULL;
257  errno = 0;
258  int n = strtol(s, &p, 10);
259  if (!errno && p != s) {
260  Value = Map ? MapToDriver(n, Map) : n;
261  if (Value >= 0)
262  return p;
263  }
264  }
265  esyslog("ERROR: invalid value for parameter '%c'", *(s - 1));
266  return NULL;
267 }
268 
270 {
271  while (s && *s) {
272  switch (toupper(*s)) {
273  case 'A': s = ParseParameter(s, lcn); break;
274  case 'B': s = ParseParameter(s, bandwidth, BandwidthValues); break;
275  case 'C': s = ParseParameter(s, coderateH, CoderateValues); break;
276  case 'D': s = ParseParameter(s, coderateL, CoderateValues); break;
277  case 'G': s = ParseParameter(s, guard, GuardValues); break;
278  case 'H': polarization = 'H'; s++; break;
279  case 'I': s = ParseParameter(s, inversion, InversionValues); break;
280  case 'L': polarization = 'L'; s++; break;
281  case 'M': s = ParseParameter(s, modulation, ModulationValues); break;
282  case 'N': s = ParseParameter(s, pilot, PilotValues); break;
283  case 'O': s = ParseParameter(s, rollOff, RollOffValues); break;
284  case 'P': s = ParseParameter(s, streamId); break;
285  case 'Q': s = ParseParameter(s, t2systemId); break;
286  case 'R': polarization = 'R'; s++; break;
287  case 'S': s = ParseParameter(s, system, SystemValuesSat); break; // we only need the numerical value, so Sat or Terr doesn't matter
288  case 'T': s = ParseParameter(s, transmission, TransmissionValues); break;
289  case 'V': polarization = 'V'; s++; break;
290  case 'X': s = ParseParameter(s, sisoMiso); break;
291  case 'Y': s = ParseParameter(s, hierarchy, HierarchyValues); break;
292  default: esyslog("ERROR: unknown parameter key '%c'", *s);
293  return false;
294  }
295  }
296  return true;
297 }
298 
299 // --- cDvbTuner -------------------------------------------------------------
300 
301 #define TUNER_POLL_TIMEOUT 10 // ms
302 
303 class cDvbTuner : public cThread {
304 private:
306  enum eTunerStatus { tsIdle, tsSet, tsPositioning, tsTuned, tsLocked };
310  int adapter, frontend;
311  uint32_t subsystemId;
320  const cScr *scr;
328  bool SetFrontendType(const cChannel *Channel);
329  cString GetBondingParams(const cChannel *Channel = NULL) const;
330  cDvbTuner *GetBondedMaster(void);
331  bool IsBondedMaster(void) const { return !bondedTuner || bondedMaster; }
332  void ClearEventQueue(void) const;
333  bool GetFrontendStatus(fe_status_t &Status) const;
334  cPositioner *GetPositioner(void);
335  void ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency);
336  void ResetToneAndVoltage(void);
337  bool SetFrontend(void);
338  virtual void Action(void);
339 public:
340  cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend);
341  virtual ~cDvbTuner();
342  int FrontendType(void) const { return frontendType; }
343  bool Bond(cDvbTuner *Tuner);
344  void UnBond(void);
345  bool BondingOk(const cChannel *Channel, bool ConsiderOccupied = false) const;
346  const cChannel *GetTransponder(void) const { return &channel; }
347  uint32_t SubsystemId(void) const { return subsystemId; }
348  bool IsTunedTo(const cChannel *Channel) const;
349  void SetChannel(const cChannel *Channel);
350  bool Locked(int TimeoutMs = 0);
351  const cPositioner *Positioner(void) const { return positioner; }
352  int GetSignalStrength(void) const;
353  int GetSignalQuality(void) const;
354  };
355 
357 
358 cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend)
359 {
360  frontendType = SYS_UNDEFINED;
361  device = Device;
362  fd_frontend = Fd_Frontend;
363  adapter = Adapter;
364  frontend = Frontend;
365  subsystemId = cDvbDeviceProbe::GetSubsystemId(adapter, frontend);
366  tuneTimeout = 0;
367  lockTimeout = 0;
368  lastTimeoutReport = 0;
369  lastDiseqc = NULL;
370  diseqcOffset = 0;
371  lastSource = 0;
372  positioner = NULL;
373  scr = NULL;
374  lnbPowerTurnedOn = false;
375  tunerStatus = tsIdle;
376  bondedTuner = NULL;
377  bondedMaster = false;
378  SetDescription("frontend %d/%d tuner", adapter, frontend);
379  Start();
380 }
381 
383 {
384  tunerStatus = tsIdle;
385  newSet.Broadcast();
386  locked.Broadcast();
387  Cancel(3);
388  UnBond();
389  /* looks like this irritates the SCR switch, so let's leave it out for now
390  if (lastDiseqc && lastDiseqc->IsScr()) {
391  unsigned int Frequency = 0;
392  ExecuteDiseqc(lastDiseqc, &Frequency);
393  }
394  */
395 }
396 
398 {
399  cMutexLock MutexLock(&bondMutex);
400  if (!bondedTuner) {
401  ResetToneAndVoltage();
402  bondedMaster = false; // makes sure we don't disturb an existing master
403  bondedTuner = Tuner->bondedTuner ? Tuner->bondedTuner : Tuner;
404  Tuner->bondedTuner = this;
405  dsyslog("tuner %d/%d bonded with tuner %d/%d", adapter, frontend, bondedTuner->adapter, bondedTuner->frontend);
406  return true;
407  }
408  else
409  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);
410  return false;
411 }
412 
414 {
415  cMutexLock MutexLock(&bondMutex);
416  if (cDvbTuner *t = bondedTuner) {
417  dsyslog("tuner %d/%d unbonded from tuner %d/%d", adapter, frontend, bondedTuner->adapter, bondedTuner->frontend);
418  while (t->bondedTuner != this)
419  t = t->bondedTuner;
420  if (t == bondedTuner)
421  t->bondedTuner = NULL;
422  else
423  t->bondedTuner = bondedTuner;
424  bondedMaster = false; // another one will automatically become master whenever necessary
425  bondedTuner = NULL;
426  }
427 }
428 
430 {
431  if (!Channel)
432  Channel = &channel;
433  cDvbTransponderParameters dtp(Channel->Parameters());
434  if (Setup.DiSEqC) {
435  if (const cDiseqc *diseqc = Diseqcs.Get(device->CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization(), NULL))
436  return diseqc->Commands();
437  }
438  else {
439  bool ToneOff = Channel->Frequency() < Setup.LnbSLOF;
440  bool VoltOff = dtp.Polarization() == 'V' || dtp.Polarization() == 'R';
441  return cString::sprintf("%c %c", ToneOff ? 't' : 'T', VoltOff ? 'v' : 'V');
442  }
443  return "";
444 }
445 
446 bool cDvbTuner::BondingOk(const cChannel *Channel, bool ConsiderOccupied) const
447 {
448  cMutexLock MutexLock(&bondMutex);
449  if (cDvbTuner *t = bondedTuner) {
450  cString BondingParams = GetBondingParams(Channel);
451  do {
452  if (t->device->Priority() > IDLEPRIORITY || ConsiderOccupied && t->device->Occupied()) {
453  if (strcmp(BondingParams, t->GetBondedMaster()->GetBondingParams()) != 0)
454  return false;
455  }
456  t = t->bondedTuner;
457  } while (t != bondedTuner);
458  }
459  return true;
460 }
461 
463 {
464  if (!bondedTuner)
465  return this; // an unbonded tuner is always "master"
466  cMutexLock MutexLock(&bondMutex);
467  if (bondedMaster)
468  return this;
469  // This tuner is bonded, but it's not the master, so let's see if there is a master at all:
470  if (cDvbTuner *t = bondedTuner) {
471  while (t != this) {
472  if (t->bondedMaster)
473  return t;
474  t = t->bondedTuner;
475  }
476  }
477  // None of the other bonded tuners is master, so make this one the master:
478  bondedMaster = true;
479  dsyslog("tuner %d/%d is now bonded master", adapter, frontend);
480  return this;
481 }
482 
483 bool cDvbTuner::IsTunedTo(const cChannel *Channel) const
484 {
485  if (tunerStatus == tsIdle)
486  return false; // not tuned to
487  if (channel.Source() != Channel->Source() || channel.Transponder() != Channel->Transponder())
488  return false; // sufficient mismatch
489  // Polarization is already checked as part of the Transponder.
490  return strcmp(channel.Parameters(), Channel->Parameters()) == 0;
491 }
492 
493 void cDvbTuner::SetChannel(const cChannel *Channel)
494 {
495  if (Channel) {
496  if (bondedTuner) {
497  cMutexLock MutexLock(&bondMutex);
498  cDvbTuner *BondedMaster = GetBondedMaster();
499  if (BondedMaster == this) {
500  if (strcmp(GetBondingParams(Channel), GetBondingParams()) != 0) {
501  // switching to a completely different band, so set all others to idle:
502  for (cDvbTuner *t = bondedTuner; t && t != this; t = t->bondedTuner)
503  t->SetChannel(NULL);
504  }
505  }
506  else if (strcmp(GetBondingParams(Channel), BondedMaster->GetBondingParams()) != 0)
507  BondedMaster->SetChannel(Channel);
508  }
509  cMutexLock MutexLock(&mutex);
510  if (!IsTunedTo(Channel))
511  tunerStatus = tsSet;
512  diseqcOffset = 0;
513  channel = *Channel;
514  lastTimeoutReport = 0;
515  newSet.Broadcast();
516  }
517  else {
518  cMutexLock MutexLock(&mutex);
519  tunerStatus = tsIdle;
520  ResetToneAndVoltage();
521  }
522  if (bondedTuner && device->IsPrimaryDevice())
523  cDevice::PrimaryDevice()->DelLivePids(); // 'device' is const, so we must do it this way
524 }
525 
526 bool cDvbTuner::Locked(int TimeoutMs)
527 {
528  bool isLocked = (tunerStatus >= tsLocked);
529  if (isLocked || !TimeoutMs)
530  return isLocked;
531 
532  cMutexLock MutexLock(&mutex);
533  if (TimeoutMs && tunerStatus < tsLocked)
534  locked.TimedWait(mutex, TimeoutMs);
535  return tunerStatus >= tsLocked;
536 }
537 
539 {
540  cPoller Poller(fd_frontend);
541  if (Poller.Poll(TUNER_POLL_TIMEOUT)) {
542  dvb_frontend_event Event;
543  while (ioctl(fd_frontend, FE_GET_EVENT, &Event) == 0)
544  ; // just to clear the event queue - we'll read the actual status below
545  }
546 }
547 
548 bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
549 {
550  ClearEventQueue();
551  while (1) {
552  if (ioctl(fd_frontend, FE_READ_STATUS, &Status) != -1)
553  return true;
554  if (errno != EINTR)
555  break;
556  }
557  return false;
558 }
559 
560 //#define DEBUG_SIGNALSTRENGTH
561 //#define DEBUG_SIGNALQUALITY
562 
564 {
565  ClearEventQueue();
566  uint16_t Signal;
567  while (1) {
568  if (ioctl(fd_frontend, FE_READ_SIGNAL_STRENGTH, &Signal) != -1)
569  break;
570  if (errno != EINTR)
571  return -1;
572  }
573  uint16_t MaxSignal = 0xFFFF; // Let's assume the default is using the entire range.
574  // Use the subsystemId to identify individual devices in case they need
575  // special treatment to map their Signal value into the range 0...0xFFFF.
576  switch (subsystemId) {
577  case 0x13C21019: // TT-budget S2-3200 (DVB-S/DVB-S2)
578  case 0x1AE40001: // TechniSat SkyStar HD2 (DVB-S/DVB-S2)
579  MaxSignal = 670; break;
580  }
581  int s = int(Signal) * 100 / MaxSignal;
582  if (s > 100)
583  s = 100;
584 #ifdef DEBUG_SIGNALSTRENGTH
585  fprintf(stderr, "FE %d/%d: %08X S = %04X %04X %3d%%\n", adapter, frontend, subsystemId, MaxSignal, Signal, s);
586 #endif
587  return s;
588 }
589 
590 #define LOCK_THRESHOLD 5 // indicates that all 5 FE_HAS_* flags are set
591 
593 {
594  fe_status_t Status;
595  if (GetFrontendStatus(Status)) {
596  // 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...
597  if ((Status & FE_HAS_LOCK) == 0) {
598  if ((Status & FE_HAS_SIGNAL) == 0)
599  return 0;
600  if ((Status & FE_HAS_CARRIER) == 0)
601  return 1;
602  if ((Status & FE_HAS_VITERBI) == 0)
603  return 2;
604  if ((Status & FE_HAS_SYNC) == 0)
605  return 3;
606  return 4;
607  }
608 #ifdef DEBUG_SIGNALQUALITY
609  bool HasSnr = true;
610 #endif
611  uint16_t Snr;
612  while (1) {
613  if (ioctl(fd_frontend, FE_READ_SNR, &Snr) != -1)
614  break;
615  if (errno != EINTR) {
616  Snr = 0xFFFF;
617 #ifdef DEBUG_SIGNALQUALITY
618  HasSnr = false;
619 #endif
620  break;
621  }
622  }
623 #ifdef DEBUG_SIGNALQUALITY
624  bool HasBer = true;
625 #endif
626  uint32_t Ber;
627  while (1) {
628  if (ioctl(fd_frontend, FE_READ_BER, &Ber) != -1)
629  break;
630  if (errno != EINTR) {
631  Ber = 0;
632 #ifdef DEBUG_SIGNALQUALITY
633  HasBer = false;
634 #endif
635  break;
636  }
637  }
638 #ifdef DEBUG_SIGNALQUALITY
639  bool HasUnc = true;
640 #endif
641  uint32_t Unc;
642  while (1) {
643  if (ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &Unc) != -1)
644  break;
645  if (errno != EINTR) {
646  Unc = 0;
647 #ifdef DEBUG_SIGNALQUALITY
648  HasUnc = false;
649 #endif
650  break;
651  }
652  }
653  uint16_t MinSnr = 0x0000;
654  uint16_t MaxSnr = 0xFFFF; // Let's assume the default is using the entire range.
655  // Use the subsystemId to identify individual devices in case they need
656  // special treatment to map their Snr value into the range 0...0xFFFF.
657  switch (subsystemId) {
658  case 0x13C21019: // TT-budget S2-3200 (DVB-S/DVB-S2)
659  case 0x1AE40001: // TechniSat SkyStar HD2 (DVB-S/DVB-S2)
660  if (frontendType == SYS_DVBS2) {
661  MinSnr = 10;
662  MaxSnr = 70;
663  }
664  else
665  MaxSnr = 200;
666  break;
667  case 0x20130245: // PCTV Systems PCTV 73ESE
668  case 0x2013024F: // PCTV Systems nanoStick T2 290e
669  MaxSnr = 255; break;
670  }
671  int a = int(constrain(Snr, MinSnr, MaxSnr)) * 100 / (MaxSnr - MinSnr);
672  int b = 100 - (Unc * 10 + (Ber / 256) * 5);
673  if (b < 0)
674  b = 0;
675  int q = LOCK_THRESHOLD + a * b * (100 - LOCK_THRESHOLD) / 100 / 100;
676  if (q > 100)
677  q = 100;
678 #ifdef DEBUG_SIGNALQUALITY
679  fprintf(stderr, "FE %d/%d: %08X Q = %04X %04X %d %5d %5d %3d%%\n", adapter, frontend, subsystemId, MaxSnr, Snr, HasSnr, HasBer ? int(Ber) : -1, HasUnc ? int(Unc) : -1, q);
680 #endif
681  return q;
682  }
683  return -1;
684 }
685 
686 static unsigned int FrequencyToHz(unsigned int f)
687 {
688  while (f && f < 1000000)
689  f *= 1000;
690  return f;
691 }
692 
694 {
695  if (!positioner) {
696  positioner = cPositioner::GetPositioner();
697  positioner->SetFrontend(fd_frontend);
698  }
699  return positioner;
700 }
701 
702 void cDvbTuner::ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency)
703 {
704  if (!lnbPowerTurnedOn) {
705  CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power
706  lnbPowerTurnedOn = true;
707  }
708  static cMutex Mutex;
709  if (Diseqc->IsScr())
710  Mutex.Lock();
711  struct dvb_diseqc_master_cmd cmd;
712  const char *CurrentAction = NULL;
713  cPositioner *Positioner = NULL;
714  bool Break = false;
715  for (int i = 0; !Break; i++) {
716  cmd.msg_len = sizeof(cmd.msg);
717  cDiseqc::eDiseqcActions da = Diseqc->Execute(&CurrentAction, cmd.msg, &cmd.msg_len, scr, Frequency);
718  if (da == cDiseqc::daNone) {
719  diseqcOffset = 0;
720  break;
721  }
722  bool d = i >= diseqcOffset;
723  switch (da) {
724  case cDiseqc::daToneOff: if (d) CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break;
725  case cDiseqc::daToneOn: if (d) CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break;
726  case cDiseqc::daVoltage13: if (d) CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break;
727  case cDiseqc::daVoltage18: if (d) CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break;
728  case cDiseqc::daMiniA: if (d) CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break;
729  case cDiseqc::daMiniB: if (d) CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break;
730  case cDiseqc::daCodes: if (d) CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd)); break;
731  case cDiseqc::daPositionN: if ((Positioner = GetPositioner()) != NULL) {
732  if (d) {
733  Positioner->GotoPosition(Diseqc->Position(), cSource::Position(channel.Source()));
734  Break = Positioner->IsMoving();
735  }
736  }
737  break;
738  case cDiseqc::daPositionA: if ((Positioner = GetPositioner()) != NULL) {
739  if (d) {
740  Positioner->GotoAngle(cSource::Position(channel.Source()));
741  Break = Positioner->IsMoving();
742  }
743  }
744  break;
745  case cDiseqc::daScr:
746  case cDiseqc::daWait: break;
747  default: esyslog("ERROR: unknown diseqc command %d", da);
748  }
749  if (Break)
750  diseqcOffset = i + 1;
751  }
752  positioner = Positioner;
753  if (scr && !Break)
754  ResetToneAndVoltage(); // makes sure we don't block the bus!
755  if (Diseqc->IsScr())
756  Mutex.Unlock();
757 }
758 
760 {
761  CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, bondedTuner ? SEC_VOLTAGE_OFF : SEC_VOLTAGE_13));
762  CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF));
763 }
764 
765 static int GetRequiredDeliverySystem(const cChannel *Channel, const cDvbTransponderParameters *Dtp)
766 {
767  int ds = SYS_UNDEFINED;
768  if (Channel->IsAtsc())
769  ds = SYS_ATSC;
770  else if (Channel->IsCable())
771  ds = SYS_DVBC_ANNEX_AC;
772  else if (Channel->IsSat())
773  ds = Dtp->System() == DVB_SYSTEM_1 ? SYS_DVBS : SYS_DVBS2;
774  else if (Channel->IsTerr())
775  ds = Dtp->System() == DVB_SYSTEM_1 ? SYS_DVBT : SYS_DVBT2;
776  else
777  esyslog("ERROR: can't determine frontend type for channel %d (%s)", Channel->Number(), Channel->Name());
778  return ds;
779 }
780 
782 {
783 #define MAXFRONTENDCMDS 16
784 #define SETCMD(c, d) { Frontend[CmdSeq.num].cmd = (c);\
785  Frontend[CmdSeq.num].u.data = (d);\
786  if (CmdSeq.num++ > MAXFRONTENDCMDS) {\
787  esyslog("ERROR: too many tuning commands on frontend %d/%d", adapter, frontend);\
788  return false;\
789  }\
790  }
791  dtv_property Frontend[MAXFRONTENDCMDS];
792  memset(&Frontend, 0, sizeof(Frontend));
793  dtv_properties CmdSeq;
794  memset(&CmdSeq, 0, sizeof(CmdSeq));
795  CmdSeq.props = Frontend;
796  SETCMD(DTV_CLEAR, 0);
797  if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
798  esyslog("ERROR: frontend %d/%d: %m", adapter, frontend);
799  return false;
800  }
801  CmdSeq.num = 0;
802 
803  cDvbTransponderParameters dtp(channel.Parameters());
804 
805  // Determine the required frontend type:
806  frontendType = GetRequiredDeliverySystem(&channel, &dtp);
807  if (frontendType == SYS_UNDEFINED)
808  return false;
809 
810  SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
811  if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2) {
812  unsigned int frequency = channel.Frequency();
813  if (Setup.DiSEqC) {
814  if (const cDiseqc *diseqc = Diseqcs.Get(device->CardIndex() + 1, channel.Source(), frequency, dtp.Polarization(), &scr)) {
815  frequency -= diseqc->Lof();
816  if (diseqc != lastDiseqc || diseqc->IsScr() || diseqc->Position() >= 0 && channel.Source() != lastSource) {
817  if (IsBondedMaster()) {
818  ExecuteDiseqc(diseqc, &frequency);
819  if (frequency == 0)
820  return false;
821  }
822  else
823  ResetToneAndVoltage();
824  lastDiseqc = diseqc;
825  lastSource = channel.Source();
826  }
827  }
828  else {
829  esyslog("ERROR: no DiSEqC parameters found for channel %d (%s)", channel.Number(), channel.Name());
830  return false;
831  }
832  }
833  else {
834  int tone = SEC_TONE_OFF;
835  if (frequency < (unsigned int)Setup.LnbSLOF) {
836  frequency -= Setup.LnbFrequLo;
837  tone = SEC_TONE_OFF;
838  }
839  else {
840  frequency -= Setup.LnbFrequHi;
841  tone = SEC_TONE_ON;
842  }
843  int volt = (dtp.Polarization() == 'V' || dtp.Polarization() == 'R') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
844  if (!IsBondedMaster()) {
845  tone = SEC_TONE_OFF;
846  volt = SEC_VOLTAGE_13;
847  }
848  CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
849  CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
850  }
851  frequency = abs(frequency); // Allow for C-band, where the frequency is less than the LOF
852 
853  // DVB-S/DVB-S2 (common parts)
854  SETCMD(DTV_FREQUENCY, frequency * 1000UL);
855  SETCMD(DTV_MODULATION, dtp.Modulation());
856  SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL);
857  SETCMD(DTV_INNER_FEC, dtp.CoderateH());
858  SETCMD(DTV_INVERSION, dtp.Inversion());
859  if (frontendType == SYS_DVBS2) {
860  // DVB-S2
861  SETCMD(DTV_PILOT, dtp.Pilot());
862  SETCMD(DTV_ROLLOFF, dtp.RollOff());
863  if (DvbApiVersion >= 0x0508)
864  SETCMD(DTV_STREAM_ID, dtp.StreamId());
865  }
866  else {
867  // DVB-S
868  SETCMD(DTV_ROLLOFF, ROLLOFF_35); // DVB-S always has a ROLLOFF of 0.35
869  }
870 
871  tuneTimeout = DVBS_TUNE_TIMEOUT;
872  lockTimeout = DVBS_LOCK_TIMEOUT;
873  }
874  else if (frontendType == SYS_DVBC_ANNEX_AC || frontendType == SYS_DVBC_ANNEX_B) {
875  // DVB-C
876  SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
877  SETCMD(DTV_INVERSION, dtp.Inversion());
878  SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL);
879  SETCMD(DTV_INNER_FEC, dtp.CoderateH());
880  SETCMD(DTV_MODULATION, dtp.Modulation());
881 
882  tuneTimeout = DVBC_TUNE_TIMEOUT;
883  lockTimeout = DVBC_LOCK_TIMEOUT;
884  }
885  else if (frontendType == SYS_DVBT || frontendType == SYS_DVBT2) {
886  // DVB-T/DVB-T2 (common parts)
887  SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
888  SETCMD(DTV_INVERSION, dtp.Inversion());
889  SETCMD(DTV_BANDWIDTH_HZ, dtp.Bandwidth());
890  SETCMD(DTV_CODE_RATE_HP, dtp.CoderateH());
891  SETCMD(DTV_CODE_RATE_LP, dtp.CoderateL());
892  SETCMD(DTV_MODULATION, dtp.Modulation());
893  SETCMD(DTV_TRANSMISSION_MODE, dtp.Transmission());
894  SETCMD(DTV_GUARD_INTERVAL, dtp.Guard());
895  SETCMD(DTV_HIERARCHY, dtp.Hierarchy());
896  if (frontendType == SYS_DVBT2) {
897  // DVB-T2
898  if (DvbApiVersion >= 0x0508) {
899  SETCMD(DTV_STREAM_ID, dtp.StreamId());
900  }
901  else if (DvbApiVersion >= 0x0503)
902  SETCMD(DTV_DVBT2_PLP_ID_LEGACY, dtp.StreamId());
903  }
904 
905  tuneTimeout = DVBT_TUNE_TIMEOUT;
906  lockTimeout = DVBT_LOCK_TIMEOUT;
907  }
908  else if (frontendType == SYS_ATSC) {
909  // ATSC
910  SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
911  SETCMD(DTV_INVERSION, dtp.Inversion());
912  SETCMD(DTV_MODULATION, dtp.Modulation());
913 
914  tuneTimeout = ATSC_TUNE_TIMEOUT;
915  lockTimeout = ATSC_LOCK_TIMEOUT;
916  }
917  else {
918  esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
919  return false;
920  }
921  SETCMD(DTV_TUNE, 0);
922  if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
923  esyslog("ERROR: frontend %d/%d: %m", adapter, frontend);
924  return false;
925  }
926  return true;
927 }
928 
930 {
931  cTimeMs Timer;
932  bool LostLock = false;
933  fe_status_t Status = (fe_status_t)0;
934  while (Running()) {
935  fe_status_t NewStatus;
936  if (GetFrontendStatus(NewStatus))
937  Status = NewStatus;
938  cMutexLock MutexLock(&mutex);
939  int WaitTime = 1000;
940  switch (tunerStatus) {
941  case tsIdle:
942  break; // we want the TimedWait() below!
943  case tsSet:
944  tunerStatus = SetFrontend() ? tsPositioning : tsIdle;
945  continue;
946  case tsPositioning:
947  if (positioner) {
948  if (positioner->IsMoving())
949  break; // we want the TimedWait() below!
950  else if (diseqcOffset) {
951  lastDiseqc = NULL;
952  tunerStatus = tsSet; // have it process the rest of the DiSEqC sequence
953  continue;
954  }
955  }
956  tunerStatus = tsTuned;
957  Timer.Set(tuneTimeout + (scr ? rand() % SCR_RANDOM_TIMEOUT : 0));
958  if (positioner)
959  continue;
960  // otherwise run directly into tsTuned...
961  case tsTuned:
962  if (Timer.TimedOut()) {
963  tunerStatus = tsSet;
964  lastDiseqc = NULL;
965  lastSource = 0;
966  if (time(NULL) - lastTimeoutReport > 60) { // let's not get too many of these
967  isyslog("frontend %d/%d timed out while tuning to channel %d (%s), tp %d", adapter, frontend, channel.Number(), channel.Name(), channel.Transponder());
968  lastTimeoutReport = time(NULL);
969  }
970  continue;
971  }
972  WaitTime = 100; // allows for a quick change from tsTuned to tsLocked
973  // run into tsLocked...
974  case tsLocked:
975  if (Status & FE_REINIT) {
976  tunerStatus = tsSet;
977  lastDiseqc = NULL;
978  lastSource = 0;
979  isyslog("frontend %d/%d was reinitialized", adapter, frontend);
980  lastTimeoutReport = 0;
981  continue;
982  }
983  else if (Status & FE_HAS_LOCK) {
984  if (LostLock) {
985  isyslog("frontend %d/%d regained lock on channel %d (%s), tp %d", adapter, frontend, channel.Number(), channel.Name(), channel.Transponder());
986  LostLock = false;
987  }
988  tunerStatus = tsLocked;
989  locked.Broadcast();
990  lastTimeoutReport = 0;
991  }
992  else if (tunerStatus == tsLocked) {
993  LostLock = true;
994  isyslog("frontend %d/%d lost lock on channel %d (%s), tp %d", adapter, frontend, channel.Number(), channel.Name(), channel.Transponder());
995  tunerStatus = tsTuned;
996  Timer.Set(lockTimeout);
997  lastTimeoutReport = 0;
998  continue;
999  }
1000  break;
1001  default: esyslog("ERROR: unknown tuner status %d", tunerStatus);
1002  }
1003  newSet.TimedWait(mutex, WaitTime);
1004  }
1005 }
1006 
1007 // --- cDvbSourceParam -------------------------------------------------------
1008 
1010 private:
1011  int param;
1012  int srate;
1014 public:
1015  cDvbSourceParam(char Source, const char *Description);
1016  virtual void SetData(cChannel *Channel);
1017  virtual void GetData(cChannel *Channel);
1018  virtual cOsdItem *GetOsdItem(void);
1019  };
1020 
1021 cDvbSourceParam::cDvbSourceParam(char Source, const char *Description)
1022 :cSourceParam(Source, Description)
1023 {
1024  param = 0;
1025  srate = 0;
1026 }
1027 
1029 {
1030  srate = Channel->Srate();
1031  dtp.Parse(Channel->Parameters());
1032  param = 0;
1033 }
1034 
1036 {
1037  Channel->SetTransponderData(Channel->Source(), Channel->Frequency(), srate, dtp.ToString(Source()), true);
1038 }
1039 
1041 {
1042  char type = Source();
1043  const tDvbParameterMap *SystemValues = type == 'S' ? SystemValuesSat : SystemValuesTerr;
1044 #undef ST
1045 #define ST(s) if (strchr(s, type))
1046  switch (param++) {
1047  case 0: ST(" S ") return new cMenuEditChrItem( tr("Polarization"), &dtp.polarization, "HVLR"); else return GetOsdItem();
1048  case 1: ST(" ST") return new cMenuEditMapItem( tr("System"), &dtp.system, SystemValues); else return GetOsdItem();
1049  case 2: ST(" CS ") return new cMenuEditIntItem( tr("Srate"), &srate); else return GetOsdItem();
1050  case 3: ST("ACST") return new cMenuEditMapItem( tr("Inversion"), &dtp.inversion, InversionValues); else return GetOsdItem();
1051  case 4: ST(" CST") return new cMenuEditMapItem( tr("CoderateH"), &dtp.coderateH, CoderateValues); else return GetOsdItem();
1052  case 5: ST(" T") return new cMenuEditMapItem( tr("CoderateL"), &dtp.coderateL, CoderateValues); else return GetOsdItem();
1053  case 6: ST("ACST") return new cMenuEditMapItem( tr("Modulation"), &dtp.modulation, ModulationValues); else return GetOsdItem();
1054  case 7: ST(" T") return new cMenuEditMapItem( tr("Bandwidth"), &dtp.bandwidth, BandwidthValues); else return GetOsdItem();
1055  case 8: ST(" T") return new cMenuEditMapItem( tr("Transmission"), &dtp.transmission, TransmissionValues); else return GetOsdItem();
1056  case 9: ST(" T") return new cMenuEditMapItem( tr("Guard"), &dtp.guard, GuardValues); else return GetOsdItem();
1057  case 10: ST(" T") return new cMenuEditMapItem( tr("Hierarchy"), &dtp.hierarchy, HierarchyValues); else return GetOsdItem();
1058  case 11: ST(" S ") return new cMenuEditMapItem( tr("Rolloff"), &dtp.rollOff, RollOffValues); else return GetOsdItem();
1059  case 12: ST(" ST") return new cMenuEditIntItem( tr("StreamId"), &dtp.streamId, 0, 255); else return GetOsdItem();
1060  case 13: ST(" S ") return new cMenuEditMapItem( tr("Pilot"), &dtp.pilot, PilotValues); else return GetOsdItem();
1061  case 14: ST(" T") return new cMenuEditIntItem( tr("T2SystemId"), &dtp.t2systemId, 0, 65535); else return GetOsdItem();
1062  case 15: ST(" T") return new cMenuEditIntItem( tr("SISO/MISO"), &dtp.sisoMiso, 0, 1); else return GetOsdItem();
1063  default: return NULL;
1064  }
1065  return NULL;
1066 }
1067 
1068 // --- cDvbDevice ------------------------------------------------------------
1069 
1072 
1073 const char *DeliverySystemNames[] = {
1074  "",
1075  "DVB-C",
1076  "DVB-C",
1077  "DVB-T",
1078  "DSS",
1079  "DVB-S",
1080  "DVB-S2",
1081  "DVB-H",
1082  "ISDBT",
1083  "ISDBS",
1084  "ISDBC",
1085  "ATSC",
1086  "ATSCMH",
1087  "DMBTH",
1088  "CMMB",
1089  "DAB",
1090  "DVB-T2",
1091  "TURBO",
1092  NULL
1093  };
1094 
1095 cDvbDevice::cDvbDevice(int Adapter, int Frontend)
1096 {
1097  adapter = Adapter;
1098  frontend = Frontend;
1099  ciAdapter = NULL;
1100  dvbTuner = NULL;
1101  numDeliverySystems = 0;
1102  numModulations = 0;
1103  bondedDevice = NULL;
1104  needsDetachBondedReceivers = false;
1105  tsBuffer = NULL;
1106 
1107  // Devices that are present on all card types:
1108 
1109  int fd_frontend = DvbOpen(DEV_DVB_FRONTEND, adapter, frontend, O_RDWR | O_NONBLOCK);
1110 
1111  // Common Interface:
1112 
1113  fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR);
1114  if (fd_ca >= 0)
1115  ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca);
1116 
1117  // The DVR device (will be opened and closed as needed):
1118 
1119  fd_dvr = -1;
1120 
1121  // We only check the devices that must be present - the others will be checked before accessing them://XXX
1122 
1123  if (fd_frontend >= 0) {
1124  if (QueryDeliverySystems(fd_frontend))
1125  dvbTuner = new cDvbTuner(this, fd_frontend, adapter, frontend);
1126  }
1127  else
1128  esyslog("ERROR: can't open DVB device %d/%d", adapter, frontend);
1129 
1130  StartSectionHandler();
1131 }
1132 
1134 {
1135  StopSectionHandler();
1136  delete dvbTuner;
1137  delete ciAdapter;
1138  UnBond();
1139  // We're not explicitly closing any device files here, since this sometimes
1140  // caused segfaults. Besides, the program is about to terminate anyway...
1141 }
1142 
1143 cString cDvbDevice::DvbName(const char *Name, int Adapter, int Frontend)
1144 {
1145  return cString::sprintf("%s/%s%d/%s%d", DEV_DVB_BASE, DEV_DVB_ADAPTER, Adapter, Name, Frontend);
1146 }
1147 
1148 int cDvbDevice::DvbOpen(const char *Name, int Adapter, int Frontend, int Mode, bool ReportError)
1149 {
1150  cString FileName = DvbName(Name, Adapter, Frontend);
1151  int fd = open(FileName, Mode);
1152  if (fd < 0 && ReportError)
1153  LOG_ERROR_STR(*FileName);
1154  return fd;
1155 }
1156 
1157 bool cDvbDevice::Exists(int Adapter, int Frontend)
1158 {
1159  cString FileName = DvbName(DEV_DVB_FRONTEND, Adapter, Frontend);
1160  if (access(FileName, F_OK) == 0) {
1161  int f = open(FileName, O_RDONLY);
1162  if (f >= 0) {
1163  close(f);
1164  return true;
1165  }
1166  else if (errno != ENODEV && errno != EINVAL)
1167  LOG_ERROR_STR(*FileName);
1168  }
1169  else if (errno != ENOENT)
1170  LOG_ERROR_STR(*FileName);
1171  return false;
1172 }
1173 
1174 bool cDvbDevice::Probe(int Adapter, int Frontend)
1175 {
1176  cString FileName = DvbName(DEV_DVB_FRONTEND, Adapter, Frontend);
1177  dsyslog("probing %s", *FileName);
1178  for (cDvbDeviceProbe *dp = DvbDeviceProbes.First(); dp; dp = DvbDeviceProbes.Next(dp)) {
1179  if (dp->Probe(Adapter, Frontend))
1180  return true; // a plugin has created the actual device
1181  }
1182  dsyslog("creating cDvbDevice");
1183  new cDvbDevice(Adapter, Frontend); // it's a "budget" device
1184  return true;
1185 }
1186 
1188 {
1189  if (dvbTuner) {
1190  if (dvbTuner->FrontendType() != SYS_UNDEFINED)
1191  return DeliverySystemNames[dvbTuner->FrontendType()];
1192  if (numDeliverySystems)
1193  return DeliverySystemNames[deliverySystems[0]]; // to have some reasonable default
1194  }
1195  return "";
1196 }
1197 
1199 {
1200  return frontendInfo.name;
1201 }
1202 
1204 {
1205  new cDvbSourceParam('A', "ATSC");
1206  new cDvbSourceParam('C', "DVB-C");
1207  new cDvbSourceParam('S', "DVB-S");
1208  new cDvbSourceParam('T', "DVB-T");
1209  cStringList Nodes;
1210  cReadDir DvbDir(DEV_DVB_BASE);
1211  if (DvbDir.Ok()) {
1212  struct dirent *a;
1213  while ((a = DvbDir.Next()) != NULL) {
1214  if (strstr(a->d_name, DEV_DVB_ADAPTER) == a->d_name) {
1215  int Adapter = strtol(a->d_name + strlen(DEV_DVB_ADAPTER), NULL, 10);
1216  cReadDir AdapterDir(AddDirectory(DEV_DVB_BASE, a->d_name));
1217  if (AdapterDir.Ok()) {
1218  struct dirent *f;
1219  while ((f = AdapterDir.Next()) != NULL) {
1220  if (strstr(f->d_name, DEV_DVB_FRONTEND) == f->d_name) {
1221  int Frontend = strtol(f->d_name + strlen(DEV_DVB_FRONTEND), NULL, 10);
1222  Nodes.Append(strdup(cString::sprintf("%2d %2d", Adapter, Frontend)));
1223  }
1224  }
1225  }
1226  }
1227  }
1228  }
1229  int Found = 0;
1230  int Used = 0;
1231  if (Nodes.Size() > 0) {
1232  Nodes.Sort();
1233  for (int i = 0; i < Nodes.Size(); i++) {
1234  int Adapter;
1235  int Frontend;
1236  if (2 == sscanf(Nodes[i], "%d %d", &Adapter, &Frontend)) {
1237  if (Exists(Adapter, Frontend)) {
1238  if (Found < MAXDEVICES) {
1239  Found++;
1240  if (UseDevice(NextCardIndex())) {
1241  if (Probe(Adapter, Frontend))
1242  Used++;
1243  }
1244  else
1245  NextCardIndex(1); // skips this one
1246  }
1247  }
1248  }
1249  }
1250  }
1251  if (Found > 0) {
1252  isyslog("found %d DVB device%s", Found, Found > 1 ? "s" : "");
1253  if (Used != Found)
1254  isyslog("using only %d DVB device%s", Used, Used > 1 ? "s" : "");
1255  }
1256  else
1257  isyslog("no DVB device found");
1258  return Found > 0;
1259 }
1260 
1262 {
1263  numDeliverySystems = 0;
1264  if (ioctl(fd_frontend, FE_GET_INFO, &frontendInfo) < 0) {
1265  LOG_ERROR;
1266  return false;
1267  }
1268  dtv_property Frontend[1];
1269  dtv_properties CmdSeq;
1270  // Determine the version of the running DVB API:
1271  if (!DvbApiVersion) {
1272  memset(&Frontend, 0, sizeof(Frontend));
1273  memset(&CmdSeq, 0, sizeof(CmdSeq));
1274  CmdSeq.props = Frontend;
1275  SETCMD(DTV_API_VERSION, 0);
1276  if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
1277  LOG_ERROR;
1278  return false;
1279  }
1280  DvbApiVersion = Frontend[0].u.data;
1281  isyslog("DVB API version is 0x%04X (VDR was built with 0x%04X)", DvbApiVersion, DVBAPIVERSION);
1282  }
1283  // Determine the types of delivery systems this device provides:
1284  bool LegacyMode = true;
1285  if (DvbApiVersion >= 0x0505) {
1286  memset(&Frontend, 0, sizeof(Frontend));
1287  memset(&CmdSeq, 0, sizeof(CmdSeq));
1288  CmdSeq.props = Frontend;
1289  SETCMD(DTV_ENUM_DELSYS, 0);
1290  int Result = ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq);
1291  if (Result == 0) {
1292  for (uint i = 0; i < Frontend[0].u.buffer.len; i++) {
1293  if (numDeliverySystems >= MAXDELIVERYSYSTEMS) {
1294  esyslog("ERROR: too many delivery systems on frontend %d/%d", adapter, frontend);
1295  break;
1296  }
1297  deliverySystems[numDeliverySystems++] = Frontend[0].u.buffer.data[i];
1298  }
1299  LegacyMode = false;
1300  }
1301  else {
1302  esyslog("ERROR: can't query delivery systems on frontend %d/%d - falling back to legacy mode", adapter, frontend);
1303  }
1304  }
1305  if (LegacyMode) {
1306  // Legacy mode (DVB-API < 5.5):
1307  switch (frontendInfo.type) {
1308  case FE_QPSK: deliverySystems[numDeliverySystems++] = SYS_DVBS;
1309  if (frontendInfo.caps & FE_CAN_2G_MODULATION)
1310  deliverySystems[numDeliverySystems++] = SYS_DVBS2;
1311  break;
1312  case FE_OFDM: deliverySystems[numDeliverySystems++] = SYS_DVBT;
1313  if (frontendInfo.caps & FE_CAN_2G_MODULATION)
1314  deliverySystems[numDeliverySystems++] = SYS_DVBT2;
1315  break;
1316  case FE_QAM: deliverySystems[numDeliverySystems++] = SYS_DVBC_ANNEX_AC; break;
1317  case FE_ATSC: deliverySystems[numDeliverySystems++] = SYS_ATSC; break;
1318  default: esyslog("ERROR: unknown frontend type %d on frontend %d/%d", frontendInfo.type, adapter, frontend);
1319  }
1320  }
1321  if (numDeliverySystems > 0) {
1322  cString ds("");
1323  for (int i = 0; i < numDeliverySystems; i++)
1324  ds = cString::sprintf("%s%s%s", *ds, i ? "," : "", DeliverySystemNames[deliverySystems[i]]);
1325  cString ms("");
1326  if (frontendInfo.caps & FE_CAN_QPSK) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QPSK, ModulationValues)); }
1327  if (frontendInfo.caps & FE_CAN_QAM_16) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_16, ModulationValues)); }
1328  if (frontendInfo.caps & FE_CAN_QAM_32) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_32, ModulationValues)); }
1329  if (frontendInfo.caps & FE_CAN_QAM_64) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_64, ModulationValues)); }
1330  if (frontendInfo.caps & FE_CAN_QAM_128) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_128, ModulationValues)); }
1331  if (frontendInfo.caps & FE_CAN_QAM_256) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_256, ModulationValues)); }
1332  if (frontendInfo.caps & FE_CAN_8VSB) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(VSB_8, ModulationValues)); }
1333  if (frontendInfo.caps & FE_CAN_16VSB) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(VSB_16, ModulationValues)); }
1334  if (frontendInfo.caps & FE_CAN_TURBO_FEC) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", "TURBO_FEC"); }
1335  if (!**ms)
1336  ms = "unknown modulations";
1337  isyslog("frontend %d/%d provides %s with %s (\"%s\")", adapter, frontend, *ds, *ms, frontendInfo.name);
1338  return true;
1339  }
1340  else
1341  esyslog("ERROR: frontend %d/%d doesn't provide any delivery systems", adapter, frontend);
1342  return false;
1343 }
1344 
1345 bool cDvbDevice::BondDevices(const char *Bondings)
1346 {
1347  UnBondDevices();
1348  if (Bondings) {
1349  cSatCableNumbers SatCableNumbers(MAXDEVICES, Bondings);
1350  for (int i = 0; i < cDevice::NumDevices(); i++) {
1351  int d = SatCableNumbers.FirstDeviceIndex(i);
1352  if (d >= 0) {
1353  int ErrorDevice = 0;
1354  if (cDevice *Device1 = cDevice::GetDevice(i)) {
1355  if (cDevice *Device2 = cDevice::GetDevice(d)) {
1356  if (cDvbDevice *DvbDevice1 = dynamic_cast<cDvbDevice *>(Device1)) {
1357  if (cDvbDevice *DvbDevice2 = dynamic_cast<cDvbDevice *>(Device2)) {
1358  if (!DvbDevice1->Bond(DvbDevice2))
1359  return false; // Bond() has already logged the error
1360  }
1361  else
1362  ErrorDevice = d + 1;
1363  }
1364  else
1365  ErrorDevice = i + 1;
1366  if (ErrorDevice) {
1367  esyslog("ERROR: device '%d' in device bondings '%s' is not a cDvbDevice", ErrorDevice, Bondings);
1368  return false;
1369  }
1370  }
1371  else
1372  ErrorDevice = d + 1;
1373  }
1374  else
1375  ErrorDevice = i + 1;
1376  if (ErrorDevice) {
1377  esyslog("ERROR: unknown device '%d' in device bondings '%s'", ErrorDevice, Bondings);
1378  return false;
1379  }
1380  }
1381  }
1382  }
1383  return true;
1384 }
1385 
1387 {
1388  for (int i = 0; i < cDevice::NumDevices(); i++) {
1389  if (cDvbDevice *d = dynamic_cast<cDvbDevice *>(cDevice::GetDevice(i)))
1390  d->UnBond();
1391  }
1392 }
1393 
1395 {
1396  cMutexLock MutexLock(&bondMutex);
1397  if (!bondedDevice) {
1398  if (Device != this) {
1399  if ((ProvidesDeliverySystem(SYS_DVBS) || ProvidesDeliverySystem(SYS_DVBS2)) && (Device->ProvidesDeliverySystem(SYS_DVBS) || Device->ProvidesDeliverySystem(SYS_DVBS2))) {
1400  if (dvbTuner && Device->dvbTuner && dvbTuner->Bond(Device->dvbTuner)) {
1401  bondedDevice = Device->bondedDevice ? Device->bondedDevice : Device;
1402  Device->bondedDevice = this;
1403  dsyslog("device %d bonded with device %d", CardIndex() + 1, bondedDevice->CardIndex() + 1);
1404  return true;
1405  }
1406  }
1407  else
1408  esyslog("ERROR: can't bond device %d with device %d (only DVB-S(2) devices can be bonded)", CardIndex() + 1, Device->CardIndex() + 1);
1409  }
1410  else
1411  esyslog("ERROR: can't bond device %d with itself", CardIndex() + 1);
1412  }
1413  else
1414  esyslog("ERROR: device %d already bonded with device %d, can't bond with device %d", CardIndex() + 1, bondedDevice->CardIndex() + 1, Device->CardIndex() + 1);
1415  return false;
1416 }
1417 
1419 {
1420  cMutexLock MutexLock(&bondMutex);
1421  if (cDvbDevice *d = bondedDevice) {
1422  if (dvbTuner)
1423  dvbTuner->UnBond();
1424  dsyslog("device %d unbonded from device %d", CardIndex() + 1, bondedDevice->CardIndex() + 1);
1425  while (d->bondedDevice != this)
1426  d = d->bondedDevice;
1427  if (d == bondedDevice)
1428  d->bondedDevice = NULL;
1429  else
1430  d->bondedDevice = bondedDevice;
1431  bondedDevice = NULL;
1432  }
1433 }
1434 
1435 bool cDvbDevice::BondingOk(const cChannel *Channel, bool ConsiderOccupied) const
1436 {
1437  cMutexLock MutexLock(&bondMutex);
1438  if (bondedDevice || Positioner())
1439  return dvbTuner && dvbTuner->BondingOk(Channel, ConsiderOccupied);
1440  return true;
1441 }
1442 
1444 {
1445  return ciAdapter;
1446 }
1447 
1448 bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
1449 {
1450  if (Handle->pid) {
1451  dmx_pes_filter_params pesFilterParams;
1452  memset(&pesFilterParams, 0, sizeof(pesFilterParams));
1453  if (On) {
1454  if (Handle->handle < 0) {
1455  Handle->handle = DvbOpen(DEV_DVB_DEMUX, adapter, frontend, O_RDWR | O_NONBLOCK, true);
1456  if (Handle->handle < 0) {
1457  LOG_ERROR;
1458  return false;
1459  }
1460  }
1461  pesFilterParams.pid = Handle->pid;
1462  pesFilterParams.input = DMX_IN_FRONTEND;
1463  pesFilterParams.output = DMX_OUT_TS_TAP;
1464  pesFilterParams.pes_type= DMX_PES_OTHER;
1465  pesFilterParams.flags = DMX_IMMEDIATE_START;
1466  if (ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
1467  LOG_ERROR;
1468  return false;
1469  }
1470  }
1471  else if (!Handle->used) {
1472  CHECK(ioctl(Handle->handle, DMX_STOP));
1473  if (Type <= ptTeletext) {
1474  pesFilterParams.pid = 0x1FFF;
1475  pesFilterParams.input = DMX_IN_FRONTEND;
1476  pesFilterParams.output = DMX_OUT_DECODER;
1477  pesFilterParams.pes_type= DMX_PES_OTHER;
1478  pesFilterParams.flags = DMX_IMMEDIATE_START;
1479  CHECK(ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams));
1480  }
1481  close(Handle->handle);
1482  Handle->handle = -1;
1483  }
1484  }
1485  return true;
1486 }
1487 
1488 int cDvbDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask)
1489 {
1490  cString FileName = DvbName(DEV_DVB_DEMUX, adapter, frontend);
1491  int f = open(FileName, O_RDWR | O_NONBLOCK);
1492  if (f >= 0) {
1493  dmx_sct_filter_params sctFilterParams;
1494  memset(&sctFilterParams, 0, sizeof(sctFilterParams));
1495  sctFilterParams.pid = Pid;
1496  sctFilterParams.timeout = 0;
1497  sctFilterParams.flags = DMX_IMMEDIATE_START;
1498  sctFilterParams.filter.filter[0] = Tid;
1499  sctFilterParams.filter.mask[0] = Mask;
1500  if (ioctl(f, DMX_SET_FILTER, &sctFilterParams) >= 0)
1501  return f;
1502  else {
1503  esyslog("ERROR: can't set filter (pid=%d, tid=%02X, mask=%02X): %m", Pid, Tid, Mask);
1504  close(f);
1505  }
1506  }
1507  else
1508  esyslog("ERROR: can't open filter handle on '%s'", *FileName);
1509  return -1;
1510 }
1511 
1512 void cDvbDevice::CloseFilter(int Handle)
1513 {
1514  close(Handle);
1515 }
1516 
1517 bool cDvbDevice::ProvidesDeliverySystem(int DeliverySystem) const
1518 {
1519  for (int i = 0; i < numDeliverySystems; i++) {
1520  if (deliverySystems[i] == DeliverySystem)
1521  return true;
1522  }
1523  return false;
1524 }
1525 
1527 {
1528  int type = Source & cSource::st_Mask;
1529  return type == cSource::stNone
1530  || type == cSource::stAtsc && ProvidesDeliverySystem(SYS_ATSC)
1531  || type == cSource::stCable && (ProvidesDeliverySystem(SYS_DVBC_ANNEX_AC) || ProvidesDeliverySystem(SYS_DVBC_ANNEX_B))
1532  || type == cSource::stSat && (ProvidesDeliverySystem(SYS_DVBS) || ProvidesDeliverySystem(SYS_DVBS2))
1533  || type == cSource::stTerr && (ProvidesDeliverySystem(SYS_DVBT) || ProvidesDeliverySystem(SYS_DVBT2));
1534 }
1535 
1536 bool cDvbDevice::ProvidesTransponder(const cChannel *Channel) const
1537 {
1538  if (!ProvidesSource(Channel->Source()))
1539  return false; // doesn't provide source
1541  if (!ProvidesDeliverySystem(GetRequiredDeliverySystem(Channel, &dtp)) ||
1542  dtp.StreamId() != 0 && !(frontendInfo.caps & FE_CAN_MULTISTREAM) ||
1543  dtp.Modulation() == QPSK && !(frontendInfo.caps & FE_CAN_QPSK) ||
1544  dtp.Modulation() == QAM_16 && !(frontendInfo.caps & FE_CAN_QAM_16) ||
1545  dtp.Modulation() == QAM_32 && !(frontendInfo.caps & FE_CAN_QAM_32) ||
1546  dtp.Modulation() == QAM_64 && !(frontendInfo.caps & FE_CAN_QAM_64) ||
1547  dtp.Modulation() == QAM_128 && !(frontendInfo.caps & FE_CAN_QAM_128) ||
1548  dtp.Modulation() == QAM_256 && !(frontendInfo.caps & FE_CAN_QAM_256) ||
1549  dtp.Modulation() == QAM_AUTO && !(frontendInfo.caps & FE_CAN_QAM_AUTO) ||
1550  dtp.Modulation() == VSB_8 && !(frontendInfo.caps & FE_CAN_8VSB) ||
1551  dtp.Modulation() == VSB_16 && !(frontendInfo.caps & FE_CAN_16VSB) ||
1552  dtp.Modulation() == PSK_8 && !(frontendInfo.caps & FE_CAN_TURBO_FEC) && dtp.System() == SYS_DVBS) // "turbo fec" is a non standard FEC used by North American broadcasters - this is a best guess to determine this condition
1553  return false; // requires modulation system which frontend doesn't provide
1554  if (!cSource::IsSat(Channel->Source()) ||
1555  (!Setup.DiSEqC || Diseqcs.Get(CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization(), NULL)))
1556  return DeviceHooksProvidesTransponder(Channel);
1557  return false;
1558 }
1559 
1560 bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
1561 {
1562  bool result = false;
1563  bool hasPriority = Priority == IDLEPRIORITY || Priority > this->Priority();
1564  bool needsDetachReceivers = false;
1565  needsDetachBondedReceivers = false;
1566 
1567  if (dvbTuner && ProvidesTransponder(Channel)) {
1568  result = hasPriority;
1569  if (Priority > IDLEPRIORITY) {
1570  if (Receiving()) {
1571  if (dvbTuner->IsTunedTo(Channel)) {
1572  if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0)) || Channel->Dpid(0) && !HasPid(Channel->Dpid(0))) {
1573  if (CamSlot() && Channel->Ca() >= CA_ENCRYPTED_MIN) {
1574  if (CamSlot()->CanDecrypt(Channel))
1575  result = true;
1576  else
1577  needsDetachReceivers = true;
1578  }
1579  else
1580  result = true;
1581  }
1582  else
1583  result = true;
1584  }
1585  else
1586  needsDetachReceivers = Receiving();
1587  }
1588  if (result) {
1589  cMutexLock MutexLock(&bondMutex);
1590  if (!BondingOk(Channel)) {
1591  // This device is bonded, so we need to check the priorities of the others:
1592  for (cDvbDevice *d = bondedDevice; d && d != this; d = d->bondedDevice) {
1593  if (d->Priority() >= Priority) {
1594  result = false;
1595  break;
1596  }
1597  needsDetachReceivers |= d->Receiving();
1598  }
1599  needsDetachBondedReceivers = true;
1600  needsDetachReceivers |= Receiving();
1601  }
1602  }
1603  }
1604  }
1605  if (NeedsDetachReceivers)
1606  *NeedsDetachReceivers = needsDetachReceivers;
1607  return result;
1608 }
1609 
1610 bool cDvbDevice::ProvidesEIT(void) const
1611 {
1612  return dvbTuner != NULL;
1613 }
1614 
1616 {
1617  return numDeliverySystems + numModulations;
1618 }
1619 
1621 {
1622  return dvbTuner ? dvbTuner->Positioner() : NULL;
1623 }
1624 
1626 {
1627  return dvbTuner ? dvbTuner->GetSignalStrength() : -1;
1628 }
1629 
1631 {
1632  return dvbTuner ? dvbTuner->GetSignalQuality() : -1;
1633 }
1634 
1636 {
1637  return dvbTuner ? dvbTuner->GetTransponder() : NULL;
1638 }
1639 
1640 bool cDvbDevice::IsTunedToTransponder(const cChannel *Channel) const
1641 {
1642  return dvbTuner ? dvbTuner->IsTunedTo(Channel) : false;
1643 }
1644 
1645 bool cDvbDevice::MaySwitchTransponder(const cChannel *Channel) const
1646 {
1647  return BondingOk(Channel, true) && cDevice::MaySwitchTransponder(Channel);
1648 }
1649 
1650 bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
1651 {
1652  if (dvbTuner)
1653  dvbTuner->SetChannel(Channel);
1654  return true;
1655 }
1656 
1657 bool cDvbDevice::HasLock(int TimeoutMs) const
1658 {
1659  return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false;
1660 }
1661 
1663 {
1664  setTransferModeForDolbyDigital = Mode;
1665 }
1666 
1668 {
1669  CloseDvr();
1670  fd_dvr = DvbOpen(DEV_DVB_DVR, adapter, frontend, O_RDONLY | O_NONBLOCK, true);
1671  if (fd_dvr >= 0)
1672  tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1);
1673  return fd_dvr >= 0;
1674 }
1675 
1677 {
1678  if (fd_dvr >= 0) {
1679  delete tsBuffer;
1680  tsBuffer = NULL;
1681  close(fd_dvr);
1682  fd_dvr = -1;
1683  }
1684 }
1685 
1687 {
1688  if (tsBuffer) {
1689  if (cCamSlot *cs = CamSlot()) {
1690  if (cs->WantsTsData()) {
1691  int Available;
1692  Data = tsBuffer->Get(&Available);
1693  if (Data) {
1694  Data = cs->Decrypt(Data, Available);
1695  tsBuffer->Skip(Available);
1696  }
1697  return true;
1698  }
1699  }
1700  Data = tsBuffer->Get();
1701  return true;
1702  }
1703  return false;
1704 }
1705 
1707 {
1708  cMutexLock MutexLock(&bondMutex);
1709  cDvbDevice *d = this;
1710  do {
1711  d->cDevice::DetachAllReceivers();
1712  d = d->bondedDevice;
1713  } while (d && d != this && needsDetachBondedReceivers);
1714  needsDetachBondedReceivers = false;
1715 }
1716 
1717 // --- cDvbDeviceProbe -------------------------------------------------------
1718 
1720 
1722 {
1723  DvbDeviceProbes.Add(this);
1724 }
1725 
1727 {
1728  DvbDeviceProbes.Del(this, false);
1729 }
1730 
1731 uint32_t cDvbDeviceProbe::GetSubsystemId(int Adapter, int Frontend)
1732 {
1733  uint32_t SubsystemId = 0;
1734  cString FileName = cString::sprintf("/dev/dvb/adapter%d/frontend%d", Adapter, Frontend);
1735  struct stat st;
1736  if (stat(FileName, &st) == 0) {
1737  cReadDir d("/sys/class/dvb");
1738  if (d.Ok()) {
1739  struct dirent *e;
1740  while ((e = d.Next()) != NULL) {
1741  if (strstr(e->d_name, "frontend")) {
1742  FileName = cString::sprintf("/sys/class/dvb/%s/dev", e->d_name);
1743  if (FILE *f = fopen(FileName, "r")) {
1744  cReadLine ReadLine;
1745  char *s = ReadLine.Read(f);
1746  fclose(f);
1747  unsigned Major;
1748  unsigned Minor;
1749  if (s && 2 == sscanf(s, "%u:%u", &Major, &Minor)) {
1750  if (((Major << 8) | Minor) == st.st_rdev) {
1751  FileName = cString::sprintf("/sys/class/dvb/%s/device/subsystem_vendor", e->d_name);
1752  if ((f = fopen(FileName, "r")) != NULL) {
1753  if (char *s = ReadLine.Read(f))
1754  SubsystemId = strtoul(s, NULL, 0) << 16;
1755  fclose(f);
1756  }
1757  else {
1758  FileName = cString::sprintf("/sys/class/dvb/%s/device/idVendor", e->d_name);
1759  if ((f = fopen(FileName, "r")) != NULL) {
1760  if (char *s = ReadLine.Read(f))
1761  SubsystemId = strtoul(s, NULL, 16) << 16;
1762  fclose(f);
1763  }
1764  }
1765  FileName = cString::sprintf("/sys/class/dvb/%s/device/subsystem_device", e->d_name);
1766  if ((f = fopen(FileName, "r")) != NULL) {
1767  if (char *s = ReadLine.Read(f))
1768  SubsystemId |= strtoul(s, NULL, 0);
1769  fclose(f);
1770  }
1771  else {
1772  FileName = cString::sprintf("/sys/class/dvb/%s/device/idProduct", e->d_name);
1773  if ((f = fopen(FileName, "r")) != NULL) {
1774  if (char *s = ReadLine.Read(f))
1775  SubsystemId |= strtoul(s, NULL, 16);
1776  fclose(f);
1777  }
1778  }
1779  break;
1780  }
1781  }
1782  }
1783  }
1784  }
1785  }
1786  }
1787  return SubsystemId;
1788 }
static unsigned int FrequencyToHz(unsigned int f)
Definition: dvbdevice.c:686
#define SETCMD(c, d)
#define DVB_SYSTEM_1
Definition: dvbdevice.c:96
struct dirent * Next(void)
Definition: tools.c:1466
int lastSource
Definition: dvbdevice.c:318
virtual ~cDvbDeviceProbe()
Definition: dvbdevice.c:1726
cDiseqcs Diseqcs
Definition: diseqc.c:439
const char * DeliverySystemNames[]
Definition: dvbdevice.c:1073
virtual ~cDvbTuner()
Definition: dvbdevice.c:382
cDvbTransponderParameters(const char *Parameters=NULL)
Definition: dvbdevice.c:202
unsigned char uchar
Definition: tools.h:30
virtual ~cDvbDevice()
Definition: dvbdevice.c:1133
void Lock(void)
Definition: thread.c:191
virtual bool ProvidesSource(int Source) const
Returns true if this device can provide the given source.
Definition: dvbdevice.c:1526
int PrintParameter(char *p, char Name, int Value) const
Definition: dvbdevice.c:223
virtual cString DeviceType(void) const
Returns a string identifying the type of this device (like "DVB-S").
Definition: dvbdevice.c:1187
bool IsBondedMaster(void) const
Definition: dvbdevice.c:331
int Vpid(void) const
Definition: channels.h:170
#define DEV_DVB_BASE
Definition: dvbdevice.h:73
static bool Exists(int Adapter, int Frontend)
Checks whether the given adapter/frontend exists.
Definition: dvbdevice.c:1157
int Number(void) const
Definition: channels.h:197
bool IsScr(void) const
Returns true if this DiSEqC sequence uses Satellite Channel Routing.
Definition: diseqc.h:132
cPositioner * positioner
Definition: dvbdevice.c:319
void ResetToneAndVoltage(void)
Definition: dvbdevice.c:759
#define SCR_RANDOM_TIMEOUT
Definition: dvbdevice.c:35
#define dsyslog(a...)
Definition: tools.h:36
cString AddDirectory(const char *DirName, const char *FileName)
Definition: tools.c:350
#define CA_ENCRYPTED_MIN
Definition: channels.h:49
friend class cDvbSourceParam
Definition: dvbdevice.h:108
void Set(int Ms=0)
Definition: tools.c:738
const char * ParseParameter(const char *s, int &Value, const tDvbParameterMap *Map=NULL)
Definition: dvbdevice.c:253
virtual bool IsTunedToTransponder(const cChannel *Channel) const
Returns true if this device is currently tuned to the given Channel&#39;s transponder.
Definition: dvbdevice.c:1640
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
static bool Initialize(void)
Initializes the DVB devices.
Definition: dvbdevice.c:1203
virtual bool GetTSPacket(uchar *&Data)
Gets exactly one TS packet from the DVR of this device and returns a pointer to it in Data...
Definition: dvbdevice.c:1686
#define LOG_ERROR
Definition: tools.h:38
cDvbTuner * dvbTuner
Definition: dvbdevice.h:239
#define DVBT_TUNE_TIMEOUT
Definition: dvbdevice.c:30
int UserIndex(int Value, const tDvbParameterMap *Map)
Definition: dvbdevice.c:151
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2014
virtual const cPositioner * Positioner(void) const
Returns a pointer to the positioner (if any) this device has used to move the satellite dish to the r...
Definition: dvbdevice.c:1620
int fd_frontend
Definition: dvbdevice.c:309
void UnBond(void)
Removes this device from any bonding it might have with other devices.
Definition: dvbdevice.c:1418
#define DVBC_TUNE_TIMEOUT
Definition: dvbdevice.c:28
int Ca(int Index=0) const
Definition: channels.h:191
void ClearEventQueue(void) const
Definition: dvbdevice.c:538
int Dpid(int i) const
Definition: channels.h:177
virtual int SignalQuality(void) const
Returns the "quality" of the currently received signal.
Definition: dvbdevice.c:1630
virtual cOsdItem * GetOsdItem(void)
Returns all the OSD items necessary for editing the source specific parameters of the channel that wa...
Definition: dvbdevice.c:1040
#define DVBS_LOCK_TIMEOUT
Definition: dvbdevice.c:27
virtual void GotoPosition(uint Number, int Longitude)
Move the dish to the satellite position stored under the given Number.
Definition: positioner.c:100
void ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency)
Definition: dvbdevice.c:702
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1080
#define DVBC_LOCK_TIMEOUT
Definition: dvbdevice.c:29
const tDvbParameterMap SystemValuesSat[]
Definition: dvbdevice.c:99
virtual void Append(T Data)
Definition: tools.h:571
#define DVBT_LOCK_TIMEOUT
Definition: dvbdevice.c:31
cDvbDeviceProbe(void)
Definition: dvbdevice.c:1721
static uint32_t GetSubsystemId(int Adapter, int Frontend)
Definition: dvbdevice.c:1731
const tDvbParameterMap InversionValues[]
Definition: dvbdevice.c:46
#define MAXDEVICES
Definition: device.h:29
#define esyslog(a...)
Definition: tools.h:34
int Srate(void) const
Definition: channels.h:169
bool Parse(const char *s)
Definition: dvbdevice.c:269
int frontend
Definition: dvbdevice.c:310
static cDevice * GetDevice(int Index)
Gets the device with the given Index.
Definition: device.c:223
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView)
Sets the device to the given channel (actual physical setup).
Definition: dvbdevice.c:1650
#define LOG_ERROR_STR(s)
Definition: tools.h:39
eDiseqcActions Execute(const char **CurrentAction, uchar *Codes, uint8_t *MaxCodes, const cScr *Scr, uint *Frequency) const
Parses the DiSEqC commands and returns the appropriate action code with every call.
Definition: diseqc.c:402
Definition: tools.h:489
int frontendType
Definition: dvbdevice.c:307
static int NumDevices(void)
Returns the total number of devices.
Definition: device.h:118
#define DEV_DVB_ADAPTER
Definition: dvbdevice.h:74
bool IsTunedTo(const cChannel *Channel) const
Definition: dvbdevice.c:483
#define TUNER_POLL_TIMEOUT
Definition: dvbdevice.c:301
void DelLivePids(void)
Deletes the live viewing PIDs.
Definition: device.c:560
int GetSignalStrength(void) const
Definition: dvbdevice.c:563
int Transponder(void) const
Returns the transponder frequency in MHz, plus the polarization in case of sat.
Definition: channels.c:158
cDvbTuner * GetBondedMaster(void)
Definition: dvbdevice.c:462
cString ToString(char Type) const
Definition: dvbdevice.c:228
bool Poll(int TimeoutMs=0)
Definition: tools.c:1443
uint32_t subsystemId
Definition: dvbdevice.c:311
int MapToDriver(int Value, const tDvbParameterMap *Map)
Definition: dvbdevice.c:192
int adapter
Definition: dvbdevice.c:310
char * Read(FILE *f)
Definition: tools.c:1398
bool QueryDeliverySystems(int fd_frontend)
Definition: dvbdevice.c:1261
cPositioner * GetPositioner(void)
Definition: dvbdevice.c:693
Definition: diseqc.h:62
const char * Parameters(void) const
Definition: channels.h:200
bool SetTransponderData(int Source, int Frequency, int Srate, const char *Parameters, bool Quiet=false)
Definition: channels.c:197
static cPositioner * GetPositioner(void)
Returns a previously created positioner.
Definition: positioner.c:133
int LnbFrequLo
Definition: config.h:272
A steerable satellite dish generally points to the south on the northern hemisphere, and to the north on the southern hemisphere (unless you&#39;re located directly on the equator, in which case the general direction is "up").
Definition: positioner.h:31
cMutex mutex
Definition: dvbdevice.c:323
static int DvbApiVersion
Definition: dvbdevice.c:24
virtual void GotoAngle(int Longitude)
Move the dish to the given angular position.
Definition: positioner.c:107
cCondVar newSet
Definition: dvbdevice.c:325
int Modulation(void) const
Definition: dvbdevice.h:135
virtual bool MaySwitchTransponder(const cChannel *Channel) const
Returns true if it is ok to switch to the Channel&#39;s transponder on this device, without disturbing an...
Definition: device.c:692
const tDvbParameterMap HierarchyValues[]
Definition: dvbdevice.c:134
#define IDLEPRIORITY
Definition: config.h:43
const char * Name(void) const
Definition: channels.c:123
char Polarization(void) const
Definition: dvbdevice.h:130
int Source(void) const
Definition: channels.h:168
static bool BondDevices(const char *Bondings)
Bonds the devices as defined in the given Bondings string.
Definition: dvbdevice.c:1345
#define trNOOP(s)
Definition: i18n.h:88
#define CHECK(s)
Definition: tools.h:50
cChannel channel
Definition: dvbdevice.c:315
static bool Probe(int Adapter, int Frontend)
Probes for existing DVB devices.
Definition: dvbdevice.c:1174
bool bondedMaster
Definition: dvbdevice.c:327
T constrain(T v, T l, T h)
Definition: tools.h:60
virtual bool SetPid(cPidHandle *Handle, int Type, bool On)
Does the actual PID setting on this device.
Definition: dvbdevice.c:1448
uint32_t SubsystemId(void) const
Definition: dvbdevice.c:347
const cScr * scr
Definition: dvbdevice.c:320
#define MAXDELIVERYSYSTEMS
Definition: dvbdevice.h:70
static cMutex bondMutex
Definition: dvbdevice.c:305
virtual void SetData(cChannel *Channel)
Sets all source specific parameters to those of the given Channel.
Definition: dvbdevice.c:1028
cDvbDevice * bondedDevice
Definition: dvbdevice.h:193
cDvbSourceParam(char Source, const char *Description)
Definition: dvbdevice.c:1021
int LnbSLOF
Definition: config.h:271
int MapToUser(int Value, const tDvbParameterMap *Map, const char **String)
Definition: dvbdevice.c:173
bool TimedOut(void) const
Definition: tools.c:743
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
#define DVBAPIVERSION
Definition: dvbdevice.h:17
static cDvbCiAdapter * CreateCiAdapter(cDevice *Device, int Fd)
Definition: dvbci.c:102
cList< cDvbDeviceProbe > DvbDeviceProbes
Definition: dvbdevice.c:1719
static cMutex bondMutex
Definition: dvbdevice.h:192
virtual cString DeviceName(void) const
Returns a string identifying the name of this device.
Definition: dvbdevice.c:1198
virtual void CloseDvr(void)
Shuts down the DVR.
Definition: dvbdevice.c:1676
#define DEV_DVB_FRONTEND
Definition: dvbdevice.h:76
#define DVBS_TUNE_TIMEOUT
Definition: dvbdevice.c:26
int StreamId(void) const
Definition: dvbdevice.h:141
virtual void CloseFilter(int Handle)
Closes a file handle that has previously been opened by OpenFilter().
Definition: dvbdevice.c:1512
static bool IsSat(int Code)
Definition: sources.h:57
bool Ok(void)
Definition: tools.h:379
#define DEV_DVB_CA
Definition: dvbdevice.h:81
cSetup Setup
Definition: config.c:373
int DriverIndex(int Value, const tDvbParameterMap *Map)
Definition: dvbdevice.c:162
#define MAXFRONTENDCMDS
void UnBond(void)
Definition: dvbdevice.c:413
Definition: ci.h:128
#define ATSC_LOCK_TIMEOUT
Definition: dvbdevice.c:33
const tDvbParameterMap ModulationValues[]
Definition: dvbdevice.c:79
bool lnbPowerTurnedOn
Definition: dvbdevice.c:321
Definition: thread.h:63
bool Locked(int TimeoutMs=0)
Definition: dvbdevice.c:526
int Frequency(void) const
Returns the actual frequency, as given in &#39;channels.conf&#39;.
Definition: channels.h:165
#define DVB_SYSTEM_2
Definition: dvbdevice.c:97
int Size(void) const
Definition: tools.h:551
virtual bool OpenDvr(void)
Opens the DVR of this device and prepares it to deliver a Transport Stream for use in a cReceiver...
Definition: dvbdevice.c:1667
int LnbFrequHi
Definition: config.h:273
#define DEV_DVB_DVR
Definition: dvbdevice.h:77
int GetSignalQuality(void) const
Definition: dvbdevice.c:592
int diseqcOffset
Definition: dvbdevice.c:317
bool Bond(cDvbTuner *Tuner)
Definition: dvbdevice.c:397
virtual void DetachAllReceivers(void)
Detaches all receivers from this device.
Definition: dvbdevice.c:1706
static void UnBondDevices(void)
Unbonds all devices.
Definition: dvbdevice.c:1386
#define DTV_ENUM_DELSYS
Definition: dvbdevice.h:57
const char * MapToUserString(int Value, const tDvbParameterMap *Map)
Definition: dvbdevice.c:184
virtual const cChannel * GetCurrentlyTunedTransponder(void) const
Returns a pointer to the currently tuned transponder.
Definition: dvbdevice.c:1635
virtual bool HasCi(void)
Returns true if this device has a Common Interface.
Definition: dvbdevice.c:1443
int CardIndex(void) const
Returns the card index of this device (0 ... MAXDEVICES - 1).
Definition: device.h:205
const tDvbParameterMap PilotValues[]
Definition: dvbdevice.c:39
virtual bool HasLock(int TimeoutMs=0) const
Returns true if the device has a lock on the requested transponder.
Definition: dvbdevice.c:1657
virtual int NumProvidedSystems(void) const
Returns the number of individual "delivery systems" this device provides.
Definition: dvbdevice.c:1615
int FrontendType(void) const
Definition: dvbdevice.c:342
bool IsSat(void) const
Definition: channels.h:205
#define MEGABYTE(n)
Definition: tools.h:44
char Source(void) const
Definition: sourceparams.h:31
eTunerStatus tunerStatus
Definition: dvbdevice.c:322
const cDiseqc * lastDiseqc
Definition: dvbdevice.c:316
static int GetRequiredDeliverySystem(const cChannel *Channel, const cDvbTransponderParameters *Dtp)
Definition: dvbdevice.c:765
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2046
cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend)
Definition: dvbdevice.c:358
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
virtual bool ProvidesDeliverySystem(int DeliverySystem) const
Definition: dvbdevice.c:1517
const tDvbParameterMap RollOffValues[]
Definition: dvbdevice.c:143
bool IsTerr(void) const
Definition: channels.h:206
static cDevice * PrimaryDevice(void)
Returns the primary device.
Definition: device.h:137
static int setTransferModeForDolbyDigital
Definition: dvbdevice.h:277
Definition: diseqc.h:34
const tDvbParameterMap CoderateValues[]
Definition: dvbdevice.c:63
int Apid(int i) const
Definition: channels.h:176
#define tr(s)
Definition: i18n.h:85
unsigned char u_char
Definition: headers.h:24
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:1394
const cPositioner * Positioner(void) const
Definition: dvbdevice.c:351
#define DEV_DVB_DEMUX
Definition: dvbdevice.h:78
const cChannel * GetTransponder(void) const
Definition: dvbdevice.c:346
virtual bool ProvidesTransponder(const cChannel *Channel) const
Returns true if this device can provide the transponder of the given Channel (which implies that it c...
Definition: dvbdevice.c:1536
#define ST(s)
#define isyslog(a...)
Definition: tools.h:35
eDiseqcActions
Definition: diseqc.h:64
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
Definition: thread.h:77
virtual bool ProvidesChannel(const cChannel *Channel, int Priority=IDLEPRIORITY, bool *NeedsDetachReceivers=NULL) const
Returns true if this device can provide the given channel.
Definition: dvbdevice.c:1560
bool SetFrontend(void)
Definition: dvbdevice.c:781
void Sort(bool IgnoreCase=false)
Definition: tools.h:629
#define DTV_STREAM_ID
Definition: dvbdevice.h:64
bool BondingOk(const cChannel *Channel, bool ConsiderOccupied=false) const
Definition: dvbdevice.c:446
The cDvbDevice implements a DVB device which can be accessed through the Linux DVB driver API...
Definition: dvbdevice.h:170
void SetChannel(const cChannel *Channel)
Definition: dvbdevice.c:493
Definition: tools.h:333
int Position(void)
Returns the orbital position of the satellite in case this is a DVB-S source (zero otherwise)...
Definition: sources.h:35
#define LOCK_THRESHOLD
Definition: dvbdevice.c:590
bool GetFrontendStatus(fe_status_t &Status) const
Definition: dvbdevice.c:548
#define DTV_DVBT2_PLP_ID_LEGACY
Definition: dvbdevice.h:65
const tDvbParameterMap SystemValuesTerr[]
Definition: dvbdevice.c:105
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: dvbdevice.c:929
const tDvbParameterMap BandwidthValues[]
Definition: dvbdevice.c:53
int lockTimeout
Definition: dvbdevice.c:313
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:1435
bool IsCable(void) const
Definition: channels.h:204
int System(void) const
Definition: dvbdevice.h:136
int tuneTimeout
Definition: dvbdevice.c:312
cCondVar locked
Definition: dvbdevice.c:324
Definition: tools.h:357
Derived cDevice classes that can receive channels will have to provide Transport Stream (TS) packets ...
Definition: device.h:835
virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask)
Opens a file handle for the given filter data.
Definition: dvbdevice.c:1488
time_t lastTimeoutReport
Definition: dvbdevice.c:314
cDvbDevice(int Adapter, int Frontend)
Definition: dvbdevice.c:1095
virtual int SignalStrength(void) const
Returns the "strength" of the currently received signal.
Definition: dvbdevice.c:1625
bool IsAtsc(void) const
Definition: channels.h:203
cString GetBondingParams(const cChannel *Channel=NULL) const
Definition: dvbdevice.c:429
#define ATSC_TUNE_TIMEOUT
Definition: dvbdevice.c:32
const char * userString
Definition: dvbdevice.h:86
virtual bool MaySwitchTransponder(const cChannel *Channel) const
Returns true if it is ok to switch to the Channel&#39;s transponder on this device, without disturbing an...
Definition: dvbdevice.c:1645
cDvbTransponderParameters dtp
Definition: dvbdevice.c:1013
static cString DvbName(const char *Name, int Adapter, int Frontend)
Definition: dvbdevice.c:1143
The cDevice class is the base from which actual devices can be derived.
Definition: device.h:109
Definition: tools.h:168
static int DvbOpen(const char *Name, int Adapter, int Frontend, int Mode, bool ReportError=false)
Definition: dvbdevice.c:1148
cDvbTuner * bondedTuner
Definition: dvbdevice.c:326
const cDvbDevice * device
Definition: dvbdevice.c:308
const tDvbParameterMap GuardValues[]
Definition: dvbdevice.c:122
virtual void GetData(cChannel *Channel)
Copies all source specific parameters to the given Channel.
Definition: dvbdevice.c:1035
const tDvbParameterMap TransmissionValues[]
Definition: dvbdevice.c:111
static void SetTransferModeForDolbyDigital(int Mode)
Definition: dvbdevice.c:1662
void Unlock(void)
Definition: thread.c:197
virtual bool ProvidesEIT(void) const
Returns true if this device provides EIT data and thus wants to be tuned to the channels it can recei...
Definition: dvbdevice.c:1610
int DiSEqC
Definition: config.h:274