vdr  1.7.31
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 2.72 2012/09/20 10:07:54 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 #define FE_CAN_TURBO_FEC 0x8000000 // TODO: remove this once it is defined in the driver
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, INVERSION_OFF, trNOOP("off") },
41  { 1, INVERSION_ON, trNOOP("on") },
42  { 999, INVERSION_AUTO, trNOOP("auto") },
43  { -1, 0, NULL }
44  };
45 
47  { 5, 5000000, "5 MHz" },
48  { 6, 6000000, "6 MHz" },
49  { 7, 7000000, "7 MHz" },
50  { 8, 8000000, "8 MHz" },
51  { 10, 10000000, "10 MHz" },
52  { 1712, 1712000, "1.712 MHz" },
53  { -1, 0, NULL }
54  };
55 
57  { 0, FEC_NONE, trNOOP("none") },
58  { 12, FEC_1_2, "1/2" },
59  { 23, FEC_2_3, "2/3" },
60  { 34, FEC_3_4, "3/4" },
61  { 35, FEC_3_5, "3/5" },
62  { 45, FEC_4_5, "4/5" },
63  { 56, FEC_5_6, "5/6" },
64  { 67, FEC_6_7, "6/7" },
65  { 78, FEC_7_8, "7/8" },
66  { 89, FEC_8_9, "8/9" },
67  { 910, FEC_9_10, "9/10" },
68  { 999, FEC_AUTO, trNOOP("auto") },
69  { -1, 0, NULL }
70  };
71 
73  { 16, QAM_16, "QAM16" },
74  { 32, QAM_32, "QAM32" },
75  { 64, QAM_64, "QAM64" },
76  { 128, QAM_128, "QAM128" },
77  { 256, QAM_256, "QAM256" },
78  { 2, QPSK, "QPSK" },
79  { 5, PSK_8, "8PSK" },
80  { 6, APSK_16, "16APSK" },
81  { 7, APSK_32, "32APSK" },
82  { 10, VSB_8, "VSB8" },
83  { 11, VSB_16, "VSB16" },
84  { 12, DQPSK, "DQPSK" },
85  { 999, QAM_AUTO, trNOOP("auto") },
86  { -1, 0, NULL }
87  };
88 
89 #define DVB_SYSTEM_1 0 // see also nit.c
90 #define DVB_SYSTEM_2 1
91 
93  { 0, DVB_SYSTEM_1, "DVB-S" },
94  { 1, DVB_SYSTEM_2, "DVB-S2" },
95  { -1, 0, NULL }
96  };
97 
99  { 0, DVB_SYSTEM_1, "DVB-T" },
100  { 1, DVB_SYSTEM_2, "DVB-T2" },
101  { -1, 0, NULL }
102  };
103 
105  { 1, TRANSMISSION_MODE_1K, "1K" },
106  { 2, TRANSMISSION_MODE_2K, "2K" },
107  { 4, TRANSMISSION_MODE_4K, "4K" },
108  { 8, TRANSMISSION_MODE_8K, "8K" },
109  { 16, TRANSMISSION_MODE_16K, "16K" },
110  { 32, TRANSMISSION_MODE_32K, "32K" },
111  { 999, TRANSMISSION_MODE_AUTO, trNOOP("auto") },
112  { -1, 0, NULL }
113  };
114 
116  { 4, GUARD_INTERVAL_1_4, "1/4" },
117  { 8, GUARD_INTERVAL_1_8, "1/8" },
118  { 16, GUARD_INTERVAL_1_16, "1/16" },
119  { 32, GUARD_INTERVAL_1_32, "1/32" },
120  { 128, GUARD_INTERVAL_1_128, "1/128" },
121  { 19128, GUARD_INTERVAL_19_128, "19/128" },
122  { 19256, GUARD_INTERVAL_19_256, "19/256" },
123  { 999, GUARD_INTERVAL_AUTO, trNOOP("auto") },
124  { -1, 0, NULL }
125  };
126 
128  { 0, HIERARCHY_NONE, trNOOP("none") },
129  { 1, HIERARCHY_1, "1" },
130  { 2, HIERARCHY_2, "2" },
131  { 4, HIERARCHY_4, "4" },
132  { 999, HIERARCHY_AUTO, trNOOP("auto") },
133  { -1, 0, NULL }
134  };
135 
137  { 0, ROLLOFF_AUTO, trNOOP("auto") },
138  { 20, ROLLOFF_20, "0.20" },
139  { 25, ROLLOFF_25, "0.25" },
140  { 35, ROLLOFF_35, "0.35" },
141  { -1, 0, NULL }
142  };
143 
144 int UserIndex(int Value, const tDvbParameterMap *Map)
145 {
146  const tDvbParameterMap *map = Map;
147  while (map && map->userValue != -1) {
148  if (map->userValue == Value)
149  return map - Map;
150  map++;
151  }
152  return -1;
153 }
154 
155 int DriverIndex(int Value, const tDvbParameterMap *Map)
156 {
157  const tDvbParameterMap *map = Map;
158  while (map && map->userValue != -1) {
159  if (map->driverValue == Value)
160  return map - Map;
161  map++;
162  }
163  return -1;
164 }
165 
166 int MapToUser(int Value, const tDvbParameterMap *Map, const char **String)
167 {
168  int n = DriverIndex(Value, Map);
169  if (n >= 0) {
170  if (String)
171  *String = tr(Map[n].userString);
172  return Map[n].userValue;
173  }
174  return -1;
175 }
176 
177 const char *MapToUserString(int Value, const tDvbParameterMap *Map)
178 {
179  int n = DriverIndex(Value, Map);
180  if (n >= 0)
181  return Map[n].userString;
182  return "???";
183 }
184 
185 int MapToDriver(int Value, const tDvbParameterMap *Map)
186 {
187  int n = UserIndex(Value, Map);
188  if (n >= 0)
189  return Map[n].driverValue;
190  return -1;
191 }
192 
193 // --- cDvbTransponderParameters ---------------------------------------------
194 
196 {
197  polarization = 0;
198  inversion = INVERSION_AUTO;
199  bandwidth = 8000000;
200  coderateH = FEC_AUTO;
201  coderateL = FEC_AUTO;
202  modulation = QPSK;
204  transmission = TRANSMISSION_MODE_AUTO;
205  guard = GUARD_INTERVAL_AUTO;
206  hierarchy = HIERARCHY_AUTO;
207  rollOff = ROLLOFF_AUTO;
208  plpId = 0;
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(" T*") q += PrintParameter(q, 'B', MapToUser(bandwidth, BandwidthValues));
225  ST(" CST*") q += PrintParameter(q, 'C', MapToUser(coderateH, CoderateValues));
226  ST(" T*") q += PrintParameter(q, 'D', MapToUser(coderateL, CoderateValues));
227  ST(" T*") q += PrintParameter(q, 'G', MapToUser(guard, GuardValues));
228  ST("ACST*") q += PrintParameter(q, 'I', MapToUser(inversion, InversionValues));
229  ST("ACST*") q += PrintParameter(q, 'M', MapToUser(modulation, ModulationValues));
230  ST(" S 2") q += PrintParameter(q, 'O', MapToUser(rollOff, RollOffValues));
231  ST(" T2") q += PrintParameter(q, 'P', plpId);
232  ST(" ST*") q += PrintParameter(q, 'S', MapToUser(system, SystemValuesSat)); // we only need the numerical value, so Sat or Terr doesn't matter
233  ST(" T*") q += PrintParameter(q, 'T', MapToUser(transmission, TransmissionValues));
234  ST(" T*") q += PrintParameter(q, 'Y', MapToUser(hierarchy, HierarchyValues));
235  return buffer;
236 }
237 
238 const char *cDvbTransponderParameters::ParseParameter(const char *s, int &Value, const tDvbParameterMap *Map)
239 {
240  if (*++s) {
241  char *p = NULL;
242  errno = 0;
243  int n = strtol(s, &p, 10);
244  if (!errno && p != s) {
245  Value = Map ? MapToDriver(n, Map) : n;
246  if (Value >= 0)
247  return p;
248  }
249  }
250  esyslog("ERROR: invalid value for parameter '%c'", *(s - 1));
251  return NULL;
252 }
253 
255 {
256  while (s && *s) {
257  switch (toupper(*s)) {
258  case 'B': s = ParseParameter(s, bandwidth, BandwidthValues); break;
259  case 'C': s = ParseParameter(s, coderateH, CoderateValues); break;
260  case 'D': s = ParseParameter(s, coderateL, CoderateValues); break;
261  case 'G': s = ParseParameter(s, guard, GuardValues); break;
262  case 'H': polarization = *s++; break;
263  case 'I': s = ParseParameter(s, inversion, InversionValues); break;
264  case 'L': polarization = *s++; break;
265  case 'M': s = ParseParameter(s, modulation, ModulationValues); break;
266  case 'O': s = ParseParameter(s, rollOff, RollOffValues); break;
267  case 'P': s = ParseParameter(s, plpId); break;
268  case 'R': polarization = *s++; break;
269  case 'S': s = ParseParameter(s, system, SystemValuesSat); break; // we only need the numerical value, so Sat or Terr doesn't matter
270  case 'T': s = ParseParameter(s, transmission, TransmissionValues); break;
271  case 'V': polarization = *s++; break;
272  case 'Y': s = ParseParameter(s, hierarchy, HierarchyValues); break;
273  default: esyslog("ERROR: unknown parameter key '%c'", *s);
274  return false;
275  }
276  }
277  return true;
278 }
279 
280 // --- cDvbTuner -------------------------------------------------------------
281 
282 #define TUNER_POLL_TIMEOUT 10 // ms
283 
284 class cDvbTuner : public cThread {
285 private:
292  uint32_t subsystemId;
298  const cScr *scr;
306  bool SetFrontendType(const cChannel *Channel);
307  cString GetBondingParams(const cChannel *Channel = NULL) const;
308  void ClearEventQueue(void) const;
309  bool GetFrontendStatus(fe_status_t &Status) const;
310  void ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency);
311  void ResetToneAndVoltage(void);
312  bool SetFrontend(void);
313  virtual void Action(void);
314 public:
315  cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend);
316  virtual ~cDvbTuner();
317  int FrontendType(void) const { return frontendType; }
318  bool Bond(cDvbTuner *Tuner);
319  void UnBond(void);
320  bool BondingOk(const cChannel *Channel, bool ConsiderOccupied = false) const;
321  cDvbTuner *GetBondedMaster(void);
322  const cChannel *GetTransponder(void) const { return &channel; }
323  uint32_t SubsystemId(void) const { return subsystemId; }
324  bool IsTunedTo(const cChannel *Channel) const;
325  void SetChannel(const cChannel *Channel);
326  bool Locked(int TimeoutMs = 0);
327  int GetSignalStrength(void) const;
328  int GetSignalQuality(void) const;
329  };
330 
332 
333 cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend)
334 {
335  frontendType = SYS_UNDEFINED;
336  device = Device;
337  fd_frontend = Fd_Frontend;
338  adapter = Adapter;
339  frontend = Frontend;
341  tuneTimeout = 0;
342  lockTimeout = 0;
343  lastTimeoutReport = 0;
344  lastDiseqc = NULL;
345  scr = NULL;
346  lnbPowerTurnedOn = false;
348  bondedTuner = NULL;
349  bondedMaster = false;
350  SetDescription("tuner on frontend %d/%d", adapter, frontend);
351  Start();
352 }
353 
355 {
357  newSet.Broadcast();
358  locked.Broadcast();
359  Cancel(3);
360  UnBond();
361  /* looks like this irritates the SCR switch, so let's leave it out for now
362  if (lastDiseqc && lastDiseqc->IsScr()) {
363  unsigned int Frequency = 0;
364  ExecuteDiseqc(lastDiseqc, &Frequency);
365  }
366  */
367 }
368 
370 {
371  cMutexLock MutexLock(&bondMutex);
372  if (!bondedTuner) {
374  bondedMaster = false; // makes sure we don't disturb an existing master
375  bondedTuner = Tuner->bondedTuner ? Tuner->bondedTuner : Tuner;
376  Tuner->bondedTuner = this;
377  dsyslog("tuner %d/%d bonded with tuner %d/%d", adapter, frontend, bondedTuner->adapter, bondedTuner->frontend);
378  return true;
379  }
380  else
381  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);
382  return false;
383 }
384 
386 {
387  cMutexLock MutexLock(&bondMutex);
388  if (cDvbTuner *t = bondedTuner) {
389  dsyslog("tuner %d/%d unbonded from tuner %d/%d", adapter, frontend, bondedTuner->adapter, bondedTuner->frontend);
390  while (t->bondedTuner != this)
391  t = t->bondedTuner;
392  if (t == bondedTuner)
393  t->bondedTuner = NULL;
394  else
395  t->bondedTuner = bondedTuner;
396  bondedMaster = false; // another one will automatically become master whenever necessary
397  bondedTuner = NULL;
398  }
399 }
400 
402 {
403  if (!Channel)
404  Channel = &channel;
405  cDvbTransponderParameters dtp(Channel->Parameters());
406  if (Setup.DiSEqC) {
407  if (const cDiseqc *diseqc = Diseqcs.Get(device->CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization(), NULL))
408  return diseqc->Commands();
409  }
410  else {
411  bool ToneOff = Channel->Frequency() < (unsigned int)Setup.LnbSLOF;
412  bool VoltOff = dtp.Polarization() == 'V' || dtp.Polarization() == 'R';
413  return cString::sprintf("%c %c", ToneOff ? 't' : 'T', VoltOff ? 'v' : 'V');
414  }
415  return "";
416 }
417 
418 bool cDvbTuner::BondingOk(const cChannel *Channel, bool ConsiderOccupied) const
419 {
420  cMutexLock MutexLock(&bondMutex);
421  if (cDvbTuner *t = bondedTuner) {
422  cString BondingParams = GetBondingParams(Channel);
423  do {
424  if (t->device->Priority() > IDLEPRIORITY || ConsiderOccupied && t->device->Occupied()) {
425  if (strcmp(BondingParams, t->GetBondingParams()) != 0)
426  return false;
427  }
428  t = t->bondedTuner;
429  } while (t != bondedTuner);
430  }
431  return true;
432 }
433 
435 {
436  if (!bondedTuner)
437  return this; // an unbonded tuner is always "master"
438  cMutexLock MutexLock(&bondMutex);
439  if (bondedMaster)
440  return this;
441  // This tuner is bonded, but it's not the master, so let's see if there is a master at all:
442  if (cDvbTuner *t = bondedTuner) {
443  while (t != this) {
444  if (t->bondedMaster)
445  return t;
446  t = t->bondedTuner;
447  }
448  }
449  // None of the other bonded tuners is master, so make this one the master:
450  bondedMaster = true;
451  dsyslog("tuner %d/%d is now bonded master", adapter, frontend);
452  return this;
453 }
454 
455 bool cDvbTuner::IsTunedTo(const cChannel *Channel) const
456 {
457  if (tunerStatus == tsIdle)
458  return false; // not tuned to
459  if (channel.Source() != Channel->Source() || channel.Transponder() != Channel->Transponder())
460  return false; // sufficient mismatch
461  // Polarization is already checked as part of the Transponder.
462  return strcmp(channel.Parameters(), Channel->Parameters()) == 0;
463 }
464 
465 void cDvbTuner::SetChannel(const cChannel *Channel)
466 {
467  if (Channel) {
468  if (bondedTuner) {
469  cMutexLock MutexLock(&bondMutex);
470  cDvbTuner *BondedMaster = GetBondedMaster();
471  if (BondedMaster == this) {
472  if (strcmp(GetBondingParams(Channel), GetBondingParams()) != 0) {
473  // switching to a completely different band, so set all others to idle:
474  for (cDvbTuner *t = bondedTuner; t && t != this; t = t->bondedTuner)
475  t->SetChannel(NULL);
476  }
477  }
478  else if (strcmp(GetBondingParams(Channel), BondedMaster->GetBondingParams()) != 0)
479  BondedMaster->SetChannel(Channel);
480  }
481  cMutexLock MutexLock(&mutex);
482  if (!IsTunedTo(Channel))
483  tunerStatus = tsSet;
484  channel = *Channel;
485  lastTimeoutReport = 0;
486  newSet.Broadcast();
487  }
488  else {
489  cMutexLock MutexLock(&mutex);
492  }
494  cDevice::PrimaryDevice()->DelLivePids(); // 'device' is const, so we must do it this way
495 }
496 
497 bool cDvbTuner::Locked(int TimeoutMs)
498 {
499  bool isLocked = (tunerStatus >= tsLocked);
500  if (isLocked || !TimeoutMs)
501  return isLocked;
502 
503  cMutexLock MutexLock(&mutex);
504  if (TimeoutMs && tunerStatus < tsLocked)
505  locked.TimedWait(mutex, TimeoutMs);
506  return tunerStatus >= tsLocked;
507 }
508 
510 {
511  cPoller Poller(fd_frontend);
512  if (Poller.Poll(TUNER_POLL_TIMEOUT)) {
513  dvb_frontend_event Event;
514  while (ioctl(fd_frontend, FE_GET_EVENT, &Event) == 0)
515  ; // just to clear the event queue - we'll read the actual status below
516  }
517 }
518 
519 bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
520 {
521  ClearEventQueue();
522  while (1) {
523  if (ioctl(fd_frontend, FE_READ_STATUS, &Status) != -1)
524  return true;
525  if (errno != EINTR)
526  break;
527  }
528  return false;
529 }
530 
531 //#define DEBUG_SIGNALSTRENGTH
532 //#define DEBUG_SIGNALQUALITY
533 
535 {
536  ClearEventQueue();
537  uint16_t Signal;
538  while (1) {
539  if (ioctl(fd_frontend, FE_READ_SIGNAL_STRENGTH, &Signal) != -1)
540  break;
541  if (errno != EINTR)
542  return -1;
543  }
544  uint16_t MaxSignal = 0xFFFF; // Let's assume the default is using the entire range.
545  // Use the subsystemId to identify individual devices in case they need
546  // special treatment to map their Signal value into the range 0...0xFFFF.
547  switch (subsystemId) {
548  case 0x13C21019: MaxSignal = 670; break; // TT-budget S2-3200 (DVB-S/DVB-S2)
549  }
550  int s = int(Signal) * 100 / MaxSignal;
551  if (s > 100)
552  s = 100;
553 #ifdef DEBUG_SIGNALSTRENGTH
554  fprintf(stderr, "FE %d/%d: %08X S = %04X %04X %3d%%\n", adapter, frontend, subsystemId, MaxSignal, Signal, s);
555 #endif
556  return s;
557 }
558 
559 #define LOCK_THRESHOLD 5 // indicates that all 5 FE_HAS_* flags are set
560 
562 {
563  fe_status_t Status;
564  if (GetFrontendStatus(Status)) {
565  // 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...
566  if ((Status & FE_HAS_LOCK) == 0) {
567  if ((Status & FE_HAS_SIGNAL) == 0)
568  return 0;
569  if ((Status & FE_HAS_CARRIER) == 0)
570  return 1;
571  if ((Status & FE_HAS_VITERBI) == 0)
572  return 2;
573  if ((Status & FE_HAS_SYNC) == 0)
574  return 3;
575  return 4;
576  }
577  bool HasSnr = true;
578  uint16_t Snr;
579  while (1) {
580  if (ioctl(fd_frontend, FE_READ_SNR, &Snr) != -1)
581  break;
582  if (errno == EOPNOTSUPP) {
583  Snr = 0xFFFF;
584  HasSnr = false;
585  break;
586  }
587  if (errno != EINTR)
588  return -1;
589  }
590  bool HasBer = true;
591  uint32_t Ber;
592  while (1) {
593  if (ioctl(fd_frontend, FE_READ_BER, &Ber) != -1)
594  break;
595  if (errno == EOPNOTSUPP) {
596  Ber = 0;
597  HasBer = false;
598  break;
599  }
600  if (errno != EINTR)
601  return -1;
602  }
603  bool HasUnc = true;
604  uint32_t Unc;
605  while (1) {
606  if (ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &Unc) != -1)
607  break;
608  if (errno == EOPNOTSUPP) {
609  Unc = 0;
610  HasUnc = false;
611  break;
612  }
613  if (errno != EINTR)
614  return -1;
615  }
616  uint16_t MaxSnr = 0xFFFF; // Let's assume the default is using the entire range.
617  // Use the subsystemId to identify individual devices in case they need
618  // special treatment to map their Snr value into the range 0...0xFFFF.
619  switch (subsystemId) {
620  case 0x13C21019: MaxSnr = 200; break; // TT-budget S2-3200 (DVB-S/DVB-S2)
621  }
622  int a = int(Snr) * 100 / MaxSnr;
623  int b = 100 - (Unc * 10 + (Ber / 256) * 5);
624  if (b < 0)
625  b = 0;
626  int q = LOCK_THRESHOLD + a * b * (100 - LOCK_THRESHOLD) / 100 / 100;
627  if (q > 100)
628  q = 100;
629 #ifdef DEBUG_SIGNALQUALITY
630  fprintf(stderr, "FE %d/%d: %08X Q = %04X %04X %d %5d %5d %3d%%\n", adapter, frontend, subsystemId, MaxSnr, Snr, HasSnr, HasBer ? int(Ber) : -1, HasUnc ? int(Unc) : -1, q);
631 #endif
632  return q;
633  }
634  return -1;
635 }
636 
637 static unsigned int FrequencyToHz(unsigned int f)
638 {
639  while (f && f < 1000000)
640  f *= 1000;
641  return f;
642 }
643 
644 void cDvbTuner::ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency)
645 {
646  if (!lnbPowerTurnedOn) {
647  CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power
648  lnbPowerTurnedOn = true;
649  }
650  static cMutex Mutex;
651  if (Diseqc->IsScr())
652  Mutex.Lock();
653  struct dvb_diseqc_master_cmd cmd;
654  const char *CurrentAction = NULL;
655  for (;;) {
656  cmd.msg_len = sizeof(cmd.msg);
657  cDiseqc::eDiseqcActions da = Diseqc->Execute(&CurrentAction, cmd.msg, &cmd.msg_len, scr, Frequency);
658  if (da == cDiseqc::daNone)
659  break;
660  switch (da) {
661  case cDiseqc::daToneOff: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break;
662  case cDiseqc::daToneOn: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break;
663  case cDiseqc::daVoltage13: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break;
664  case cDiseqc::daVoltage18: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break;
665  case cDiseqc::daMiniA: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break;
666  case cDiseqc::daMiniB: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break;
667  case cDiseqc::daCodes: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd)); break;
668  default: esyslog("ERROR: unknown diseqc command %d", da);
669  }
670  }
671  if (scr)
672  ResetToneAndVoltage(); // makes sure we don't block the bus!
673  if (Diseqc->IsScr())
674  Mutex.Unlock();
675 }
676 
678 {
679  CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13));
680  CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF));
681 }
682 
683 static int GetRequiredDeliverySystem(const cChannel *Channel, const cDvbTransponderParameters *Dtp)
684 {
685  int ds = SYS_UNDEFINED;
686  if (Channel->IsAtsc())
687  ds = SYS_ATSC;
688  else if (Channel->IsCable())
689  ds = SYS_DVBC_ANNEX_AC;
690  else if (Channel->IsSat())
691  ds = Dtp->System() == DVB_SYSTEM_1 ? SYS_DVBS : SYS_DVBS2;
692  else if (Channel->IsTerr())
693  ds = Dtp->System() == DVB_SYSTEM_1 ? SYS_DVBT : SYS_DVBT2;
694  else
695  esyslog("ERROR: can't determine frontend type for channel %d", Channel->Number());
696  return ds;
697 }
698 
700 {
701 #define MAXFRONTENDCMDS 16
702 #define SETCMD(c, d) { Frontend[CmdSeq.num].cmd = (c);\
703  Frontend[CmdSeq.num].u.data = (d);\
704  if (CmdSeq.num++ > MAXFRONTENDCMDS) {\
705  esyslog("ERROR: too many tuning commands on frontend %d/%d", adapter, frontend);\
706  return false;\
707  }\
708  }
709  dtv_property Frontend[MAXFRONTENDCMDS];
710  memset(&Frontend, 0, sizeof(Frontend));
711  dtv_properties CmdSeq;
712  memset(&CmdSeq, 0, sizeof(CmdSeq));
713  CmdSeq.props = Frontend;
714  SETCMD(DTV_CLEAR, 0);
715  if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
716  esyslog("ERROR: frontend %d/%d: %m", adapter, frontend);
717  return false;
718  }
719  CmdSeq.num = 0;
720 
722 
723  // Determine the required frontend type:
725  if (frontendType == SYS_UNDEFINED)
726  return false;
727 
728  SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
729  if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2) {
730  unsigned int frequency = channel.Frequency();
731  if (Setup.DiSEqC) {
732  if (const cDiseqc *diseqc = Diseqcs.Get(device->CardIndex() + 1, channel.Source(), frequency, dtp.Polarization(), &scr)) {
733  frequency -= diseqc->Lof();
734  if (diseqc != lastDiseqc || diseqc->IsScr()) {
735  if (GetBondedMaster() == this) {
736  ExecuteDiseqc(diseqc, &frequency);
737  if (frequency == 0)
738  return false;
739  }
740  else
742  lastDiseqc = diseqc;
743  }
744  }
745  else {
746  esyslog("ERROR: no DiSEqC parameters found for channel %d", channel.Number());
747  return false;
748  }
749  }
750  else {
751  int tone = SEC_TONE_OFF;
752  if (frequency < (unsigned int)Setup.LnbSLOF) {
753  frequency -= Setup.LnbFrequLo;
754  tone = SEC_TONE_OFF;
755  }
756  else {
757  frequency -= Setup.LnbFrequHi;
758  tone = SEC_TONE_ON;
759  }
760  int volt = (dtp.Polarization() == 'V' || dtp.Polarization() == 'R') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
761  if (GetBondedMaster() != this) {
762  tone = SEC_TONE_OFF;
763  volt = SEC_VOLTAGE_13;
764  }
765  CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
766  CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
767  }
768  frequency = abs(frequency); // Allow for C-band, where the frequency is less than the LOF
769 
770  // DVB-S/DVB-S2 (common parts)
771  SETCMD(DTV_FREQUENCY, frequency * 1000UL);
772  SETCMD(DTV_MODULATION, dtp.Modulation());
773  SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL);
774  SETCMD(DTV_INNER_FEC, dtp.CoderateH());
775  SETCMD(DTV_INVERSION, dtp.Inversion());
776  if (frontendType == SYS_DVBS2) {
777  // DVB-S2
778  SETCMD(DTV_PILOT, PILOT_AUTO);
779  SETCMD(DTV_ROLLOFF, dtp.RollOff());
780  }
781  else {
782  // DVB-S
783  SETCMD(DTV_ROLLOFF, ROLLOFF_35); // DVB-S always has a ROLLOFF of 0.35
784  }
785 
788  }
789  else if (frontendType == SYS_DVBC_ANNEX_AC || frontendType == SYS_DVBC_ANNEX_B) {
790  // DVB-C
791  SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
792  SETCMD(DTV_INVERSION, dtp.Inversion());
793  SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL);
794  SETCMD(DTV_INNER_FEC, dtp.CoderateH());
795  SETCMD(DTV_MODULATION, dtp.Modulation());
796 
799  }
800  else if (frontendType == SYS_DVBT || frontendType == SYS_DVBT2) {
801  // DVB-T/DVB-T2 (common parts)
802  SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
803  SETCMD(DTV_INVERSION, dtp.Inversion());
804  SETCMD(DTV_BANDWIDTH_HZ, dtp.Bandwidth());
805  SETCMD(DTV_CODE_RATE_HP, dtp.CoderateH());
806  SETCMD(DTV_CODE_RATE_LP, dtp.CoderateL());
807  SETCMD(DTV_MODULATION, dtp.Modulation());
808  SETCMD(DTV_TRANSMISSION_MODE, dtp.Transmission());
809  SETCMD(DTV_GUARD_INTERVAL, dtp.Guard());
810  SETCMD(DTV_HIERARCHY, dtp.Hierarchy());
811  if (frontendType == SYS_DVBT2) {
812  // DVB-T2
813  SETCMD(DTV_DVBT2_PLP_ID, dtp.PlpId());
814  }
815 
818  }
819  else if (frontendType == SYS_ATSC) {
820  // ATSC
821  SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
822  SETCMD(DTV_INVERSION, dtp.Inversion());
823  SETCMD(DTV_MODULATION, dtp.Modulation());
824 
827  }
828  else {
829  esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
830  return false;
831  }
832  SETCMD(DTV_TUNE, 0);
833  if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
834  esyslog("ERROR: frontend %d/%d: %m", adapter, frontend);
835  return false;
836  }
837  return true;
838 }
839 
841 {
842  cTimeMs Timer;
843  bool LostLock = false;
844  fe_status_t Status = (fe_status_t)0;
845  while (Running()) {
846  fe_status_t NewStatus;
847  if (GetFrontendStatus(NewStatus))
848  Status = NewStatus;
849  cMutexLock MutexLock(&mutex);
850  int WaitTime = 1000;
851  switch (tunerStatus) {
852  case tsIdle:
853  break;
854  case tsSet:
856  Timer.Set(tuneTimeout + (scr ? rand() % SCR_RANDOM_TIMEOUT : 0));
857  continue;
858  case tsTuned:
859  if (Timer.TimedOut()) {
860  tunerStatus = tsSet;
861  lastDiseqc = NULL;
862  if (time(NULL) - lastTimeoutReport > 60) { // let's not get too many of these
863  isyslog("frontend %d/%d timed out while tuning to channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder());
864  lastTimeoutReport = time(NULL);
865  }
866  continue;
867  }
868  WaitTime = 100; // allows for a quick change from tsTuned to tsLocked
869  case tsLocked:
870  if (Status & FE_REINIT) {
871  tunerStatus = tsSet;
872  lastDiseqc = NULL;
873  isyslog("frontend %d/%d was reinitialized", adapter, frontend);
874  lastTimeoutReport = 0;
875  continue;
876  }
877  else if (Status & FE_HAS_LOCK) {
878  if (LostLock) {
879  isyslog("frontend %d/%d regained lock on channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder());
880  LostLock = false;
881  }
883  locked.Broadcast();
884  lastTimeoutReport = 0;
885  }
886  else if (tunerStatus == tsLocked) {
887  LostLock = true;
888  isyslog("frontend %d/%d lost lock on channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder());
890  Timer.Set(lockTimeout);
891  lastTimeoutReport = 0;
892  continue;
893  }
894  break;
895  default: esyslog("ERROR: unknown tuner status %d", tunerStatus);
896  }
897  newSet.TimedWait(mutex, WaitTime);
898  }
899 }
900 
901 // --- cDvbSourceParam -------------------------------------------------------
902 
904 private:
905  int param;
906  int srate;
908 public:
909  cDvbSourceParam(char Source, const char *Description);
910  virtual void SetData(cChannel *Channel);
911  virtual void GetData(cChannel *Channel);
912  virtual cOsdItem *GetOsdItem(void);
913  };
914 
915 cDvbSourceParam::cDvbSourceParam(char Source, const char *Description)
916 :cSourceParam(Source, Description)
917 {
918  param = 0;
919  srate = 0;
920 }
921 
923 {
924  srate = Channel->Srate();
925  dtp.Parse(Channel->Parameters());
926  param = 0;
927 }
928 
930 {
931  Channel->SetTransponderData(Channel->Source(), Channel->Frequency(), srate, dtp.ToString(Source()), true);
932 }
933 
935 {
936  char type = Source();
937  const tDvbParameterMap *SystemValues = type == 'S' ? SystemValuesSat : SystemValuesTerr;
938 #undef ST
939 #define ST(s) if (strchr(s, type))
940  switch (param++) {
941  case 0: ST(" S ") return new cMenuEditChrItem( tr("Polarization"), &dtp.polarization, "HVLR"); else return GetOsdItem();
942  case 1: ST(" ST") return new cMenuEditMapItem( tr("System"), &dtp.system, SystemValues); else return GetOsdItem();
943  case 2: ST(" CS ") return new cMenuEditIntItem( tr("Srate"), &srate); else return GetOsdItem();
944  case 3: ST("ACST") return new cMenuEditMapItem( tr("Inversion"), &dtp.inversion, InversionValues); else return GetOsdItem();
945  case 4: ST(" CST") return new cMenuEditMapItem( tr("CoderateH"), &dtp.coderateH, CoderateValues); else return GetOsdItem();
946  case 5: ST(" T") return new cMenuEditMapItem( tr("CoderateL"), &dtp.coderateL, CoderateValues); else return GetOsdItem();
947  case 6: ST("ACST") return new cMenuEditMapItem( tr("Modulation"), &dtp.modulation, ModulationValues); else return GetOsdItem();
948  case 7: ST(" T") return new cMenuEditMapItem( tr("Bandwidth"), &dtp.bandwidth, BandwidthValues); else return GetOsdItem();
949  case 8: ST(" T") return new cMenuEditMapItem( tr("Transmission"), &dtp.transmission, TransmissionValues); else return GetOsdItem();
950  case 9: ST(" T") return new cMenuEditMapItem( tr("Guard"), &dtp.guard, GuardValues); else return GetOsdItem();
951  case 10: ST(" T") return new cMenuEditMapItem( tr("Hierarchy"), &dtp.hierarchy, HierarchyValues); else return GetOsdItem();
952  case 11: ST(" S ") return new cMenuEditMapItem( tr("Rolloff"), &dtp.rollOff, RollOffValues); else return GetOsdItem();
953  case 12: ST(" T") return new cMenuEditIntItem( tr("PlpId"), &dtp.plpId, 0, 255); else return GetOsdItem();
954  default: return NULL;
955  }
956  return NULL;
957 }
958 
959 // --- cDvbDevice ------------------------------------------------------------
960 
963 
964 const char *DeliverySystemNames[] = {
965  "",
966  "DVB-C",
967  "DVB-C",
968  "DVB-T",
969  "DSS",
970  "DVB-S",
971  "DVB-S2",
972  "DVB-H",
973  "ISDBT",
974  "ISDBS",
975  "ISDBC",
976  "ATSC",
977  "ATSCMH",
978  "DMBTH",
979  "CMMB",
980  "DAB",
981  "DVB-T2",
982  "TURBO",
983  NULL
984  };
985 
986 cDvbDevice::cDvbDevice(int Adapter, int Frontend)
987 {
988  adapter = Adapter;
989  frontend = Frontend;
990  ciAdapter = NULL;
991  dvbTuner = NULL;
992  numDeliverySystems = 0;
993  numModulations = 0;
994  bondedDevice = NULL;
996  tsBuffer = NULL;
997 
998  // Devices that are present on all card types:
999 
1000  int fd_frontend = DvbOpen(DEV_DVB_FRONTEND, adapter, frontend, O_RDWR | O_NONBLOCK);
1001 
1002  // Common Interface:
1003 
1004  fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR);
1005  if (fd_ca >= 0)
1007 
1008  // The DVR device (will be opened and closed as needed):
1009 
1010  fd_dvr = -1;
1011 
1012  // We only check the devices that must be present - the others will be checked before accessing them://XXX
1013 
1014  if (fd_frontend >= 0) {
1015  if (QueryDeliverySystems(fd_frontend))
1016  dvbTuner = new cDvbTuner(this, fd_frontend, adapter, frontend);
1017  }
1018  else
1019  esyslog("ERROR: can't open DVB device %d/%d", adapter, frontend);
1020 
1022 }
1023 
1025 {
1027  delete dvbTuner;
1028  delete ciAdapter;
1029  UnBond();
1030  // We're not explicitly closing any device files here, since this sometimes
1031  // caused segfaults. Besides, the program is about to terminate anyway...
1032 }
1033 
1034 cString cDvbDevice::DvbName(const char *Name, int Adapter, int Frontend)
1035 {
1036  return cString::sprintf("%s/%s%d/%s%d", DEV_DVB_BASE, DEV_DVB_ADAPTER, Adapter, Name, Frontend);
1037 }
1038 
1039 int cDvbDevice::DvbOpen(const char *Name, int Adapter, int Frontend, int Mode, bool ReportError)
1040 {
1041  cString FileName = DvbName(Name, Adapter, Frontend);
1042  int fd = open(FileName, Mode);
1043  if (fd < 0 && ReportError)
1044  LOG_ERROR_STR(*FileName);
1045  return fd;
1046 }
1047 
1048 bool cDvbDevice::Exists(int Adapter, int Frontend)
1049 {
1050  cString FileName = DvbName(DEV_DVB_FRONTEND, Adapter, Frontend);
1051  if (access(FileName, F_OK) == 0) {
1052  int f = open(FileName, O_RDONLY);
1053  if (f >= 0) {
1054  close(f);
1055  return true;
1056  }
1057  else if (errno != ENODEV && errno != EINVAL)
1058  LOG_ERROR_STR(*FileName);
1059  }
1060  else if (errno != ENOENT)
1061  LOG_ERROR_STR(*FileName);
1062  return false;
1063 }
1064 
1065 bool cDvbDevice::Probe(int Adapter, int Frontend)
1066 {
1067  cString FileName = DvbName(DEV_DVB_FRONTEND, Adapter, Frontend);
1068  dsyslog("probing %s", *FileName);
1069  for (cDvbDeviceProbe *dp = DvbDeviceProbes.First(); dp; dp = DvbDeviceProbes.Next(dp)) {
1070  if (dp->Probe(Adapter, Frontend))
1071  return true; // a plugin has created the actual device
1072  }
1073  dsyslog("creating cDvbDevice");
1074  new cDvbDevice(Adapter, Frontend); // it's a "budget" device
1075  return true;
1076 }
1077 
1079 {
1080  if (dvbTuner) {
1081  if (dvbTuner->FrontendType() != SYS_UNDEFINED)
1083  if (numDeliverySystems)
1084  return DeliverySystemNames[deliverySystems[0]]; // to have some reasonable default
1085  }
1086  return "";
1087 }
1088 
1090 {
1091  return frontendInfo.name;
1092 }
1093 
1095 {
1096  new cDvbSourceParam('A', "ATSC");
1097  new cDvbSourceParam('C', "DVB-C");
1098  new cDvbSourceParam('S', "DVB-S");
1099  new cDvbSourceParam('T', "DVB-T");
1100  cStringList Nodes;
1101  cReadDir DvbDir(DEV_DVB_BASE);
1102  if (DvbDir.Ok()) {
1103  struct dirent *a;
1104  while ((a = DvbDir.Next()) != NULL) {
1105  if (strstr(a->d_name, DEV_DVB_ADAPTER) == a->d_name) {
1106  int Adapter = strtol(a->d_name + strlen(DEV_DVB_ADAPTER), NULL, 10);
1107  cReadDir AdapterDir(AddDirectory(DEV_DVB_BASE, a->d_name));
1108  if (AdapterDir.Ok()) {
1109  struct dirent *f;
1110  while ((f = AdapterDir.Next()) != NULL) {
1111  if (strstr(f->d_name, DEV_DVB_FRONTEND) == f->d_name) {
1112  int Frontend = strtol(f->d_name + strlen(DEV_DVB_FRONTEND), NULL, 10);
1113  Nodes.Append(strdup(cString::sprintf("%2d %2d", Adapter, Frontend)));
1114  }
1115  }
1116  }
1117  }
1118  }
1119  }
1120  int Checked = 0;
1121  int Found = 0;
1122  if (Nodes.Size() > 0) {
1123  Nodes.Sort();
1124  for (int i = 0; i < Nodes.Size(); i++) {
1125  int Adapter;
1126  int Frontend;
1127  if (2 == sscanf(Nodes[i], "%d %d", &Adapter, &Frontend)) {
1128  if (Exists(Adapter, Frontend)) {
1129  if (Checked++ < MAXDVBDEVICES) {
1130  if (UseDevice(NextCardIndex())) {
1131  if (Probe(Adapter, Frontend))
1132  Found++;
1133  }
1134  else
1135  NextCardIndex(1); // skips this one
1136  }
1137  }
1138  }
1139  }
1140  }
1141  NextCardIndex(MAXDVBDEVICES - Checked); // skips the rest
1142  if (Found > 0)
1143  isyslog("found %d DVB device%s", Found, Found > 1 ? "s" : "");
1144  else
1145  isyslog("no DVB device found");
1146  return Found > 0;
1147 }
1148 
1150 {
1151  numDeliverySystems = 0;
1152  if (ioctl(fd_frontend, FE_GET_INFO, &frontendInfo) < 0) {
1153  LOG_ERROR;
1154  return false;
1155  }
1156 #if (DVB_API_VERSION << 8 | DVB_API_VERSION_MINOR) >= 0x0505
1157  dtv_property Frontend[1];
1158  memset(&Frontend, 0, sizeof(Frontend));
1159  dtv_properties CmdSeq;
1160  memset(&CmdSeq, 0, sizeof(CmdSeq));
1161  CmdSeq.props = Frontend;
1162  SETCMD(DTV_ENUM_DELSYS, 0);
1163  int Result = ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq);
1164  if (Result == 0) {
1165  for (uint i = 0; i < Frontend[0].u.buffer.len; i++) {
1167  esyslog("ERROR: too many delivery systems on frontend %d/%d", adapter, frontend);
1168  break;
1169  }
1170  deliverySystems[numDeliverySystems++] = Frontend[0].u.buffer.data[i];
1171  }
1172  }
1173  else {
1174  esyslog("ERROR: can't query delivery systems on frontend %d/%d - falling back to legacy mode", adapter, frontend);
1175 #else
1176  {
1177 #endif
1178  // Legacy mode (DVB-API < 5.5):
1179  switch (frontendInfo.type) {
1180  case FE_QPSK: deliverySystems[numDeliverySystems++] = SYS_DVBS;
1181  if (frontendInfo.caps & FE_CAN_2G_MODULATION)
1182  deliverySystems[numDeliverySystems++] = SYS_DVBS2;
1183  break;
1184  case FE_OFDM: deliverySystems[numDeliverySystems++] = SYS_DVBT;
1185  if (frontendInfo.caps & FE_CAN_2G_MODULATION)
1186  deliverySystems[numDeliverySystems++] = SYS_DVBT2;
1187  break;
1188  case FE_QAM: deliverySystems[numDeliverySystems++] = SYS_DVBC_ANNEX_AC; break;
1189  case FE_ATSC: deliverySystems[numDeliverySystems++] = SYS_ATSC; break;
1190  default: esyslog("ERROR: unknown frontend type %d on frontend %d/%d", frontendInfo.type, adapter, frontend);
1191  }
1192  }
1193  if (numDeliverySystems > 0) {
1194  cString ds("");
1195  for (int i = 0; i < numDeliverySystems; i++)
1196  ds = cString::sprintf("%s%s%s", *ds, i ? "," : "", DeliverySystemNames[deliverySystems[i]]);
1197  cString ms("");
1198  if (frontendInfo.caps & FE_CAN_QPSK) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QPSK, ModulationValues)); }
1199  if (frontendInfo.caps & FE_CAN_QAM_16) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_16, ModulationValues)); }
1200  if (frontendInfo.caps & FE_CAN_QAM_32) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_32, ModulationValues)); }
1201  if (frontendInfo.caps & FE_CAN_QAM_64) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_64, ModulationValues)); }
1202  if (frontendInfo.caps & FE_CAN_QAM_128) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_128, ModulationValues)); }
1203  if (frontendInfo.caps & FE_CAN_QAM_256) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_256, ModulationValues)); }
1204  if (frontendInfo.caps & FE_CAN_8VSB) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(VSB_8, ModulationValues)); }
1205  if (frontendInfo.caps & FE_CAN_16VSB) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(VSB_16, ModulationValues)); }
1206  if (frontendInfo.caps & FE_CAN_TURBO_FEC) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", "TURBO_FEC"); }
1207  if (!**ms)
1208  ms = "unknown modulations";
1209  isyslog("frontend %d/%d provides %s with %s (\"%s\")", adapter, frontend, *ds, *ms, frontendInfo.name);
1210  return true;
1211  }
1212  else
1213  esyslog("ERROR: frontend %d/%d doesn't provide any delivery systems", adapter, frontend);
1214  return false;
1215 }
1216 
1218 {
1219  if (ciAdapter)
1220  return ciAdapter->Ready();
1221  return true;
1222 }
1223 
1224 bool cDvbDevice::BondDevices(const char *Bondings)
1225 {
1226  UnBondDevices();
1227  if (Bondings) {
1228  cSatCableNumbers SatCableNumbers(MAXDEVICES, Bondings);
1229  for (int i = 0; i < cDevice::NumDevices(); i++) {
1230  int d = SatCableNumbers.FirstDeviceIndex(i);
1231  if (d >= 0) {
1232  int ErrorDevice = 0;
1233  if (cDevice *Device1 = cDevice::GetDevice(i)) {
1234  if (cDevice *Device2 = cDevice::GetDevice(d)) {
1235  if (cDvbDevice *DvbDevice1 = dynamic_cast<cDvbDevice *>(Device1)) {
1236  if (cDvbDevice *DvbDevice2 = dynamic_cast<cDvbDevice *>(Device2)) {
1237  if (!DvbDevice1->Bond(DvbDevice2))
1238  return false; // Bond() has already logged the error
1239  }
1240  else
1241  ErrorDevice = d + 1;
1242  }
1243  else
1244  ErrorDevice = i + 1;
1245  if (ErrorDevice) {
1246  esyslog("ERROR: device '%d' in device bondings '%s' is not a cDvbDevice", ErrorDevice, Bondings);
1247  return false;
1248  }
1249  }
1250  else
1251  ErrorDevice = d + 1;
1252  }
1253  else
1254  ErrorDevice = i + 1;
1255  if (ErrorDevice) {
1256  esyslog("ERROR: unknown device '%d' in device bondings '%s'", ErrorDevice, Bondings);
1257  return false;
1258  }
1259  }
1260  }
1261  }
1262  return true;
1263 }
1264 
1266 {
1267  for (int i = 0; i < cDevice::NumDevices(); i++) {
1268  if (cDvbDevice *d = dynamic_cast<cDvbDevice *>(cDevice::GetDevice(i)))
1269  d->UnBond();
1270  }
1271 }
1272 
1274 {
1275  cMutexLock MutexLock(&bondMutex);
1276  if (!bondedDevice) {
1277  if (Device != this) {
1278  if ((ProvidesDeliverySystem(SYS_DVBS) || ProvidesDeliverySystem(SYS_DVBS2)) && (Device->ProvidesDeliverySystem(SYS_DVBS) || Device->ProvidesDeliverySystem(SYS_DVBS2))) {
1279  if (dvbTuner && Device->dvbTuner && dvbTuner->Bond(Device->dvbTuner)) {
1280  bondedDevice = Device->bondedDevice ? Device->bondedDevice : Device;
1281  Device->bondedDevice = this;
1282  dsyslog("device %d bonded with device %d", CardIndex() + 1, bondedDevice->CardIndex() + 1);
1283  return true;
1284  }
1285  }
1286  else
1287  esyslog("ERROR: can't bond device %d with device %d (only DVB-S(2) devices can be bonded)", CardIndex() + 1, Device->CardIndex() + 1);
1288  }
1289  else
1290  esyslog("ERROR: can't bond device %d with itself", CardIndex() + 1);
1291  }
1292  else
1293  esyslog("ERROR: device %d already bonded with device %d, can't bond with device %d", CardIndex() + 1, bondedDevice->CardIndex() + 1, Device->CardIndex() + 1);
1294  return false;
1295 }
1296 
1298 {
1299  cMutexLock MutexLock(&bondMutex);
1300  if (cDvbDevice *d = bondedDevice) {
1301  if (dvbTuner)
1302  dvbTuner->UnBond();
1303  dsyslog("device %d unbonded from device %d", CardIndex() + 1, bondedDevice->CardIndex() + 1);
1304  while (d->bondedDevice != this)
1305  d = d->bondedDevice;
1306  if (d == bondedDevice)
1307  d->bondedDevice = NULL;
1308  else
1309  d->bondedDevice = bondedDevice;
1310  bondedDevice = NULL;
1311  }
1312 }
1313 
1314 bool cDvbDevice::BondingOk(const cChannel *Channel, bool ConsiderOccupied) const
1315 {
1316  cMutexLock MutexLock(&bondMutex);
1317  if (bondedDevice)
1318  return dvbTuner && dvbTuner->BondingOk(Channel, ConsiderOccupied);
1319  return true;
1320 }
1321 
1323 {
1324  return ciAdapter;
1325 }
1326 
1327 bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
1328 {
1329  if (Handle->pid) {
1330  dmx_pes_filter_params pesFilterParams;
1331  memset(&pesFilterParams, 0, sizeof(pesFilterParams));
1332  if (On) {
1333  if (Handle->handle < 0) {
1334  Handle->handle = DvbOpen(DEV_DVB_DEMUX, adapter, frontend, O_RDWR | O_NONBLOCK, true);
1335  if (Handle->handle < 0) {
1336  LOG_ERROR;
1337  return false;
1338  }
1339  }
1340  pesFilterParams.pid = Handle->pid;
1341  pesFilterParams.input = DMX_IN_FRONTEND;
1342  pesFilterParams.output = DMX_OUT_TS_TAP;
1343  pesFilterParams.pes_type= DMX_PES_OTHER;
1344  pesFilterParams.flags = DMX_IMMEDIATE_START;
1345  if (ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
1346  LOG_ERROR;
1347  return false;
1348  }
1349  }
1350  else if (!Handle->used) {
1351  CHECK(ioctl(Handle->handle, DMX_STOP));
1352  if (Type <= ptTeletext) {
1353  pesFilterParams.pid = 0x1FFF;
1354  pesFilterParams.input = DMX_IN_FRONTEND;
1355  pesFilterParams.output = DMX_OUT_DECODER;
1356  pesFilterParams.pes_type= DMX_PES_OTHER;
1357  pesFilterParams.flags = DMX_IMMEDIATE_START;
1358  CHECK(ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams));
1359  }
1360  close(Handle->handle);
1361  Handle->handle = -1;
1362  }
1363  }
1364  return true;
1365 }
1366 
1367 int cDvbDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask)
1368 {
1370  int f = open(FileName, O_RDWR | O_NONBLOCK);
1371  if (f >= 0) {
1372  dmx_sct_filter_params sctFilterParams;
1373  memset(&sctFilterParams, 0, sizeof(sctFilterParams));
1374  sctFilterParams.pid = Pid;
1375  sctFilterParams.timeout = 0;
1376  sctFilterParams.flags = DMX_IMMEDIATE_START;
1377  sctFilterParams.filter.filter[0] = Tid;
1378  sctFilterParams.filter.mask[0] = Mask;
1379  if (ioctl(f, DMX_SET_FILTER, &sctFilterParams) >= 0)
1380  return f;
1381  else {
1382  esyslog("ERROR: can't set filter (pid=%d, tid=%02X, mask=%02X): %m", Pid, Tid, Mask);
1383  close(f);
1384  }
1385  }
1386  else
1387  esyslog("ERROR: can't open filter handle on '%s'", *FileName);
1388  return -1;
1389 }
1390 
1391 void cDvbDevice::CloseFilter(int Handle)
1392 {
1393  close(Handle);
1394 }
1395 
1396 bool cDvbDevice::ProvidesDeliverySystem(int DeliverySystem) const
1397 {
1398  for (int i = 0; i < numDeliverySystems; i++) {
1399  if (deliverySystems[i] == DeliverySystem)
1400  return true;
1401  }
1402  return false;
1403 }
1404 
1405 bool cDvbDevice::ProvidesSource(int Source) const
1406 {
1407  int type = Source & cSource::st_Mask;
1408  return type == cSource::stNone
1409  || type == cSource::stAtsc && ProvidesDeliverySystem(SYS_ATSC)
1410  || type == cSource::stCable && (ProvidesDeliverySystem(SYS_DVBC_ANNEX_AC) || ProvidesDeliverySystem(SYS_DVBC_ANNEX_B))
1411  || type == cSource::stSat && (ProvidesDeliverySystem(SYS_DVBS) || ProvidesDeliverySystem(SYS_DVBS2))
1412  || type == cSource::stTerr && (ProvidesDeliverySystem(SYS_DVBT) || ProvidesDeliverySystem(SYS_DVBT2));
1413 }
1414 
1415 bool cDvbDevice::ProvidesTransponder(const cChannel *Channel) const
1416 {
1417  if (!ProvidesSource(Channel->Source()))
1418  return false; // doesn't provide source
1419  cDvbTransponderParameters dtp(Channel->Parameters());
1420  if (!ProvidesDeliverySystem(GetRequiredDeliverySystem(Channel, &dtp)) ||
1421  dtp.Modulation() == QPSK && !(frontendInfo.caps & FE_CAN_QPSK) ||
1422  dtp.Modulation() == QAM_16 && !(frontendInfo.caps & FE_CAN_QAM_16) ||
1423  dtp.Modulation() == QAM_32 && !(frontendInfo.caps & FE_CAN_QAM_32) ||
1424  dtp.Modulation() == QAM_64 && !(frontendInfo.caps & FE_CAN_QAM_64) ||
1425  dtp.Modulation() == QAM_128 && !(frontendInfo.caps & FE_CAN_QAM_128) ||
1426  dtp.Modulation() == QAM_256 && !(frontendInfo.caps & FE_CAN_QAM_256) ||
1427  dtp.Modulation() == QAM_AUTO && !(frontendInfo.caps & FE_CAN_QAM_AUTO) ||
1428  dtp.Modulation() == VSB_8 && !(frontendInfo.caps & FE_CAN_8VSB) ||
1429  dtp.Modulation() == VSB_16 && !(frontendInfo.caps & FE_CAN_16VSB) ||
1430  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
1431  return false; // requires modulation system which frontend doesn't provide
1432  if (!cSource::IsSat(Channel->Source()) ||
1433  (!Setup.DiSEqC || Diseqcs.Get(CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization(), NULL)))
1434  return DeviceHooksProvidesTransponder(Channel);
1435  return false;
1436 }
1437 
1438 bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
1439 {
1440  bool result = false;
1441  bool hasPriority = Priority == IDLEPRIORITY || Priority > this->Priority();
1442  bool needsDetachReceivers = false;
1444 
1445  if (dvbTuner && ProvidesTransponder(Channel)) {
1446  result = hasPriority;
1447  if (Priority > IDLEPRIORITY) {
1448  if (Receiving()) {
1449  if (dvbTuner->IsTunedTo(Channel)) {
1450  if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0)) || Channel->Dpid(0) && !HasPid(Channel->Dpid(0))) {
1451  if (CamSlot() && Channel->Ca() >= CA_ENCRYPTED_MIN) {
1452  if (CamSlot()->CanDecrypt(Channel))
1453  result = true;
1454  else
1455  needsDetachReceivers = true;
1456  }
1457  else
1458  result = true;
1459  }
1460  else
1461  result = true;
1462  }
1463  else
1464  needsDetachReceivers = Receiving();
1465  }
1466  if (result) {
1467  if (!BondingOk(Channel)) {
1468  // This device is bonded, so we need to check the priorities of the others:
1469  for (cDvbDevice *d = bondedDevice; d && d != this; d = d->bondedDevice) {
1470  if (d->Priority() >= Priority) {
1471  result = false;
1472  break;
1473  }
1474  }
1476  needsDetachReceivers = Receiving();
1477  }
1478  }
1479  }
1480  }
1481  if (NeedsDetachReceivers)
1482  *NeedsDetachReceivers = needsDetachReceivers;
1483  return result;
1484 }
1485 
1486 bool cDvbDevice::ProvidesEIT(void) const
1487 {
1488  return dvbTuner != NULL;
1489 }
1490 
1492 {
1494 }
1495 
1497 {
1498  return dvbTuner ? dvbTuner->GetSignalStrength() : -1;
1499 }
1500 
1502 {
1503  return dvbTuner ? dvbTuner->GetSignalQuality() : -1;
1504 }
1505 
1507 {
1508  return dvbTuner ? dvbTuner->GetTransponder() : NULL;
1509 }
1510 
1511 bool cDvbDevice::IsTunedToTransponder(const cChannel *Channel) const
1512 {
1513  return dvbTuner ? dvbTuner->IsTunedTo(Channel) : false;
1514 }
1515 
1516 bool cDvbDevice::MaySwitchTransponder(const cChannel *Channel) const
1517 {
1518  return BondingOk(Channel, true) && cDevice::MaySwitchTransponder(Channel);
1519 }
1520 
1521 bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
1522 {
1523  if (dvbTuner)
1524  dvbTuner->SetChannel(Channel);
1525  return true;
1526 }
1527 
1528 bool cDvbDevice::HasLock(int TimeoutMs)
1529 {
1530  return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false;
1531 }
1532 
1534 {
1536 }
1537 
1539 {
1540  CloseDvr();
1541  fd_dvr = DvbOpen(DEV_DVB_DVR, adapter, frontend, O_RDONLY | O_NONBLOCK, true);
1542  if (fd_dvr >= 0)
1543  tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1);
1544  return fd_dvr >= 0;
1545 }
1546 
1548 {
1549  if (fd_dvr >= 0) {
1550  delete tsBuffer;
1551  tsBuffer = NULL;
1552  close(fd_dvr);
1553  fd_dvr = -1;
1554  }
1555 }
1556 
1558 {
1559  if (tsBuffer) {
1560  Data = tsBuffer->Get();
1561  return true;
1562  }
1563  return false;
1564 }
1565 
1567 {
1568  cMutexLock MutexLock(&bondMutex);
1569  cDvbDevice *d = this;
1570  do {
1571  d->cDevice::DetachAllReceivers();
1572  d = d->bondedDevice;
1573  } while (d && d != this && needsDetachBondedReceivers);
1575 }
1576 
1577 // --- cDvbDeviceProbe -------------------------------------------------------
1578 
1580 
1582 {
1583  DvbDeviceProbes.Add(this);
1584 }
1585 
1587 {
1588  DvbDeviceProbes.Del(this, false);
1589 }
1590 
1591 uint32_t cDvbDeviceProbe::GetSubsystemId(int Adapter, int Frontend)
1592 {
1593  uint32_t SubsystemId = 0;
1594  cString FileName = cString::sprintf("/dev/dvb/adapter%d/frontend%d", Adapter, Frontend);
1595  struct stat st;
1596  if (stat(FileName, &st) == 0) {
1597  cReadDir d("/sys/class/dvb");
1598  if (d.Ok()) {
1599  struct dirent *e;
1600  while ((e = d.Next()) != NULL) {
1601  if (strstr(e->d_name, "frontend")) {
1602  FileName = cString::sprintf("/sys/class/dvb/%s/dev", e->d_name);
1603  if (FILE *f = fopen(FileName, "r")) {
1604  cReadLine ReadLine;
1605  char *s = ReadLine.Read(f);
1606  fclose(f);
1607  unsigned Major;
1608  unsigned Minor;
1609  if (s && 2 == sscanf(s, "%u:%u", &Major, &Minor)) {
1610  if (((Major << 8) | Minor) == st.st_rdev) {
1611  FileName = cString::sprintf("/sys/class/dvb/%s/device/subsystem_vendor", e->d_name);
1612  if ((f = fopen(FileName, "r")) != NULL) {
1613  if (char *s = ReadLine.Read(f))
1614  SubsystemId = strtoul(s, NULL, 0) << 16;
1615  fclose(f);
1616  }
1617  FileName = cString::sprintf("/sys/class/dvb/%s/device/subsystem_device", e->d_name);
1618  if ((f = fopen(FileName, "r")) != NULL) {
1619  if (char *s = ReadLine.Read(f))
1620  SubsystemId |= strtoul(s, NULL, 0);
1621  fclose(f);
1622  }
1623  break;
1624  }
1625  }
1626  }
1627  }
1628  }
1629  }
1630  }
1631  return SubsystemId;
1632 }