VDR-1.3.37: retuning -- possibly a fix for VDSB
Commit Message
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.
Klaus
@@ -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,55 @@
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;
+ continue;
+ }
+ else if (tunerStatus == tsLocked) {
+ LostLock = true;
+ esyslog("ERROR: frontend %d lost lock", cardIndex);
+ tunerStatus = tsTuned;
+ Timer.Set(lockTimeout);
+ lastTimeoutReport = 0;
continue;
}
}