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