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