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