--- dvbdevice.c	2013-08-23 11:52:14.000000000 +0200
+++ dvbdevice.c.edit	2013-10-13 10:58:44.262951623 +0200
@@ -285,9 +285,10 @@
 private:
   static cMutex bondMutex;
   enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
+  bool SendDiseqc;
   int frontendType;
   const cDvbDevice *device;
-  int fd_frontend;
+  mutable int fd_frontend;
   int adapter, frontend;
   uint32_t subsystemId;
   int tuneTimeout;
@@ -298,9 +299,10 @@
   const cScr *scr;
   bool lnbPowerTurnedOn;
   eTunerStatus tunerStatus;
-  cMutex mutex;
+  mutable cMutex mutex;
   cCondVar locked;
   cCondVar newSet;
+  dvb_diseqc_master_cmd diseqc_cmd;
   cDvbTuner *bondedTuner;
   bool bondedMaster;
   bool SetFrontendType(const cChannel *Channel);
@@ -313,6 +315,10 @@
   void ResetToneAndVoltage(void);
   bool SetFrontend(void);
   virtual void Action(void);
+
+  mutable bool isIdle;
+  bool OpenFrontend(void) const;
+  bool CloseFrontend(void);
 public:
   cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend);
   virtual ~cDvbTuner();
@@ -327,12 +333,18 @@
   bool Locked(int TimeoutMs = 0);
   int GetSignalStrength(void) const;
   int GetSignalQuality(void) const;
+  bool SetIdle(bool Idle);
+  bool IsIdle(void) const { return isIdle; }
+  bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd);
+private:
+  int GetCurrentDeliverySystem(void);
   };
 
 cMutex cDvbTuner::bondMutex;
 
 cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend)
 {
+  SendDiseqc = false;
   frontendType = SYS_UNDEFINED;
   device = Device;
   fd_frontend = Fd_Frontend;
@@ -348,6 +360,7 @@
   tunerStatus = tsIdle;
   bondedTuner = NULL;
   bondedMaster = false;
+  isIdle = false;
   SetDescription("tuner on frontend %d/%d", adapter, frontend);
   Start();
 }
@@ -365,6 +378,8 @@
      ExecuteDiseqc(lastDiseqc, &Frequency);
      }
   */
+  if (device && device->IsSubDevice())
+     CloseFrontend();
 }
 
 bool cDvbTuner::Bond(cDvbTuner *Tuner)
