revert breaks some stupid old compilers
[oscam.git] / oscam-time.c
blobdf5e475b0d6de8e098fd9ecc74b8d07a785d8f69
1 #include "globals.h"
2 #include "oscam-time.h"
4 static enum clock_type clock_type = CLOCK_TYPE_UNKNOWN;
6 #if defined(CLOCKFIX)
7 struct timeval lasttime; // holds previous time to detect systemtime adjustments due to eg transponder change on dvb receivers
8 #endif
10 int64_t comp_timeb(struct timeb *tpa, struct timeb *tpb)
12 return (int64_t)(((int64_t)(tpa->time - tpb->time) * 1000ull) + ((int64_t) tpa->millitm - (int64_t) tpb->millitm));
15 int64_t comp_timebus(struct timeb *tpa, struct timeb *tpb)
17 return (int64_t)(((int64_t)(tpa->time - tpb->time) * 1000000ull) + ((int64_t) tpa->millitm - (int64_t) tpb->millitm));
20 /* Checks if year is a leap year. If so, 1 is returned, else 0. */
21 static int8_t is_leap(unsigned int y)
23 return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0);
26 /* Drop-in replacement for timegm function as some plattforms strip the function from their libc.. */
27 time_t cs_timegm(struct tm *tm)
29 time_t result = 0;
30 int32_t i;
31 if(tm->tm_mon > 12 || tm->tm_mon < 0 || tm->tm_mday > 31 || tm->tm_min > 60 || tm->tm_sec > 60 || tm->tm_hour > 24)
32 { return 0; }
33 for(i = 70; i < tm->tm_year; ++i)
35 result += is_leap(i + 1900) ? 366 : 365;
37 for(i = 0; i < tm->tm_mon; ++i)
39 if(i == 0 || i == 2 || i == 4 || i == 6 || i == 7 || i == 9 || i == 11) { result += 31; }
40 else if(i == 3 || i == 5 || i == 8 || i == 10) { result += 30; }
41 else if(is_leap(tm->tm_year + 1900)) { result += 29; }
42 else { result += 28; }
44 result += tm->tm_mday - 1;
45 result *= 24;
46 result += tm->tm_hour;
47 result *= 60;
48 result += tm->tm_min;
49 result *= 60;
50 result += tm->tm_sec;
51 return result;
54 /* Drop-in replacement for gmtime_r as some plattforms strip the function from their libc. */
55 struct tm *cs_gmtime_r(const time_t *timep, struct tm *r)
57 static const int16_t daysPerMonth[13] = { 0,
58 31,
59 31 + 28,
60 31 + 28 + 31,
61 31 + 28 + 31 + 30,
62 31 + 28 + 31 + 30 + 31,
63 31 + 28 + 31 + 30 + 31 + 30,
64 31 + 28 + 31 + 30 + 31 + 30 + 31,
65 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
66 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
67 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
68 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
69 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31
71 time_t i;
72 time_t work = * timep % 86400;
73 r->tm_sec = work % 60;
74 work /= 60;
75 r->tm_min = work % 60;
76 r->tm_hour = work / 60;
77 work = * timep / 86400;
78 r->tm_wday = (4 + work) % 7;
79 for(i = 1970; ; ++i)
81 time_t k = is_leap(i) ? 366 : 365;
82 if(work >= k)
83 { work -= k; }
84 else
85 { break; }
87 r->tm_year = i - 1900;
88 r->tm_yday = work;
89 r->tm_mday = 1;
90 if(is_leap(i) && work > 58)
92 if(work == 59)
93 { r->tm_mday = 2; } /* 29.2. */
94 work -= 1;
96 for(i = 11; i && daysPerMonth[i] > work; --i)
97 { ; }
98 r->tm_mon = i;
99 r->tm_mday += work - daysPerMonth[i];
100 return r;
103 /* Drop-in replacement for ctime_r as some plattforms strip the function from their libc. */
104 char *cs_ctime_r(const time_t *timep, char *buf)
106 struct tm t;
107 localtime_r(timep, &t);
108 strftime(buf, 26, "%c\n", &t);
109 return buf;
112 void cs_ftime(struct timeb *tp)
114 struct timeval tv;
115 gettimeofday(&tv, NULL);
116 #if defined(CLOCKFIX)
117 if (tv.tv_sec > lasttime.tv_sec || (tv.tv_sec == lasttime.tv_sec && tv.tv_usec >= lasttime.tv_usec)){ // check for time issues!
118 lasttime = tv; // register this valid time
120 else
122 tv = lasttime;
123 settimeofday(&tv, NULL); // set time back to last known valid time
124 //fprintf(stderr, "*** WARNING: BAD TIME AFFECTING WHOLE OSCAM ECM HANDLING, SYSTEMTIME SET TO LAST KNOWN VALID TIME **** \n");
126 #endif
127 tp->time = tv.tv_sec;
128 tp->millitm = tv.tv_usec / 1000;
131 void cs_ftimeus(struct timeb *tp)
133 struct timeval tv;
134 gettimeofday(&tv, NULL);
135 #if defined(CLOCKFIX)
136 if (tv.tv_sec > lasttime.tv_sec || (tv.tv_sec == lasttime.tv_sec && tv.tv_usec >= lasttime.tv_usec)){ // check for time issues!
137 lasttime = tv; // register this valid time
139 else
141 tv = lasttime;
142 settimeofday(&tv, NULL); // set time back to last known valid time
143 //fprintf(stderr, "*** WARNING: BAD TIME AFFECTING WHOLE OSCAM ECM HANDLING, SYSTEMTIME SET TO LAST KNOWN VALID TIME **** \n");
145 #endif
146 tp->time = tv.tv_sec;
147 tp->millitm = tv.tv_usec;
150 void cs_sleepms(uint32_t msec)
152 //does not interfere with signals like sleep and usleep do
153 struct timespec req_ts;
154 req_ts.tv_sec = msec / 1000;
155 req_ts.tv_nsec = (msec % 1000) * 1000000L;
156 int32_t olderrno = errno; // Some OS (especially MacOSX) seem to set errno to ETIMEDOUT when sleeping
157 while (1)
159 /* Sleep for the time specified in req_ts. If interrupted by a
160 signal, place the remaining time left to sleep back into req_ts. */
161 int rval = nanosleep (&req_ts, &req_ts);
162 if (rval == 0)
163 break; // Completed the entire sleep time; all done.
164 else if (errno == EINTR)
165 continue; // Interrupted by a signal. Try again.
166 else
167 break; // Some other error; bail out.
169 errno = olderrno;
172 void cs_sleepus(uint32_t usec)
174 //does not interfere with signals like sleep and usleep do
175 struct timespec req_ts;
176 req_ts.tv_sec = usec / 1000000;
177 req_ts.tv_nsec = (usec % 1000000) * 1000L;
178 int32_t olderrno = errno; // Some OS (especially MacOSX) seem to set errno to ETIMEDOUT when sleeping
179 while (1)
181 /* Sleep for the time specified in req_ts. If interrupted by a
182 signal, place the remaining time left to sleep back into req_ts. */
183 int rval = nanosleep (&req_ts, &req_ts);
184 if (rval == 0)
185 break; // Completed the entire sleep time; all done.
186 else if (errno == EINTR)
187 continue; // Interrupted by a signal. Try again.
188 else
189 break; // Some other error; bail out.
191 errno = olderrno;
194 void add_ms_to_timespec(struct timespec *timeout, int32_t msec)
196 struct timespec now;
197 int64_t nanosecs, secs;
198 const int64_t NANOSEC_PER_MS = 1000000;
199 const int64_t NANOSEC_PER_SEC = 1000000000;
200 cs_gettime(&now);
201 nanosecs = (int64_t) (msec * NANOSEC_PER_MS + now.tv_nsec);
202 if (nanosecs >= NANOSEC_PER_SEC){
203 secs = now.tv_sec + (nanosecs / NANOSEC_PER_SEC);
204 nanosecs %= NANOSEC_PER_SEC;
206 else{
207 secs = now.tv_sec;
209 timeout->tv_sec = (long)secs;
210 timeout->tv_nsec = (long)nanosecs;
213 void add_ms_to_timeb(struct timeb *tb, int32_t ms)
215 if (ms >= 1000){
216 tb->time += ms / 1000;
217 tb->millitm += (ms % 1000);
219 else{
220 tb->millitm += ms;
222 if(tb->millitm >= 1000)
224 tb->millitm %= 1000;
225 tb->time++;
229 int64_t add_ms_to_timeb_diff(struct timeb *tb, int32_t ms)
231 struct timeb tb_now;
232 add_ms_to_timeb(tb, ms);
233 cs_ftime(&tb_now);
234 return comp_timeb(tb, &tb_now);
237 #if defined(__UCLIBC__)
238 # define __UCLIBC_VER (__UCLIBC_MAJOR__ * 10000 + __UCLIBC_MINOR__ * 100 + __UCLIBC_SUBLEVEL__)
239 #else
240 # define __UCLIBC_VER 999999
241 #endif
243 #if defined(__GLIBC__)
244 # define __GLIBCVER (__GLIBC__ * 100 + __GLIBC_MINOR__)
245 #else
246 # define __GLIBCVER 9999
247 #endif
249 // Assume we have HAVE_pthread_condattr_setclock if CLOCK_MONOTONIC is defined
250 #if defined(CLOCKFIX) && defined(CLOCK_MONOTONIC)
251 #define HAVE_pthread_condattr_setclock 1
252 #endif
254 #if defined(HAVE_pthread_condattr_setclock)
255 // UCLIBC 0.9.31 does not have pthread_condattr_setclock
256 # if __UCLIBC_VER < 932
257 # undef HAVE_pthread_condattr_setclock
258 # endif
259 // glibc 2.3.6 in ppc old toolchain do not have pthread_condattr_setclock
260 # if __GLIBCVER < 204
261 # undef HAVE_pthread_condattr_setclock
262 # endif
263 // android's libc not have pthread_condattr_setclock
264 # if __BIONIC__
265 # undef HAVE_pthread_condattr_setclock
266 # endif
267 #endif
269 void __cs_pthread_cond_init(const char *n, pthread_cond_t *cond)
271 pthread_condattr_t attr;
272 SAFE_CONDATTR_INIT_R(&attr, n); // init condattr with defaults
273 #if 0
274 #if defined(HAVE_pthread_condattr_setclock)
275 enum clock_type ctype = cs_getclocktype();
276 SAFE_CONDATTR_SETCLOCK_R(&attr, (ctype == CLOCK_TYPE_MONOTONIC) ? CLOCK_MONOTONIC : CLOCK_REALTIME, n);
277 #endif
278 #endif
279 SAFE_COND_INIT_R(cond, &attr, n); // init thread with right clock assigned
280 pthread_condattr_destroy(&attr);
283 void __cs_pthread_cond_init_nolog(const char *n, pthread_cond_t *cond)
285 pthread_condattr_t attr;
286 SAFE_CONDATTR_INIT_NOLOG_R(&attr, n); // init condattr with defaults
287 #if 0
288 #if defined(HAVE_pthread_condattr_setclock)
289 enum clock_type ctype = cs_getclocktype();
290 SAFE_CONDATTR_SETCLOCK_NOLOG_R(&attr, (ctype == CLOCK_TYPE_MONOTONIC) ? CLOCK_MONOTONIC : CLOCK_REALTIME, n);
291 #endif
292 #endif
293 SAFE_COND_INIT_NOLOG_R(cond, &attr, n); // init thread with right clock assigned
294 pthread_condattr_destroy(&attr);
298 void sleepms_on_cond(const char *n, pthread_mutex_t *mutex, pthread_cond_t *cond, uint32_t msec)
300 struct timespec ts;
301 add_ms_to_timespec(&ts, msec);
302 SAFE_MUTEX_LOCK_R(mutex, n);
303 SAFE_COND_TIMEDWAIT_R(cond, mutex, &ts, n); // sleep on sleep_cond
304 SAFE_MUTEX_UNLOCK_R(mutex, n);
307 void cs_pthread_cond_init(const char *n, pthread_mutex_t *mutex, pthread_cond_t *cond)
309 SAFE_MUTEX_INIT_R(mutex, NULL, n);
310 __cs_pthread_cond_init(n, cond);
313 void cs_pthread_cond_init_nolog(const char *n, pthread_mutex_t *mutex, pthread_cond_t *cond)
315 SAFE_MUTEX_INIT_NOLOG_R(mutex, NULL, n);
316 __cs_pthread_cond_init(n, cond);
319 enum clock_type cs_getclocktype(void) {
320 if (clock_type == CLOCK_TYPE_UNKNOWN) {
321 struct timespec ts;
322 cs_gettime(&ts); // init clock type
324 return clock_type;
327 time_t cs_walltime(struct timeb *tp)
329 // we dont need to fetch time again and calculate if oscam is already using realtimeclock!
330 if (clock_type != CLOCK_TYPE_MONOTONIC)
331 return tp->time;
333 struct timespec ts;
334 struct timeval tv;
336 cs_gettime(&ts);
337 gettimeofday(&tv, NULL);
338 int64_t skew = tv.tv_sec - ts.tv_sec;
339 return(tp->time + skew);
342 /* Return real time clock value calculated based on cs_gettime(). Use this instead of time() */
343 time_t cs_time(void)
345 struct timeb tb;
346 cs_ftime(&tb);
347 return cs_walltime(&tb);
350 #ifdef __MACH__
351 #include <mach/clock.h>
352 #include <mach/mach.h>
353 #endif
355 void cs_gettime(struct timespec *ts)
357 struct timeval tv;
358 gettimeofday(&tv, NULL);
359 #if defined(CLOCKFIX)
360 if (tv.tv_sec > lasttime.tv_sec || (tv.tv_sec == lasttime.tv_sec && tv.tv_usec >= lasttime.tv_usec)){ // check for time issues!
361 lasttime = tv; // register this valid time
363 else
365 tv = lasttime;
366 settimeofday(&tv, NULL); // set time back to last known valid time
367 //fprintf(stderr, "*** WARNING: BAD TIME AFFECTING WHOLE OSCAM ECM HANDLING, SYSTEMTIME SET TO LAST KNOWN VALID TIME **** \n");
369 #endif
370 ts->tv_sec = tv.tv_sec;
371 ts->tv_nsec = tv.tv_usec * 1000;
372 clock_type = CLOCK_TYPE_REALTIME;
373 return;
374 #if 0
375 #if !defined(CLOCKFIX) || (!defined(CLOCK_MONOTONIC) && !defined(__MACH__))
376 struct timeval tv;
377 gettimeofday(&tv, NULL);
378 ts->tv_sec = tv.tv_sec;
379 ts->tv_nsec = tv.tv_usec * 1000;
380 clock_type = CLOCK_TYPE_REALTIME;
381 return;
382 #elif defined (__MACH__)
383 // OS X does not have clock_gettime, use clock_get_time
384 clock_serv_t cclock;
385 mach_timespec_t mts;
386 host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
387 clock_get_time(cclock, &mts);
388 mach_port_deallocate(mach_task_self(), cclock);
389 ts->tv_sec = mts.tv_sec;
390 ts->tv_nsec = mts.tv_nsec;
391 clock_type = CLOCK_TYPE_REALTIME;
392 #else
393 if (clock_type == CLOCK_TYPE_REALTIME) { // monotonic returned error
394 clock_gettime(CLOCK_REALTIME, ts);
395 return;
397 int32_t ret = clock_gettime(CLOCK_MONOTONIC, ts);
398 clock_type = CLOCK_TYPE_MONOTONIC;
399 if ((ret < 0 && errno == EINVAL)){ // Error fetching time from this source (Shouldn't happen on modern Linux)
400 clock_gettime(CLOCK_REALTIME, ts);
401 clock_type = CLOCK_TYPE_REALTIME;
403 #endif
404 #endif