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