try to detect transponders with a to slow or to fast time..
[enigma2.git] / lib / dvb / dvbtime.cpp
1 #include <lib/dvb/dvbtime.h>
2 #include <lib/dvb/dvb.h>
3
4 #include <sys/ioctl.h>
5 #include <stdio.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9
10 // defines for DM7000 / DM7020
11 #define FP_IOCTL_SET_RTC         0x101
12 #define FP_IOCTL_GET_RTC         0x102
13
14 static time_t prev_time;
15
16 void setRTC(time_t time)
17 {
18         int fd = open("/dev/dbox/fp0", O_RDWR);
19         if ( fd >= 0 )
20         {
21                 if ( ::ioctl(fd, FP_IOCTL_SET_RTC, (void*)&time ) < 0 )
22                         eDebug("FP_IOCTL_SET_RTC failed(%m)");
23                 else
24                         prev_time = time;
25                 close(fd);
26         }
27 }
28
29 time_t getRTC()
30 {
31         time_t rtc_time=0;
32         int fd = open("/dev/dbox/fp0", O_RDWR);
33         if ( fd >= 0 )
34         {
35                 if ( ::ioctl(fd, FP_IOCTL_GET_RTC, (void*)&rtc_time ) < 0 )
36                         eDebug("FP_IOCTL_GET_RTC failed(%m)");
37                 close(fd);
38         }
39         return rtc_time != prev_time ? rtc_time : 0;
40 }
41
42 time_t parseDVBtime(__u8 t1, __u8 t2, __u8 t3, __u8 t4, __u8 t5)
43 {
44         tm t;
45         t.tm_sec=fromBCD(t5);
46         t.tm_min=fromBCD(t4);
47         t.tm_hour=fromBCD(t3);
48         int mjd=(t1<<8)|t2;
49         int k;
50
51         t.tm_year = (int) ((mjd - 15078.2) / 365.25);
52         t.tm_mon = (int) ((mjd - 14956.1 - (int)(t.tm_year * 365.25)) / 30.6001);
53         t.tm_mday = (int) (mjd - 14956 - (int)(t.tm_year * 365.25) - (int)(t.tm_mon * 30.6001));
54         k = (t.tm_mon == 14 || t.tm_mon == 15) ? 1 : 0;
55         t.tm_year = t.tm_year + k;
56         t.tm_mon = t.tm_mon - 1 - k * 12;
57         t.tm_mon--;
58
59         t.tm_isdst =  0;
60         t.tm_gmtoff = 0;
61
62         return timegm(&t);
63 }
64
65 TDT::TDT(eDVBChannel *chan, int update_count)
66         :chan(chan), update_count(update_count)
67 {
68         CONNECT(tableReady, TDT::ready);
69         CONNECT(m_interval_timer.timeout, TDT::start);
70         if (chan)
71                 chan->getDemux(demux, 0);
72 }
73
74 void TDT::ready(int error)
75 {
76         eDVBLocalTimeHandler::getInstance()->updateTime(error, chan, ++update_count);
77 }
78
79 int TDT::createTable(unsigned int nr, const __u8 *data, unsigned int max)
80 {
81         if ( data && data[0] == 0x70 || data[0] == 0x73 )
82         {
83                 int length = ((data[1] & 0x0F) << 8) | data[2];
84                 if ( length >= 5 )
85                 {
86                         time_t tptime = parseDVBtime(data[3], data[4], data[5], data[6], data[7]);
87                         if (tptime && tptime != -1)
88                                 eDVBLocalTimeHandler::getInstance()->updateTime(tptime, chan, update_count);
89                         error=0;
90                         return 1;
91                 }
92         }
93         return 0;
94 }
95
96 void TDT::start()
97 {
98         if ( chan )
99         {
100                 eDVBTableSpec spec;
101                 spec.pid = TimeAndDateSection::PID;
102                 spec.tid = TimeAndDateSection::TID;
103                 spec.tid_mask = 0xFC;
104                 spec.timeout = TimeAndDateSection::TIMEOUT;
105                 spec.flags= eDVBTableSpec::tfAnyVersion |
106                                         eDVBTableSpec::tfHaveTID |
107                                         eDVBTableSpec::tfHaveTIDMask |
108                                         eDVBTableSpec::tfHaveTimeout;
109                 if ( demux )
110                         eGTable::start( demux, spec );
111         }
112 }
113
114 void TDT::startTimer( int interval )
115 {
116         m_interval_timer.start(interval, true);
117 }
118
119 eDVBLocalTimeHandler *eDVBLocalTimeHandler::instance;
120 DEFINE_REF(eDVBLocalTimeHandler);
121
122 eDVBLocalTimeHandler::eDVBLocalTimeHandler()
123         :m_time_ready(false), m_time_difference(0)
124 {
125         if ( !instance )
126                 instance=this;
127         ePtr<eDVBResourceManager> res_mgr;
128         eDVBResourceManager::getInstance(res_mgr);
129         if (!res_mgr)
130                 eDebug("[eDVBLocalTimerHandler] no resource manager !!!!!!!");
131         else
132         {
133                 res_mgr->connectChannelAdded(slot(*this,&eDVBLocalTimeHandler::DVBChannelAdded), m_chanAddedConn);
134                 time_t now = time(0);
135                 if ( now < 1072224000 ) // 01.01.2004
136                         eDebug("RTC not ready... wait for transponder time");
137                 else // inform all who's waiting for valid system time..
138                 {
139                         eDebug("Use valid Linux Time :) (RTC?)");
140                         m_time_ready = true;
141                         /*emit*/ m_timeUpdated();
142                 }
143         }
144 }
145
146 eDVBLocalTimeHandler::~eDVBLocalTimeHandler()
147 {
148         instance=0;
149         for (std::map<iDVBChannel*, channel_data>::iterator it=m_knownChannels.begin(); it != m_knownChannels.end(); ++it)
150                 delete it->second.tdt;
151         if (ready())
152         {
153                 eDebug("set RTC to previous valid time");
154                 setRTC(nowTime());
155         }
156 }
157
158 void eDVBLocalTimeHandler::readTimeOffsetData( const char* filename )
159 {
160         m_timeOffsetMap.clear();
161         FILE *f=fopen(filename, "r");
162         if (!f)
163                 return;
164         char line[256];
165         fgets(line, 256, f);
166         while (true)
167         {
168                 if (!fgets( line, 256, f ))
169                         break;
170                 if (strstr(line, "Transponder UTC Time Offsets\n"))
171                         continue;
172                 int dvbnamespace,tsid,onid,offs;
173                 if ( sscanf( line, "%08x,%04x,%04x:%d\n",&dvbnamespace,&tsid,&onid,&offs ) == 4 )
174                         m_timeOffsetMap[eDVBChannelID(dvbnamespace,tsid,onid)]=offs;
175         }
176         fclose(f);
177 }
178
179 void eDVBLocalTimeHandler::writeTimeOffsetData( const char* filename )
180 {
181         FILE *f=fopen(filename, "w+");
182         if ( f )
183         {
184                 fprintf(f, "Transponder UTC Time Offsets\n");
185                 for ( std::map<eDVBChannelID,int>::iterator it ( m_timeOffsetMap.begin() ); it != m_timeOffsetMap.end(); ++it )
186                         fprintf(f, "%08x,%04x,%04x:%d\n",
187                                 it->first.dvbnamespace.get(),
188                                 it->first.transport_stream_id.get(), it->first.original_network_id.get(), it->second );
189                 fclose(f);
190         }
191 }
192
193 void eDVBLocalTimeHandler::updateTime( time_t tp_time, eDVBChannel *chan, int update_count )
194 {
195         bool restart_tdt = false;
196         if (!tp_time)
197                 restart_tdt = true;
198         else if (tp_time == -1)
199         {
200                 restart_tdt = true;
201                 /*if ( eSystemInfo::getInstance()->getHwType() == eSystemInfo::DM7020 ||
202                 ( eSystemInfo::getInstance()->getHwType() == eSystemInfo::DM7000
203                         && eSystemInfo::getInstance()->hasStandbyWakeupTimer() ) )     TODO !!!!!!! */
204                 {
205                         eDebug("[eDVBLocalTimerHandler] no transponder tuned... or no TDT/TOT avail .. try to use RTC :)");
206                         time_t rtc_time = getRTC();
207                         if ( rtc_time ) // RTC Ready?
208                         {
209                                 tm now;
210                                 localtime_r(&rtc_time, &now);
211                                 eDebug("[eDVBLocalTimerHandler] RTC time is %02d:%02d:%02d",
212                                         now.tm_hour,
213                                         now.tm_min,
214                                         now.tm_sec);
215                                 time_t linuxTime=time(0);
216                                 time_t nowTime=linuxTime+m_time_difference;
217                                 localtime_r(&nowTime, &now);
218                                 eDebug("[eDVBLocalTimerHandler] Receiver time is %02d:%02d:%02d",
219                                         now.tm_hour,
220                                         now.tm_min,
221                                         now.tm_sec);
222                                 m_time_difference = rtc_time - linuxTime;
223                                 eDebug("[eDVBLocalTimerHandler] RTC to Receiver time difference is %ld seconds", nowTime - rtc_time );
224                                 if ( m_time_difference )
225                                 {
226                                         eDebug("[eDVBLocalTimerHandler] set Linux Time to RTC Time");
227                                         timeval tnow;
228                                         gettimeofday(&tnow,0);
229                                         tnow.tv_sec=rtc_time;
230                                         settimeofday(&tnow,0);
231                                         m_time_difference=0;
232                                 }
233                                 else if ( !m_time_difference )
234                                         eDebug("[eDVBLocalTimerHandler] no change needed");
235                                 else
236                                         eDebug("[eDVBLocalTimerHandler] set to RTC time");
237                                 /*emit*/ m_timeUpdated();
238                         }
239                         else
240                                 eDebug("[eDVBLocalTimerHandler] shit RTC not ready :(");
241                 }
242         }
243         else
244         {
245                 std::map< eDVBChannelID, int >::iterator it( m_timeOffsetMap.find( chan->getChannelID() ) );
246
247  // current linux time
248                 time_t linuxTime = time(0);
249
250  // current enigma time
251                 time_t nowTime=linuxTime+m_time_difference;
252
253         // difference between current enigma time and transponder time
254                 int enigma_diff = tp_time-nowTime;
255
256                 int new_diff=0;
257
258                 bool updated = m_time_ready;
259
260                 if ( m_time_ready )  // ref time ready?
261                 {
262                         // difference between reference time (current enigma time)
263                         // and the transponder time
264                         eDebug("[eDVBLocalTimerHandler] diff is %d", enigma_diff);
265                         if ( abs(enigma_diff) < 120 )
266                         {
267                                 eDebug("[eDVBLocalTimerHandler] diff < 120 .. use Transponder Time");
268                                 m_timeOffsetMap[chan->getChannelID()] = 0;
269                                 new_diff = enigma_diff;
270                         }
271                         else if ( it != m_timeOffsetMap.end() ) // correction saved?
272                         {
273                                 eDebug("[eDVBLocalTimerHandler] we have correction %d", it->second);
274                                 time_t CorrectedTpTime = tp_time+it->second;
275                                 int ddiff = CorrectedTpTime-nowTime;
276                                 eDebug("[eDVBLocalTimerHandler] diff after add correction is %d", ddiff);
277                                 if ( abs(it->second) < 300 ) // stored correction < 5 min
278                                 {
279                                         eDebug("[eDVBLocalTimerHandler] use stored correction(<5 min)");
280                                         new_diff = ddiff;
281                                 }
282                                 else if ( getRTC() )
283                                 {
284                                         time_t rtc=getRTC();
285                                         m_timeOffsetMap[chan->getChannelID()] = rtc-tp_time;
286                                         new_diff = rtc-nowTime;  // set enigma time to rtc
287                                         eDebug("[eDVBLocalTimerHandler] update stored correction to %ld (calced against RTC time)", rtc-tp_time );
288                                 }
289                                 else if ( abs(ddiff) <= 120 )
290                                 {
291 // with stored correction calced time difference is lower 2 min
292 // this don't help when a transponder have a clock running to slow or to fast
293 // then its better to have a DM7020 with always running RTC
294                                         eDebug("[eDVBLocalTimerHandler] use stored correction(corr < 2 min)");
295                                         new_diff = ddiff;
296                                 }
297                                 else  // big change in calced correction.. hold current time and update correction
298                                 {
299                                         eDebug("[eDVBLocalTimerHandler] update stored correction to %d", -enigma_diff);
300                                         m_timeOffsetMap[chan->getChannelID()] = -enigma_diff;
301                                 }
302                         }
303                         else
304                         {
305                                 eDebug("[eDVBLocalTimerHandler] no correction found... store calced correction(%d)",-enigma_diff);
306                                 m_timeOffsetMap[chan->getChannelID()] = -enigma_diff;
307                         }
308                 }
309                 else  // no time setted yet
310                 {
311                         if ( it != m_timeOffsetMap.end() )
312                         {
313                                 enigma_diff += it->second;
314                                 eDebug("[eDVBLocalTimerHandler] we have correction (%d)... use", it->second );
315                         }
316                         else
317                                 eDebug("[eDVBLocalTimerHandler] dont have correction.. set Transponder Diff");
318                         new_diff=enigma_diff;
319                         m_time_ready=true;
320                 }
321
322                 time_t t = nowTime+new_diff;
323                 m_last_tp_time_difference=tp_time-t;
324
325                 if (!new_diff &&
326                         updated) // overrride this check on first received TDT
327                 {
328                         eDebug("[eDVBLocalTimerHandler] not changed");
329                         return;
330                 }
331
332                 if ( !update_count )
333                 {
334                         // set rtc to calced transponder time when the first tdt is received on this
335                         // transponder
336                         setRTC(t);
337                         eDebug("[eDVBLocalTimerHandler] update RTC");
338                 }
339                 else if (getRTC())
340                 {
341                         if (abs(getRTC() - t) > 60)
342                         {
343                                 eDebug("[eDVBLocalTimerHandler] difference between new linux time and RTC time is > 60 sec... transponder time looks not ok... use rtc time");
344                                 t = getRTC();
345                         }
346                         else
347                                 eDebug("[eDVBLocalTimerHandler] difference between linux time and RTC time is < 60 sec... so the transponder time looks ok");
348                 }
349                 else
350                         eDebug("[eDVBLocalTimerHandler] no RTC available :(");
351
352                 tm now;
353                 localtime_r(&t, &now);
354                 eDebug("[eDVBLocalTimerHandler] time update to %02d:%02d:%02d",
355                         now.tm_hour,
356                         now.tm_min,
357                         now.tm_sec);
358
359                 m_time_difference = t - linuxTime;   // calc our new linux_time -> enigma_time correction
360                 eDebug("[eDVBLocalTimerHandler] m_time_difference is %d", m_time_difference );
361
362                 if ( m_time_difference )
363                 {
364                         eDebug("[eDVBLocalTimerHandler] set Linux Time");
365                         timeval tnow;
366                         gettimeofday(&tnow,0);
367                         tnow.tv_sec=t;
368                         settimeofday(&tnow,0);
369                         m_time_difference=0;
370                 }
371
372                  /*emit*/ m_timeUpdated();
373         }
374
375         if ( restart_tdt )
376         {
377                 std::map<iDVBChannel*, channel_data>::iterator it =
378                         m_knownChannels.find(chan);
379                 if ( it != m_knownChannels.end() )
380                 {
381                         TDT *prev_tdt = it->second.tdt;
382                         it->second.tdt = new TDT(chan, prev_tdt->getUpdateCount());
383                         it->second.tdt->startTimer(60*60*1000);  // restart TDT for this transponder in 60min
384                         delete prev_tdt;
385                 }
386         }
387 }
388
389 void eDVBLocalTimeHandler::DVBChannelAdded(eDVBChannel *chan)
390 {
391         if ( chan )
392         {
393 //              eDebug("[eDVBLocalTimerHandler] add channel %p", chan);
394                 std::pair<std::map<iDVBChannel*, channel_data>::iterator, bool> tmp =
395                         m_knownChannels.insert( std::pair<iDVBChannel*, channel_data>(chan, channel_data()) );
396                 tmp.first->second.tdt = NULL;
397                 tmp.first->second.channel = chan;
398                 tmp.first->second.m_prevChannelState = -1;
399                 chan->connectStateChange(slot(*this, &eDVBLocalTimeHandler::DVBChannelStateChanged), tmp.first->second.m_stateChangedConn);
400         }
401 }
402
403 void eDVBLocalTimeHandler::DVBChannelStateChanged(iDVBChannel *chan)
404 {
405         std::map<iDVBChannel*, channel_data>::iterator it =
406                 m_knownChannels.find(chan);
407         if ( it != m_knownChannels.end() )
408         {
409                 int state=0;
410                 chan->getState(state);
411                 if ( state != it->second.m_prevChannelState )
412                 {
413                         switch (state)
414                         {
415                                 case iDVBChannel::state_ok:
416                                         eDebug("[eDVBLocalTimerHandler] channel %p running", chan);
417                                         it->second.tdt = new TDT(it->second.channel);
418                                         it->second.tdt->start();
419                                         break;
420                                 case iDVBChannel::state_release:
421                                         eDebug("[eDVBLocalTimerHandler] remove channel %p", chan);
422                                         delete it->second.tdt;
423                                         m_knownChannels.erase(it);
424                                         break;
425                                 default: // ignore all other events
426                                         return;
427                         }
428                         it->second.m_prevChannelState = state;
429                 }
430         }
431 }