source: trunk/oscam-time.c

Last change on this file was 11473, checked in by Gorgone Impertinence, 10 months ago

big cleanup pack 3

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