Mailing List archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[vdr] Re: Fixed tuner lock detection



Klaus Schmidinger wrote:
> Wolfgang Fritz wrote:
> 
>>...
>>
>>>         if (tunerStatus != tsIdle) {
>>>            dvb_frontend_event event;
>>>-           if (ioctl(fd_frontend, FE_GET_EVENT, &event) == 0) {
>>>-              if (event.status & FE_REINIT) {
>>>-                 tunerStatus = tsSet;
>>>-                 esyslog("ERROR: frontend %d was reinitialized - re-tuning", cardIndex);
>>>+           while (ioctl(fd_frontend, FE_GET_EVENT, &event) == 0) {
>>>+                 if (event.status & FE_REINIT) {
>>>+                    tunerStatus = tsSet;
>>>+                    esyslog("ERROR: frontend %d was reinitialized - re-tuning", cardIndex);
>>>+                    }
>>>+                 if (event.status & FE_HAS_LOCK) {
>>
>>Question 1: Are you sure the drivers always signal a valid lock on the
>>first "rising edge" of FE_HAS_LOCK? They didn't in the past (the "spike
>>problem").
> 
> 
> Well, if the driver sends an event that indicates FE_HAS_LOCK I'd say
> it's ok for the application to assume that there actually _is_ a lock.
> Anyting else is a driver bug and should be fixed there. IIRC there was
> a fix in the driver for this recently.
> 
OK, maybe I'm too paranoid here. But looking the increasing number of
supported frontends, the different "flavours" of the DVB drivers (2.4 /
2.6 kernel, CVS...) I find it quite hard to believe that all of them are
bug free.

> The problem in the old code in VDR was that it has fetched the current
> status with ioctl(fd_frontend, FE_READ_STATUS, &status) instead of reacting
> on FE events. My tests showed that after tuning to a different transponder,
> the FE_READ_STATUS immediately returned FE_HAS_LOCK, while the actual
> event arrived somewhat later. I guess the FE_READ_STATUS call (sometimes)
> delivered the status of the _previous_ lock.
>
I observed that too, but in my opinion the FE_GET_EVENT was also wrong.
This was the same bug which showed the FE_HAS_LOCK spikes (abt 100 ms)
when switching from a locked channel to channel that could not lock
(e.g. the "channel 0"). When switching from a locked channel to a valid
channel, this wrong FE_HAS_LOCK was combined with the valid FE_HAS_LOCK.

> 
>>Question 2: How is the case of a loss of lock handled (May occur e.g. in
>>bad weather conditions?). This case is not handled in
>>DvbTuner::Action(). Do we get a VDSB error and an emergency exit?
> 
> 
> There hasn't been any special code regarding loss of lock so far, and this
> modification doesn't introduce one, either. I did try to do FE_READ_STATUS
> in every loop and wanted to retune if the lock was lost, but apparently
> the FE_HAS_LOCK sometimes gets 0 even though there is a perfect lock.
> Maybe this is caused by the problem with the ves1x93, which sometimes
> returns bogus status values.
> 

Mmh, but this is a driver bug and should be fixed in the driver,
shouldn't it ;-)

Anyway, FYI I attach the version of dvbdevice.c I'm currently running.
It has a a lock/loss of lock detection which suppresses short glitches.
The diff is against 1.3.12.

> Anyway, if there is no more data while a recording is going on, there
> will still be an emergency exit. This fix here is only supposed to prevent
> the DVB card from getting into an unstable condition by starting filters
> before there is an actual lock.
>

Would it be possible to introduce a "mild form" of emergency exit, say,
if a recording gets no more data, it declares an emergency, but instead
of triggering an immediate restart, check if there are other recordings
running without problems (on other cards maybe) and delay the restart
until these recordings have finished? Unfortunately I'm not familiar
enough with the VDR code and internals yet to test this.

> 
>>...
>>Fine! I am using a similar patch since quite some time now without problems.
> 
> 
> I hope _this_ one will also work for you. It's mainly everything from
> the existing suggestions that made sense to me.
> 

It's the same approach I'm using: wait for a certain time to get a lock,
 if you can't get one, return with error. So I'm sure it will work for me.

I'll try to switch to 1.3.13 this weekend an test this patch.

Wolfgang

> Klaus
> 
> 

Index: dvbdevice.c
===================================================================
--- dvbdevice.c	(Revision 16)
+++ dvbdevice.c	(Revision 93)
@@ -35,8 +35,16 @@
 
 #define DO_REC_AND_PLAY_ON_PRIMARY_DEVICE 1
 #define DO_MULTIPLE_RECORDINGS 1
-//#define WAIT_FOR_LOCK_AFTER_TUNING 1
+#define WAIT_FOR_LOCK_AFTER_TUNING 1
 
+#define USE_ALTERNATE_CDVBTUNER_ACTION
+#define EXTENDED_DEBUGGING_OUTPUT
+#ifdef EXTENDED_DEBUGGING_OUTPUT
+#define DSYSLOG(a...) dsyslog(a)
+#else
+#define DSYSLOG(a...)
+#endif
+ 
 #define DEV_VIDEO         "/dev/video"
 #define DEV_DVB_ADAPTER   "/dev/dvb/adapter"
 #define DEV_DVB_OSD       "osd"
@@ -248,13 +256,136 @@
   return true;
 }
 
+#ifdef USE_ALTERNATE_CDVBTUNER_ACTION
+#define LOCK_TIMEOUT 300
+#define UNLOCK_TIMEOUT 300
 void cDvbTuner::Action(void)
 {
+#ifdef EXTENDED_DEBUGGING_OUTPUT
+  int last;
+  last = time_ms ();
+#endif
   active = true;
+  int timer = 0;
   while (active) {
         cMutexLock MutexLock(&mutex);
         if (tunerStatus == tsSet)
            tunerStatus = SetFrontend() ? tsTuned : tsIdle;
+
+        bool fast = (tunerStatus == tsTuned) || (ciHandler && (time(NULL) - startTime < 20));
+        if (tunerStatus != tsIdle) {
+	   fe_status_t status = fe_status_t(0);
+           dvb_frontend_event event;
+	   struct pollfd pd;
+	   pd.fd = fd_frontend;
+	   pd.events = POLLIN | POLLPRI;
+	   int pr;
+	   /* poll call */
+	   pr = poll (&pd, 1, fast ? 100 : 1000);
+	   if (pr == 1) {
+	      if (ioctl(fd_frontend, FE_GET_EVENT, &event) == 0) {
+		 status = event.status;
+#ifdef EXTENDED_DEBUGGING_OUTPUT
+		 int now = time_ms ();
+		 dsyslog ("Event on frontend %d (channel \"%s\"): %02x dt =%8d ms", 
+			  cardIndex, channel.Name(), event.status, now - last);
+		 last = now;
+#endif
+	      }
+           }
+	   else {
+	       if (pr == 0) {
+		 CHECK(ioctl(fd_frontend, FE_READ_STATUS, &status));
+	       }
+	       else {
+		   dsyslog ("cDvbTuner::%s: poll error\n", __FUNCTION__);
+		   continue;
+	       }
+	   }
+	   if (status & FE_REINIT) {
+	       tunerStatus = tsSet;
+	       esyslog("ERROR: frontend %d was reinitialized - re-tuning", cardIndex);
+	       continue;
+	   }
+	   if (tunerStatus != tsLocked) {
+	       if (status & FE_HAS_LOCK) {
+		   if (timer == 0) {
+#ifdef EXTENDED_DEBUGGING_OUTPUT
+		       dsyslog ("Frontend %d (channel \"%s\") first lock", cardIndex, channel.Name());
+#endif
+		       timer = time_ms ();
+		   }
+		   else {
+		       if (time_ms () - timer > LOCK_TIMEOUT) {
+			   timer = 0;
+			   tunerStatus = tsLocked;
+#ifdef EXTENDED_DEBUGGING_OUTPUT
+			   dsyslog ("Frontend %d (channel \"%s\") locked", 
+				   cardIndex, channel.Name());
+#endif
+		       }
+		   }
+	       }
+	       else {
+		   timer = 0;
+	       }
+           }
+	   else {
+	       if (!(status & FE_HAS_LOCK)) {
+		   if (timer == 0) {
+#ifdef EXTENDED_DEBUGGING_OUTPUT
+		       dsyslog ("Frontend %d (channel \"%s\") first no-lock", cardIndex, channel.Name());
+#endif
+		       timer = time_ms ();
+		   }
+		   else {
+		       if (time_ms () - timer > UNLOCK_TIMEOUT) {
+			   timer = 0;
+			   tunerStatus = tsSet;
+#ifdef EXTENDED_DEBUGGING_OUTPUT
+			   dsyslog ("Frontend %d (channel \"%s\") lost lock, retuning", 
+				   cardIndex, channel.Name());
+#endif
+		       }
+		   }
+	       }
+	       else {
+		   timer = 0;
+	       }
+           }
+	}
+	  
+        if (ciHandler) {
+           if (ciHandler->Process() && useCa) {
+              if (tunerStatus == tsLocked) {
+                 for (int Slot = 0; Slot < ciHandler->NumSlots(); Slot++) {
+                     cCiCaPmt CaPmt(channel.Source(), channel.Transponder(), channel.Sid(), ciHandler->GetCaSystemIds(Slot));
+                     if (CaPmt.Valid()) {
+                        CaPmt.AddPid(channel.Vpid(), 2);
+                        CaPmt.AddPid(channel.Apid1(), 4);
+                        CaPmt.AddPid(channel.Apid2(), 4);
+                        CaPmt.AddPid(channel.Dpid1(), 0);
+                        if (ciHandler->SetCaPmt(CaPmt, Slot)) {
+                           tunerStatus = tsCam;
+                           startTime = 0;
+                           }
+                        }
+                     }
+                 }
+              }
+           else if (tunerStatus > tsLocked)
+              tunerStatus = tsLocked;
+           }
+        }
+}
+#else
+void cDvbTuner::Action(void)
+{
+  active = true;
+  while (active) {
+        cMutexLock MutexLock(&mutex);
+        if (tunerStatus == tsSet)
+           tunerStatus = SetFrontend() ? tsTuned : tsIdle;
         if (tunerStatus == tsTuned) {
            fe_status_t status = fe_status_t(0);
            CHECK(ioctl(fd_frontend, FE_READ_STATUS, &status));
@@ -296,6 +427,7 @@
         newSet.TimedWait(mutex, (ciHandler && (time(NULL) - startTime < 20)) ? 100 : 1000);
         }
 }
+#endif
 
 // --- cDvbDevice ------------------------------------------------------------
 
@@ -578,6 +710,7 @@
 bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
 {
   if (Handle->pid) {
+     DSYSLOG ("SetPid: Setting pid %d (type %d) On = %d", Handle->pid, Type, On);
      dmx_pes_filter_params pesFilterParams;
      memset(&pesFilterParams, 0, sizeof(pesFilterParams));
      if (On) {
@@ -709,6 +842,7 @@
 
 bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
 {
+  DSYSLOG ("SetChannelDevice: channel %d, LiveView=%d", Channel->Number(), LiveView);
   bool IsEncrypted = Channel->Ca() > CACONFBASE && !ciHandler; // only LL-firmware can do non-live CA channels
 
   bool DoTune = !dvbTuner->IsTunedTo(Channel);
@@ -754,6 +888,10 @@
   time_t t0 = time(NULL);
   while (!dvbTuner->Locked() && time(NULL) - t0 < 5)
         usleep(100);
+  if (!dvbTuner->Locked()){
+    esyslog("ERROR: failed to tune to channel %d \"%s\"", Channel->Number(), Channel->Name());
+    return false;
+  }
 #endif
   // PID settings:
 

Home | Main Index | Thread Index