#define FP_IOCTL_SET_RTC 0x101
#define FP_IOCTL_GET_RTC 0x102
+#define TIME_UPDATE_INTERVAL (30*60*1000)
+
static time_t prev_time;
void setRTC(time_t time)
{
- int fd = open("/dev/dbox/fp0", O_RDWR);
- if ( fd >= 0 )
+ FILE *f = fopen("/proc/stb/fp/rtc", "w");
+ if (f)
{
- if ( ::ioctl(fd, FP_IOCTL_SET_RTC, (void*)&time ) < 0 )
- eDebug("FP_IOCTL_SET_RTC failed(%m)");
- else
+ if (fprintf(f, "%u", (unsigned int)time))
prev_time = time;
- close(fd);
+ else
+ eDebug("write /proc/stb/fp/rtc failed (%m)");
+ fclose(f);
+ }
+ else
+ {
+ int fd = open("/dev/dbox/fp0", O_RDWR);
+ if ( fd >= 0 )
+ {
+ if ( ::ioctl(fd, FP_IOCTL_SET_RTC, (void*)&time ) < 0 )
+ eDebug("FP_IOCTL_SET_RTC failed(%m)");
+ else
+ prev_time = time;
+ close(fd);
+ }
}
}
time_t getRTC()
{
time_t rtc_time=0;
- int fd = open("/dev/dbox/fp0", O_RDWR);
- if ( fd >= 0 )
+ FILE *f = fopen("/proc/stb/fp/rtc", "r");
+ if (f)
+ {
+ // sanity check to detect corrupt atmel firmware
+ unsigned int tmp;
+ if (fscanf(f, "%u", &tmp) != 1)
+ eDebug("read /proc/stb/fp/rtc failed (%m)");
+ else
+ rtc_time=tmp;
+ fclose(f);
+ }
+ else
{
- if ( ::ioctl(fd, FP_IOCTL_GET_RTC, (void*)&rtc_time ) < 0 )
- eDebug("FP_IOCTL_GET_RTC failed(%m)");
- close(fd);
+ int fd = open("/dev/dbox/fp0", O_RDWR);
+ if ( fd >= 0 )
+ {
+ if ( ::ioctl(fd, FP_IOCTL_GET_RTC, (void*)&rtc_time ) < 0 )
+ eDebug("FP_IOCTL_GET_RTC failed(%m)");
+ close(fd);
+ }
}
return rtc_time != prev_time ? rtc_time : 0;
}
-int fromBCD(int bcd)
-{
- if ((bcd&0xF0)>=0xA0)
- return -1;
- if ((bcd&0xF)>=0xA)
- return -1;
- return ((bcd&0xF0)>>4)*10+(bcd&0xF);
-}
-
-time_t parseDVBtime(__u8 t1, __u8 t2, __u8 t3, __u8 t4, __u8 t5)
+time_t parseDVBtime(__u8 t1, __u8 t2, __u8 t3, __u8 t4, __u8 t5, __u16 *hash)
{
tm t;
t.tm_sec=fromBCD(t5);
t.tm_isdst = 0;
t.tm_gmtoff = 0;
+ if (hash) {
+ *hash = t.tm_hour * 60 + t.tm_min;
+ *hash |= t.tm_mday << 11;
+ }
+
return timegm(&t);
}
-TDT::TDT(eDVBChannel *chan)
- :chan(chan)
+TDT::TDT(eDVBChannel *chan, int update_count)
+ :chan(chan), m_interval_timer(eTimer::create()), update_count(update_count)
{
CONNECT(tableReady, TDT::ready);
- CONNECT(m_interval_timer.timeout, TDT::start);
+ CONNECT(m_interval_timer->timeout, TDT::start);
if (chan)
- chan->getDemux(demux);
+ chan->getDemux(demux, 0);
}
void TDT::ready(int error)
{
- eDVBLocalTimeHandler::getInstance()->updateTime(error, chan);
+ eDVBLocalTimeHandler::getInstance()->updateTime(error, chan, ++update_count);
}
-int TDT::createTable(int nr, const __u8 *data, unsigned int max)
+int TDT::createTable(unsigned int nr, const __u8 *data, unsigned int max)
{
- if ( data && data[0] == 0x70 || data[0] == 0x73 )
+ if ( data && (data[0] == 0x70 || data[0] == 0x73 ))
{
int length = ((data[1] & 0x0F) << 8) | data[2];
- if ( length >= 8 )
+ if ( length >= 5 )
{
time_t tptime = parseDVBtime(data[3], data[4], data[5], data[6], data[7]);
- eDVBLocalTimeHandler::getInstance()->updateTime(tptime, chan);
+ if (tptime && tptime != -1)
+ eDVBLocalTimeHandler::getInstance()->updateTime(tptime, chan, update_count);
error=0;
return 1;
}
if ( chan )
{
eDVBTableSpec spec;
- spec.pid = TimeAndDateTable::PID;
- spec.tid = TimeAndDateTable::TID;
+ spec.pid = TimeAndDateSection::PID;
+ spec.tid = TimeAndDateSection::TID;
spec.tid_mask = 0xFC;
- spec.timeout = TimeAndDateTable::TIMEOUT;
+ spec.timeout = TimeAndDateSection::TIMEOUT;
spec.flags= eDVBTableSpec::tfAnyVersion |
eDVBTableSpec::tfHaveTID |
eDVBTableSpec::tfHaveTIDMask |
- eDVBTableSpec::tfCheckCRC |
eDVBTableSpec::tfHaveTimeout;
if ( demux )
eGTable::start( demux, spec );
void TDT::startTimer( int interval )
{
- m_interval_timer.start(interval, true);
+ m_interval_timer->start(interval, true);
}
eDVBLocalTimeHandler *eDVBLocalTimeHandler::instance;
DEFINE_REF(eDVBLocalTimeHandler);
eDVBLocalTimeHandler::eDVBLocalTimeHandler()
- :m_time_ready(false), m_time_difference(0)
+ :m_use_dvb_time(false), m_updateNonTunedTimer(eTimer::create(eApp)), m_time_ready(false)
{
if ( !instance )
instance=this;
else
{
res_mgr->connectChannelAdded(slot(*this,&eDVBLocalTimeHandler::DVBChannelAdded), m_chanAddedConn);
- res_mgr->connectChannelRemoved(slot(*this,&eDVBLocalTimeHandler::DVBChannelRemoved), m_chanRemovedConn);
- res_mgr->connectChannelRunning(slot(*this,&eDVBLocalTimeHandler::DVBChannelRunning), m_chanRunningConn);
+ time_t now = time(0);
+ if ( now < 1072224000 ) // 01.01.2004
+ eDebug("RTC not ready... wait for transponder time");
+ else // inform all who's waiting for valid system time..
+ {
+ eDebug("Use valid Linux Time :) (RTC?)");
+ m_time_ready = true;
+ /*emit*/ m_timeUpdated();
+ }
}
+ CONNECT(m_updateNonTunedTimer->timeout, eDVBLocalTimeHandler::updateNonTuned);
}
eDVBLocalTimeHandler::~eDVBLocalTimeHandler()
{
instance=0;
- for (std::map<iDVBChannel*, TDT*>::iterator it=m_active_tables.begin(); it != m_active_tables.end(); ++it)
- delete it->second;
+ if (ready())
+ {
+ eDebug("set RTC to previous valid time");
+ setRTC(::time(0));
+ }
}
void eDVBLocalTimeHandler::readTimeOffsetData( const char* filename )
}
}
-void eDVBLocalTimeHandler::updateTime( time_t tp_time, eDVBChannel *chan )
+void eDVBLocalTimeHandler::setUseDVBTime(bool b)
{
+ if (m_use_dvb_time != b) {
+ if (m_use_dvb_time) {
+ eDebug("[eDVBLocalTimeHandler] disable sync local time with transponder time!");
+ std::map<iDVBChannel*, channel_data>::iterator it =
+ m_knownChannels.begin();
+ for (; it != m_knownChannels.end(); ++it) {
+ if (it->second.m_prevChannelState == iDVBChannel::state_ok)
+ it->second.tdt = 0;
+ }
+ }
+ else {
+ eDebug("[eDVBLocalTimeHandler] enable sync local time with transponder time!");
+ std::map<iDVBChannel*, channel_data>::iterator it =
+ m_knownChannels.begin();
+ for (; it != m_knownChannels.end(); ++it) {
+ if (it->second.m_prevChannelState == iDVBChannel::state_ok) {
+ it->second.tdt = new TDT(it->second.channel);
+ it->second.tdt->start();
+ }
+ }
+ }
+ m_use_dvb_time = b;
+ }
+}
+
+void eDVBLocalTimeHandler::updateNonTuned()
+{
+ updateTime(-1, 0, 0);
+ m_updateNonTunedTimer->start(TIME_UPDATE_INTERVAL, true);
+}
+
+void eDVBLocalTimeHandler::updateTime( time_t tp_time, eDVBChannel *chan, int update_count )
+{
+ int time_difference;
bool restart_tdt = false;
- if (!tp_time )
+ if (!tp_time)
restart_tdt = true;
else if (tp_time == -1)
{
time_t rtc_time = getRTC();
if ( rtc_time ) // RTC Ready?
{
- tm now = *localtime(&rtc_time);
+ tm now;
+ localtime_r(&rtc_time, &now);
eDebug("[eDVBLocalTimerHandler] RTC time is %02d:%02d:%02d",
now.tm_hour,
now.tm_min,
now.tm_sec);
time_t linuxTime=time(0);
- time_t nowTime=linuxTime+m_time_difference;
- now = *localtime(&nowTime);
+ localtime_r(&linuxTime, &now);
eDebug("[eDVBLocalTimerHandler] Receiver time is %02d:%02d:%02d",
now.tm_hour,
now.tm_min,
now.tm_sec);
- m_time_difference = rtc_time - linuxTime;
- eDebug("[eDVBLocalTimerHandler] RTC to Receiver time difference is %d seconds", nowTime - rtc_time );
- if ( abs(m_time_difference) > 59 )
+ time_difference = rtc_time - linuxTime;
+ eDebug("[eDVBLocalTimerHandler] RTC to Receiver time difference is %ld seconds", linuxTime - rtc_time );
+ if ( time_difference )
{
eDebug("[eDVBLocalTimerHandler] set Linux Time to RTC Time");
timeval tnow;
gettimeofday(&tnow,0);
tnow.tv_sec=rtc_time;
settimeofday(&tnow,0);
- for (ePtrList<eMainloop>::iterator it(eMainloop::existing_loops)
- ;it != eMainloop::existing_loops.end(); ++it)
- it->setTimerOffset(m_time_difference);
- m_time_difference=0;
}
- else if ( !m_time_difference )
+ else if ( !time_difference )
eDebug("[eDVBLocalTimerHandler] no change needed");
else
eDebug("[eDVBLocalTimerHandler] set to RTC time");
// current linux time
time_t linuxTime = time(0);
- // current enigma time
- time_t nowTime=linuxTime+m_time_difference;
-
// difference between current enigma time and transponder time
- int enigma_diff = tp_time-nowTime;
+ int enigma_diff = tp_time-linuxTime;
int new_diff=0;
+ bool updated = m_time_ready;
+
if ( m_time_ready ) // ref time ready?
{
// difference between reference time (current enigma time)
{
eDebug("[eDVBLocalTimerHandler] we have correction %d", it->second);
time_t CorrectedTpTime = tp_time+it->second;
- int ddiff = CorrectedTpTime-nowTime;
+ int ddiff = CorrectedTpTime-linuxTime;
eDebug("[eDVBLocalTimerHandler] diff after add correction is %d", ddiff);
if ( abs(it->second) < 300 ) // stored correction < 5 min
{
eDebug("[eDVBLocalTimerHandler] use stored correction(<5 min)");
new_diff = ddiff;
}
- else if ( /*eSystemInfo::getInstance()->getHwType() == eSystemInfo::DM7020 && TODO !!!*/
- getRTC() )
+ else if ( getRTC() )
{
time_t rtc=getRTC();
m_timeOffsetMap[chan->getChannelID()] = rtc-tp_time;
- new_diff = rtc-nowTime; // set enigma time to rtc
- eDebug("[eDVBLocalTimerHandler] update stored correction to %d (calced against RTC time)", rtc-tp_time );
+ new_diff = rtc-linuxTime; // set enigma time to rtc
+ eDebug("[eDVBLocalTimerHandler] update stored correction to %ld (calced against RTC time)", rtc-tp_time );
}
else if ( abs(ddiff) <= 120 )
{
m_time_ready=true;
}
- time_t t = nowTime+new_diff;
+ time_t t = linuxTime+new_diff;
m_last_tp_time_difference=tp_time-t;
- if (!new_diff)
+ if (!new_diff &&
+ updated) // overrride this check on first received TDT
{
eDebug("[eDVBLocalTimerHandler] not changed");
return;
}
- tm now = *localtime(&t);
+ if ( !update_count )
+ {
+ // set rtc to calced transponder time when the first tdt is received on this
+ // transponder
+ setRTC(t);
+ eDebug("[eDVBLocalTimerHandler] update RTC");
+ }
+ else if (getRTC())
+ {
+ if (abs(getRTC() - t) > 60)
+ {
+ eDebug("[eDVBLocalTimerHandler] difference between new linux time and RTC time is > 60 sec... transponder time looks not ok... use rtc time");
+ t = getRTC();
+ }
+ else
+ eDebug("[eDVBLocalTimerHandler] difference between linux time and RTC time is < 60 sec... so the transponder time looks ok");
+ }
+ else
+ eDebug("[eDVBLocalTimerHandler] no RTC available :(");
+
+ tm now;
+ localtime_r(&t, &now);
eDebug("[eDVBLocalTimerHandler] time update to %02d:%02d:%02d",
now.tm_hour,
now.tm_min,
now.tm_sec);
- m_time_difference = t - linuxTime; // calc our new linux_time -> enigma_time correction
- eDebug("[eDVBLocalTimerHandler] m_time_difference is %d", m_time_difference );
-
-// if ( eSystemInfo::getInstance()->getHwType() == eSystemInfo::DM7020 ) TODO !!
- setRTC(t);
+ time_difference = t - linuxTime; // calc our new linux_time -> enigma_time correction
+ eDebug("[eDVBLocalTimerHandler] m_time_difference is %d", time_difference );
- if ( abs(m_time_difference) > 59 )
+ if ( time_difference )
{
eDebug("[eDVBLocalTimerHandler] set Linux Time");
timeval tnow;
gettimeofday(&tnow,0);
tnow.tv_sec=t;
settimeofday(&tnow,0);
- for (ePtrList<eMainloop>::iterator it(eMainloop::existing_loops)
- ;it != eMainloop::existing_loops.end(); ++it)
- it->setTimerOffset(m_time_difference);
- m_time_difference=0;
}
/*emit*/ m_timeUpdated();
if ( restart_tdt )
{
- std::map<iDVBChannel*, TDT*>::iterator it =
- m_active_tables.find(chan);
- if ( it != m_active_tables.end() )
+ std::map<iDVBChannel*, channel_data>::iterator it =
+ m_knownChannels.find(chan);
+ if ( it != m_knownChannels.end() )
{
- delete it->second;
- it->second = new TDT(chan);
- it->second->startTimer(60*60*1000); // restart TDT for this transponder in 60min
+ int updateCount = it->second.tdt->getUpdateCount();
+ it->second.tdt = 0;
+ it->second.tdt = new TDT(chan, updateCount);
+ it->second.tdt->startTimer(TIME_UPDATE_INTERVAL); // restart TDT for this transponder in 30min
}
}
-
}
void eDVBLocalTimeHandler::DVBChannelAdded(eDVBChannel *chan)
{
- eDebug("[eDVBLocalTimerHandler] add channel %p", chan);
if ( chan )
{
- std::map<iDVBChannel*, TDT*>::iterator it =
- m_active_tables.find(chan);
- if ( it != m_active_tables.end() )
- {
- delete it->second;
- it->second = new TDT(chan);
- }
- else
- m_active_tables[chan] = new TDT(chan);
+// eDebug("[eDVBLocalTimerHandler] add channel %p", chan);
+ std::pair<std::map<iDVBChannel*, channel_data>::iterator, bool> tmp =
+ m_knownChannels.insert( std::pair<iDVBChannel*, channel_data>(chan, channel_data()) );
+ tmp.first->second.tdt = NULL;
+ tmp.first->second.channel = chan;
+ tmp.first->second.m_prevChannelState = -1;
+ chan->connectStateChange(slot(*this, &eDVBLocalTimeHandler::DVBChannelStateChanged), tmp.first->second.m_stateChangedConn);
}
}
-void eDVBLocalTimeHandler::DVBChannelRemoved(eDVBChannel *chan)
+void eDVBLocalTimeHandler::DVBChannelStateChanged(iDVBChannel *chan)
{
- eDebug("[eDVBLocalTimerHandler] remove channel %p", chan);
- std::map<iDVBChannel*, TDT*>::iterator it =
- m_active_tables.find(chan);
- if ( it != m_active_tables.end() )
+ std::map<iDVBChannel*, channel_data>::iterator it =
+ m_knownChannels.find(chan);
+ if ( it != m_knownChannels.end() )
{
- delete it->second;
- m_active_tables.erase(it);
+ int state=0;
+ chan->getState(state);
+ if ( state != it->second.m_prevChannelState )
+ {
+ switch (state)
+ {
+ case iDVBChannel::state_ok:
+ eDebug("[eDVBLocalTimerHandler] channel %p running", chan);
+ m_updateNonTunedTimer->stop();
+ if (m_use_dvb_time) {
+ it->second.tdt = new TDT(it->second.channel);
+ it->second.tdt->start();
+ }
+ break;
+ case iDVBChannel::state_release:
+ eDebug("[eDVBLocalTimerHandler] remove channel %p", chan);
+ m_knownChannels.erase(it);
+ if (m_knownChannels.empty())
+ m_updateNonTunedTimer->start(TIME_UPDATE_INTERVAL, true);
+ break;
+ default: // ignore all other events
+ return;
+ }
+ it->second.m_prevChannelState = state;
+ }
}
}
-
-void eDVBLocalTimeHandler::DVBChannelRunning(iDVBChannel *chan)
-{
- eDebug("[eDVBLocalTimerHandler] start channel %p", chan);
- std::map<iDVBChannel*, TDT*>::iterator it =
- m_active_tables.find(chan);
- if ( it != m_active_tables.end() )
- it->second->start();
-}