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