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