@@ -509,6 +524,8 @@
 
 void cDvbTuner::ClearEventQueue(void) const
 {
+  if (!OpenFrontend())
+     return;
   cPoller Poller(fd_frontend);
   if (Poller.Poll(TUNER_POLL_TIMEOUT)) {
      dvb_frontend_event Event;
@@ -531,32 +548,104 @@
 
 //#define DEBUG_SIGNALSTRENGTH
 //#define DEBUG_SIGNALQUALITY
-
+#define LOCK_THRESHOLD 5 // indicates that all 5 FE_HAS_* flags are set
+// Inizio Modifica
 int cDvbTuner::GetSignalStrength(void) const
 {
-  ClearEventQueue();
-  uint16_t Signal;
-  while (1) {
-        if (ioctl(fd_frontend, FE_READ_SIGNAL_STRENGTH, &Signal) != -1)
-           break;
-        if (errno != EINTR)
-           return -1;
+  fe_status_t Status;
+  if (GetFrontendStatus(Status)) {
+     // 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...
+     if ((Status & FE_HAS_LOCK) == 0) {
+        if ((Status & FE_HAS_SIGNAL) == 0)
+           return 0;
+        if ((Status & FE_HAS_CARRIER) == 0)
+           return 1;
+        if ((Status & FE_HAS_VITERBI) == 0)
+           return 2;
+        if ((Status & FE_HAS_SYNC) == 0)
+           return 3;
+        return 4;
         }
-  uint16_t MaxSignal = 0xFFFF; // Let's assume the default is using the entire range.
-  // Use the subsystemId to identify individual devices in case they need
-  // special treatment to map their Signal value into the range 0...0xFFFF.
-  switch (subsystemId) {
-    case 0x13C21019: // TT-budget S2-3200 (DVB-S/DVB-S2)
-    case 0x1AE40001: // TechniSat SkyStar HD2 (DVB-S/DVB-S2)
-                     MaxSignal = 670; break;
-    }
-  int s = int(Signal) * 100 / MaxSignal;
-  if (s > 100)
-     s = 100;
 #ifdef DEBUG_SIGNALSTRENGTH
-  fprintf(stderr, "FE %d/%d: %08X S = %04X %04X %3d%%\n", adapter, frontend, subsystemId, MaxSignal, Signal, s);
+     bool HasSignal = true;
 #endif
-  return s;
+     uint16_t Signal;
+     while (1) {
+          if (ioctl(fd_frontend, FE_READ_SIGNAL_STRENGTH, &Signal) != -1)
+              break;
+           if (errno != EINTR) {
+              Signal = 0xFFFF;
+#ifdef DEBUG_SIGNALSTRENGTH
+              HasSignal = false;
+#endif
+              break;
+              }
+           }
+#ifdef DEBUG_SIGNALSTRENGTH
+     bool HasBer = true;
+#endif
+     uint32_t Ber;
+     while (1) {
+           if (ioctl(fd_frontend, FE_READ_BER, &Ber) != -1)
+              break;
+           if (errno != EINTR) {
+              Ber = 0;
+#ifdef DEBUG_SIGNALSTRENGTH
+              HasBer = false;
+#endif
+              break;
+              }
+           }
+#ifdef DEBUG_SIGNALSTRENGTH
+     bool HasUnc = true;
+#endif
+     uint32_t Unc;
+     while (1) {
+           if (ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &Unc) != -1)
+              break;
+           if (errno != EINTR) {
+              Unc = 0;
+#ifdef DEBUG_SIGNALSTRENGTH
+              HasUnc = false;
+#endif
+              break;
+              }
+           }
+     uint16_t MinSignal = 0x0000;
+     uint16_t MaxSignal = 0xFFFF; // Let's assume the default is using the entire range.
+     // Use the subsystemId to identify individual devices in case they need
+     // special treatment to map their Snr value into the range 0...0xFFFF.
+     switch (subsystemId) {
+       case 0x13C21019: // TT-budget S2-3200 (DVB-S/DVB-S2)
+       case 0x1AE40001: // TechniSat SkyStar HD2 (DVB-S/DVB-S2)
+                        if (frontendType == SYS_DVBS2) {
+                           MinSignal = 10;
+                           MaxSignal = 70;
+                           }
+                        else
+//                           MaxSignal = 200;
+                           MaxSignal = 670;
+
+                        break;
+       case 0x20130245: // PCTV Systems PCTV 73ESE
+       case 0x2013024F: // PCTV Systems nanoStick T2 290e
+//                        MaxSignal = 255; break;
+                            MaxSignal = 670;
+
+       }
+     int a = int(constrain(Signal, MinSignal, MaxSignal)) * 350 / (MaxSignal - MinSignal);
+     int b = 100 - (Unc * 10 + (Ber / 256) * 5);
+     if (b < 0)
+        b = 0;
+     int s = LOCK_THRESHOLD + a * b * (100 - LOCK_THRESHOLD) / 100 / 100;
+     if (s > 100)
+        s = 100;
+    #ifdef DEBUG_SIGNALSTRENGTH
+     fprintf(stderr, "FE %d/%d: %08X S = %04X %04X %d %5d %5d %3d%%\n", adapter, frontend, subsystemId, MaxSignal, Signal, HasSignal, HasBer ? int(Ber) : -1, HasUnc ? int(Unc) : -1, s);
+#endif
+     return s;
+     }
+  return -1;
 }
 
 #define LOCK_THRESHOLD 5 // indicates that all 5 FE_HAS_* flags are set
@@ -655,6 +744,35 @@
   return -1;
 }
 
+int cDvbTuner::GetCurrentDeliverySystem()
+{
+  dtv_property Frontend[1];
+  memset(&Frontend, 0, sizeof(Frontend));
+  dtv_properties CmdSeq;
+  memset(&CmdSeq, 0, sizeof(CmdSeq));
+  CmdSeq.props = Frontend;
+  Frontend[0].cmd = DTV_DELIVERY_SYSTEM;
+  Frontend[0].u.data = 0;
+  if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) < 0) {
+     esyslog("ERROR: frontend %d/%d: %m", adapter, frontend);
+     return SYS_UNDEFINED;
+     }
+  return Frontend[0].u.data;
+}
+
+bool cDvbTuner::SendDiseqcCmd(dvb_diseqc_master_cmd cmd)
+{
+  cMutexLock MutexLock(&mutex);
+  int frontendType = GetCurrentDeliverySystem();
+  if ((frontendType != SYS_DVBS && frontendType != SYS_DVBS2) || SendDiseqc)
+    return false;
+  diseqc_cmd=cmd;
+  SendDiseqc=true;
+  newSet.Broadcast();
+  return true;
+}
+
+
 static unsigned int FrequencyToHz(unsigned int f)
 {
   while (f && f < 1000000)
@@ -719,6 +837,8 @@
 
 bool cDvbTuner::SetFrontend(void)
 {
+  if (!OpenFrontend())
+     return false;
 #define MAXFRONTENDCMDS 16
 #define SETCMD(c, d) { Frontend[CmdSeq.num].cmd = (c);\
                        Frontend[CmdSeq.num].u.data = (d);\
@@ -870,10 +990,16 @@
   bool LostLock = false;
   fe_status_t Status = (fe_status_t)0;
   while (Running()) {
-        fe_status_t NewStatus;
-        if (GetFrontendStatus(NewStatus))
-           Status = NewStatus;
+        if (!isIdle) {
+           fe_status_t NewStatus;
+           if (GetFrontendStatus(NewStatus))
+              Status = NewStatus;
+           }
         cMutexLock MutexLock(&mutex);
+        if (SendDiseqc) {
+           CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &diseqc_cmd));
+           SendDiseqc=false;
+           }
         int WaitTime = 1000;
         switch (tunerStatus) {
           case tsIdle:
@@ -925,6 +1051,40 @@
         }
 }
 
