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