VDR-1.3.37: retuning -- possibly a fix for VDSB

Message ID 43BBDAC3.2030304@cadsoft.de
State New
Headers

Commit Message

Klaus Schmidinger Jan. 4, 2006, 2:25 p.m. UTC
  Klaus Schmidinger wrote:
> Klaus Schmidinger wrote:
> 
>> Thomas Rausch wrote:
>>
>>> After 6 hours I must say that my VDR did not bring yet back the 
>>> error. So far otherwise always. I want to hope that it functions now. :)
>>>
>>> In the LOG comes however repeatedly:
>>>
>>> Jan  3 18:15:43 cat vdr[16675]: ERROR: frontend 0 lost lock
>>> Jan  3 18:15:43 cat vdr[16675]: frontend 0 regained lock
>>> Jan  3 18:16:03 cat vdr[16675]: ERROR: frontend 0 lost lock
>>> Jan  3 18:16:03 cat vdr[16675]: frontend 0 regained lock
>>> Jan  3 18:16:25 cat vdr[16675]: ERROR: frontend 0 lost lock
>>> Jan  3 18:16:26 cat vdr[16675]: frontend 0 regained lock
>>> Jan  3 18:16:45 cat vdr[16675]: ERROR: frontend 0 lost lock
>>> Jan  3 18:16:45 cat vdr[16675]: frontend 0 regained lock
>>> Jan  3 18:17:06 cat vdr[16675]: ERROR: frontend 0 lost lock
>>> Jan  3 18:17:06 cat vdr[16675]: frontend 0 regained lock
>>
>>
>>
>> Apparently the FE_GET_EVENT ioctl() sometimes delivers a different
>> status value than FE_READ_STATUS. I ran some tests, comparing both
>> values and whenever VDR reported a lost lock, the current FE_READ_STATUS
>> value still had the FE_HAS_LOCK set correctly.
>>
>> Following an old recommendation of Holger Wächtler I finally changed
>> the frontend status handling so that it polls the frontend and reads
>> any pending event (but simply discards it), and then reads the actual
>> status via FE_READ_STATUS.
>>
>> With the attached patch (which is again a complete patch against
>> dvbdevice.c of version 1.3.37) I don't get these spurious "lost lock"
>> messages any more.
>>
>> I've also increased the timeout for polling the event queue to 10ms,
>> because 1ms seemed a little hard to me. This had no effect on the
>> "lost lock" problem, however, so the change in status handling really
>> is what fixed it.
>>
>> Please try whether this also helps in your case.
> 
> 
> Unfortunately this broke the CAM menu handling, so here's
> a slightly different version.
> 
> Sorry about that...

And sorry again...

Looks like the only thing that was really wrong was the 'continue'
in the (Status & FE_HAS_LOCK) branch.

So here's yet another version, hopefully this will be the last one.

Klaus
  

Patch

--- dvbdevice.c	2005/11/26 13:23:11	1.138
+++ dvbdevice.c	2006/01/04 14:20:13
@@ -47,6 +40,13 @@ 
 #define DEV_DVB_AUDIO     "audio"
 #define DEV_DVB_CA        "ca"
 
+#define DVBS_TUNE_TIMEOUT  2000 //ms
+#define DVBS_LOCK_TIMEOUT  2000 //ms
+#define DVBC_TUNE_TIMEOUT  5000 //ms
+#define DVBC_LOCK_TIMEOUT  2000 //ms
+#define DVBT_TUNE_TIMEOUT  9000 //ms
+#define DVBT_LOCK_TIMEOUT  2000 //ms
+
 class cDvbName {
 private:
   char buffer[PATH_MAX];
@@ -73,6 +73,9 @@ 
   enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
   int fd_frontend;
   int cardIndex;
+  int tuneTimeout;
+  int lockTimeout;
+  time_t lastTimeoutReport;
   fe_type_t frontendType;
   cCiHandler *ciHandler;
   cChannel channel;
@@ -81,7 +84,7 @@ 
   cMutex mutex;
   cCondVar locked;
   cCondVar newSet;
-  bool GetFrontendEvent(dvb_frontend_event &Event, int TimeoutMs = 0);
+  bool GetFrontendStatus(fe_status_t &Status, int TimeoutMs = 0);
   bool SetFrontend(void);
   virtual void Action(void);
 public:
@@ -98,6 +101,9 @@ 
   cardIndex = CardIndex;
   frontendType = FrontendType;
   ciHandler = CiHandler;
+  tuneTimeout = 0;
+  lockTimeout = 0;
+  lastTimeoutReport = 0;
   diseqcCommands = NULL;
   tunerStatus = tsIdle;
   if (frontendType == FE_QPSK)
@@ -125,6 +131,7 @@ 
   if (Tune)
      tunerStatus = tsSet;
   channel = *Channel;
+  lastTimeoutReport = 0;
   newSet.Broadcast();
 }
 
@@ -140,26 +147,20 @@ 
   return tunerStatus >= tsLocked;
 }
 