+bool cDvbTuner::SetIdle(bool Idle)
+{
+  if (isIdle == Idle)
+     return true;
+  isIdle = Idle;
+  if (Idle)
+     return CloseFrontend();
+  return OpenFrontend();
+}
+
+bool cDvbTuner::OpenFrontend(void) const
+{
+  if (fd_frontend >= 0)
+     return true;
+  cMutexLock MutexLock(&mutex);
+  fd_frontend = cDvbDevice::DvbOpen(DEV_DVB_FRONTEND, adapter, frontend, O_RDWR | O_NONBLOCK);
+  if (fd_frontend < 0)
+     return false;
+  isIdle = false;
+  return true;
+}
+
+bool cDvbTuner::CloseFrontend(void)
+{
+  if (fd_frontend < 0)
+     return true;
+  cMutexLock MutexLock(&mutex);
+  tunerStatus = tsIdle;
+  newSet.Broadcast();
+  close(fd_frontend);
+  fd_frontend = -1;
+  return true;
+}
+
 // --- cDvbSourceParam -------------------------------------------------------
 
 class cDvbSourceParam : public cSourceParam {
@@ -1010,7 +1170,8 @@
   NULL
   };
 
-cDvbDevice::cDvbDevice(int Adapter, int Frontend)
+cDvbDevice::cDvbDevice(int Adapter, int Frontend, cDevice *ParentDevice)
+:cDevice(ParentDevice)
 {
   adapter = Adapter;
   frontend = Frontend;
@@ -1028,9 +1189,8 @@
 
   // Common Interface:
 
-  fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR);
-  if (fd_ca >= 0)
-     ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca);
+  int fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR);
+  ciAdapter = cDvbCiAdapter::CreateCiAdapter(parentDevice ? parentDevice : this, fd_ca, adapter, frontend);
 
   // The DVR device (will be opened and closed as needed):
 
