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