-bool cDvbTuner::GetFrontendEvent(dvb_frontend_event &Event, int TimeoutMs)
+bool cDvbTuner::GetFrontendStatus(fe_status_t &Status, int TimeoutMs)
 {
   if (TimeoutMs) {
-     struct pollfd pfd;
-     pfd.fd = fd_frontend;
-     pfd.events = POLLIN | POLLPRI;
-     do {
-        int stat = poll(&pfd, 1, TimeoutMs);
-        if (stat == 1)
-           break;
-        if (stat < 0) {
-           if (errno == EINTR)
-              continue;
-           esyslog("ERROR: frontend %d poll failed: %m", cardIndex);
-           }
+     cPoller Poller(fd_frontend);
+     if (Poller.Poll(TimeoutMs)) {
+        // just to clear the event queue - we'll read the actual status below
+        dvb_frontend_event Event;
+        CHECK(ioctl(fd_frontend, FE_GET_EVENT, &Event));
+        }
+     else
         return false;
-        } while (0);
      }
   do {
-     int stat = ioctl(fd_frontend, FE_GET_EVENT, &Event);
+     int stat = ioctl(fd_frontend, FE_READ_STATUS, &Status);
      if (stat == 0)
         return true;
      if (stat < 0) {
@@ -245,6 +246,9 @@ 
          Frontend.inversion = fe_spectral_inversion_t(channel.Inversion());
          Frontend.u.qpsk.symbol_rate = channel.Srate() * 1000UL;
          Frontend.u.qpsk.fec_inner = fe_code_rate_t(channel.CoderateH());
+
+         tuneTimeout = DVBS_TUNE_TIMEOUT;
+         lockTimeout = DVBS_LOCK_TIMEOUT;
          }
          break;
     case FE_QAM: { // DVB-C
@@ -256,6 +260,9 @@ 
          Frontend.u.qam.symbol_rate = channel.Srate() * 1000UL;
          Frontend.u.qam.fec_inner = fe_code_rate_t(channel.CoderateH());
          Frontend.u.qam.modulation = fe_modulation_t(channel.Modulation());
+
+         tuneTimeout = DVBC_TUNE_TIMEOUT;
+         lockTimeout = DVBC_LOCK_TIMEOUT;
          }
          break;
     case FE_OFDM: { // DVB-T
@@ -271,6 +278,9 @@ 
          Frontend.u.ofdm.transmission_mode = fe_transmit_mode_t(channel.Transmission());
          Frontend.u.ofdm.guard_interval = fe_guard_interval_t(channel.Guard());
          Frontend.u.ofdm.hierarchy_information = fe_hierarchy_t(channel.Hierarchy());
+
+         tuneTimeout = DVBT_TUNE_TIMEOUT;
+         lockTimeout = DVBT_LOCK_TIMEOUT;
          }
          break;
     default:
@@ -286,30 +296,54 @@ 
 
 void cDvbTuner::Action(void)
 {
-  dvb_frontend_event event;
+  cTimeMs Timer;
+  bool LostLock = false;
+  fe_status_t Status = (fe_status_t)0;
   while (Running()) {
-        bool hasEvent = GetFrontendEvent(event, 1);
-
+        fe_status_t NewStatus;
+        if (GetFrontendStatus(NewStatus, 10))
+           Status = NewStatus;
         cMutexLock MutexLock(&mutex);
         switch (tunerStatus) {
           case tsIdle:
                break;
           case tsSet:
-               if (hasEvent)
-                  continue;
                tunerStatus = SetFrontend() ? tsTuned : tsIdle;
+               Timer.Set(tuneTimeout);
                continue;
           case tsTuned:
-          case tsLocked:
-               if (hasEvent) {
-                  if (event.status & FE_REINIT) {
-                     tunerStatus = tsSet;
-                     esyslog("ERROR: frontend %d was reinitialized - re-tuning", cardIndex);
+               if (Timer.TimedOut()) {
+                  tunerStatus = tsSet;
+                  diseqcCommands = NULL;
+                  if (time(NULL) - lastTimeoutReport > 60) { // let's not get too many of these
+                     esyslog("ERROR: frontend %d timed out while tuning", cardIndex);
+                     lastTimeoutReport = time(NULL);
                      }
-                  if (event.status & FE_HAS_LOCK) {
-                     tunerStatus = tsLocked;
-                     locked.Broadcast();
+                  continue;
+                  }
+          case tsLocked:
+               if (Status & FE_REINIT) {
+                  tunerStatus = tsSet;
+                  diseqcCommands = NULL;
+                  esyslog("ERROR: frontend %d was reinitialized", cardIndex);
+                  lastTimeoutReport = 0;
+                  continue;
+                  }
+               else if (Status & FE_HAS_LOCK) {
+                  if (LostLock) {
+                     esyslog("frontend %d regained lock", cardIndex);
+                     LostLock = false;
                      }
+                  tunerStatus = tsLocked;
+                  locked.Broadcast();
+                  lastTimeoutReport = 0;
+                  }
+               else if (tunerStatus == tsLocked) {
+                  LostLock = true;
+                  esyslog("ERROR: frontend %d lost lock", cardIndex);
+                  tunerStatus = tsTuned;
+                  Timer.Set(lockTimeout);
+                  lastTimeoutReport = 0;
                   continue;
                   }
           }