@@ -1274,7 +1434,11 @@
          if (d >= 0) {
             int ErrorDevice = 0;
             if (cDevice *Device1 = cDevice::GetDevice(i)) {
+               if (Device1->HasSubDevice())
+                  Device1 = Device1->SubDevice();
                if (cDevice *Device2 = cDevice::GetDevice(d)) {
+                  if (Device2->HasSubDevice())
+                     Device2 = Device2->SubDevice();
                   if (cDvbDevice *DvbDevice1 = dynamic_cast<cDvbDevice *>(Device1)) {
                      if (cDvbDevice *DvbDevice2 = dynamic_cast<cDvbDevice *>(Device2)) {
                         if (!DvbDevice1->Bond(DvbDevice2))
@@ -1308,7 +1472,10 @@
 void cDvbDevice::UnBondDevices(void)
 {
   for (int i = 0; i < cDevice::NumDevices(); i++) {
-      if (cDvbDevice *d = dynamic_cast<cDvbDevice *>(cDevice::GetDevice(i)))
+      cDevice *dev = cDevice::GetDevice(i);
+      if (dev && dev->HasSubDevice())
+         dev = dev->SubDevice();
+      if (cDvbDevice *d = dynamic_cast<cDvbDevice *>(dev))
          d->UnBond();
       }
 }
@@ -1362,6 +1529,26 @@
   return true;
 }
 
+bool cDvbDevice::SetIdleDevice(bool Idle, bool TestOnly)
+{
+  if (TestOnly) {
+     if (ciAdapter)
+        return ciAdapter->SetIdle(Idle, true);
+     return true;
+     }
+  if (!dvbTuner->SetIdle(Idle))
+     return false;
+  if (ciAdapter && !ciAdapter->SetIdle(Idle, false)) {
+     dvbTuner->SetIdle(!Idle);
+     return false;
+     }
+  if (Idle)
+     StopSectionHandler();
+  else
+     StartSectionHandler();
+  return true;
+}
+
 bool cDvbDevice::HasCi(void)
 {
   return ciAdapter;
@@ -1447,6 +1634,10 @@
 
 bool cDvbDevice::ProvidesSource(int Source) const
 {
+  if (Setup.ChannelBlocker == 1) {
+      if (IsPrimaryDevice()) return false;
+      //isyslog("ChannelBlocker aktive on primary Interface");
+  }
   int type = Source & cSource::st_Mask;
   return type == cSource::stNone
       || type == cSource::stAtsc  && ProvidesDeliverySystem(SYS_ATSC)
@@ -1531,7 +1722,7 @@
 
 bool cDvbDevice::ProvidesEIT(void) const
 {
-  return dvbTuner != NULL;
+  return !IsIdle() && (dvbTuner != NULL) && !dvbTuner->IsIdle() && ((ciAdapter == NULL) || !ciAdapter->IsIdle());
 }
 
 int cDvbDevice::NumProvidedSystems(void) const
@@ -1576,6 +1767,11 @@
   return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false;
 }
 
+bool cDvbDevice::SendDiseqcCmd(dvb_diseqc_master_cmd cmd)
+{
+  return dvbTuner->SendDiseqcCmd(cmd);
+}
+
 void cDvbDevice::SetTransferModeForDolbyDigital(int Mode)
 {
   setTransferModeForDolbyDigital = Mode;
@@ -1585,8 +1781,12 @@
 {
   CloseDvr();
   fd_dvr = DvbOpen(DEV_DVB_DVR, adapter, frontend, O_RDONLY | O_NONBLOCK, true);
-  if (fd_dvr >= 0)
-     tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1);
+  if (fd_dvr >= 0) {
+     if (ciAdapter)
+        tsBuffer = ciAdapter->GetTSBuffer(fd_dvr);
+     if (tsBuffer == NULL)
+        tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1);
+     }
   return fd_dvr >= 0;
 }
 