1 /* $Id$ */
2 /*
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <pj/os.h>
21 #include <pj/errno.h>
22 #include <pj/string.h>
23 #include <pj/log.h>
24 #include <windows.h>
25 
26 ///////////////////////////////////////////////////////////////////////////////
27 
28 #define SECS_TO_FT_MULT 10000000
29 
30 static LARGE_INTEGER base_time;
31 
32 #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE
33 #   define WINCE_TIME
34 #endif
35 
36 #ifdef WINCE_TIME
37 /* Note:
38  *  In Windows CE/Windows Mobile platforms, the availability of milliseconds
39  *  time resolution in SYSTEMTIME.wMilliseconds depends on the OEM, and most
40  *  likely it won't be available. When it's not available, the
41  *  SYSTEMTIME.wMilliseconds will contain a constant arbitrary value.
42  *
43  *  Because of that, we need to emulate the milliseconds time resolution
44  *  using QueryPerformanceCounter() (via pj_get_timestamp() API). However
45  *  there is limitation on using this, i.e. the time returned by
46  *  pj_gettimeofday() may be off by up to plus/minus 999 msec (the second
47  *  part will be correct, however the msec part may be off), because we're
48  *  not synchronizing the msec field with the change of value of the "second"
49  *  field of the system time.
50  *
51  *  Also there is other caveat which need to be handled (and they are
52  *  handled by this implementation):
53  *   - user may change system time, so pj_gettimeofday() needs to periodically
54  *     checks if system time has changed. The period on which system time is
55  *     checked is controlled by PJ_WINCE_TIME_CHECK_INTERVAL macro.
56  */
57 static LARGE_INTEGER g_start_time;  /* Time gettimeofday() is first called  */
58 static pj_timestamp  g_start_tick;  /* TS gettimeofday() is first called  */
59 static pj_timestamp  g_last_update; /* Last time check_system_time() is
60 				       called, to periodically synchronize
61 				       with up-to-date system time (in case
62 				       user changes system time).	    */
63 static pj_uint64_t   g_update_period; /* Period (in TS) check_system_time()
64 				         should be called.		    */
65 
66 /* Period on which check_system_time() is called, in seconds		    */
67 #ifndef PJ_WINCE_TIME_CHECK_INTERVAL
68 #   define PJ_WINCE_TIME_CHECK_INTERVAL (10)
69 #endif
70 
71 #endif
72 
73 #ifdef WINCE_TIME
init_start_time(void)74 static pj_status_t init_start_time(void)
75 {
76     SYSTEMTIME st;
77     FILETIME ft;
78     pj_timestamp freq;
79     pj_status_t status;
80 
81     GetLocalTime(&st);
82     SystemTimeToFileTime(&st, &ft);
83 
84     g_start_time.LowPart = ft.dwLowDateTime;
85     g_start_time.HighPart = ft.dwHighDateTime;
86     g_start_time.QuadPart /= SECS_TO_FT_MULT;
87     g_start_time.QuadPart -= base_time.QuadPart;
88 
89     status = pj_get_timestamp(&g_start_tick);
90     if (status != PJ_SUCCESS)
91 	return status;
92 
93     g_last_update.u64 = g_start_tick.u64;
94 
95     status = pj_get_timestamp_freq(&freq);
96     if (status != PJ_SUCCESS)
97 	return status;
98 
99     g_update_period = PJ_WINCE_TIME_CHECK_INTERVAL * freq.u64;
100 
101     PJ_LOG(4,("os_time_win32.c", "WinCE time (re)started"));
102 
103     return PJ_SUCCESS;
104 }
105 
check_system_time(pj_uint64_t ts_elapsed)106 static pj_status_t check_system_time(pj_uint64_t ts_elapsed)
107 {
108     enum { MIS = 5 };
109     SYSTEMTIME st;
110     FILETIME ft;
111     LARGE_INTEGER cur, calc;
112     DWORD diff;
113     pj_timestamp freq;
114     pj_status_t status;
115 
116     /* Get system's current time */
117     GetLocalTime(&st);
118     SystemTimeToFileTime(&st, &ft);
119 
120     cur.LowPart = ft.dwLowDateTime;
121     cur.HighPart = ft.dwHighDateTime;
122     cur.QuadPart /= SECS_TO_FT_MULT;
123     cur.QuadPart -= base_time.QuadPart;
124 
125     /* Get our calculated system time */
126     status = pj_get_timestamp_freq(&freq);
127     if (status != PJ_SUCCESS)
128 	return status;
129 
130     calc.QuadPart = g_start_time.QuadPart + ts_elapsed / freq.u64;
131 
132     /* See the difference between calculated and actual system time */
133     if (calc.QuadPart >= cur.QuadPart) {
134 	diff = (DWORD)(calc.QuadPart - cur.QuadPart);
135     } else {
136 	diff = (DWORD)(cur.QuadPart - calc.QuadPart);
137     }
138 
139     if (diff > MIS) {
140 	/* System time has changed */
141 	PJ_LOG(3,("os_time_win32.c", "WinCE system time changed detected "
142 				      "(diff=%u)", diff));
143 	status = init_start_time();
144     } else {
145 	status = PJ_SUCCESS;
146     }
147 
148     return status;
149 }
150 
151 #endif
152 
153 // Find 1st Jan 1970 as a FILETIME
get_base_time(void)154 static pj_status_t get_base_time(void)
155 {
156     SYSTEMTIME st;
157     FILETIME ft;
158     pj_status_t status = PJ_SUCCESS;
159 
160     memset(&st,0,sizeof(st));
161     st.wYear=1970;
162     st.wMonth=1;
163     st.wDay=1;
164     SystemTimeToFileTime(&st, &ft);
165 
166     base_time.LowPart = ft.dwLowDateTime;
167     base_time.HighPart = ft.dwHighDateTime;
168     base_time.QuadPart /= SECS_TO_FT_MULT;
169 
170 #ifdef WINCE_TIME
171     pj_enter_critical_section();
172     status = init_start_time();
173     pj_leave_critical_section();
174 #endif
175 
176     return status;
177 }
178 
pj_gettimeofday(pj_time_val * tv)179 PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
180 {
181 #ifdef WINCE_TIME
182     pj_timestamp tick;
183     pj_uint64_t msec_elapsed;
184 #else
185     SYSTEMTIME st;
186     FILETIME ft;
187     LARGE_INTEGER li;
188 #endif
189     pj_status_t status;
190 
191     if (base_time.QuadPart == 0) {
192 	status = get_base_time();
193 	if (status != PJ_SUCCESS)
194 	    return status;
195     }
196 
197 #ifdef WINCE_TIME
198     do {
199 	status = pj_get_timestamp(&tick);
200 	if (status != PJ_SUCCESS)
201 	    return status;
202 
203 	if (tick.u64 - g_last_update.u64 >= g_update_period) {
204 	    pj_enter_critical_section();
205 	    if (tick.u64 - g_last_update.u64 >= g_update_period) {
206 		g_last_update.u64 = tick.u64;
207 		check_system_time(tick.u64 - g_start_tick.u64);
208 	    }
209 	    pj_leave_critical_section();
210 	} else {
211 	    break;
212 	}
213     } while (1);
214 
215     msec_elapsed = pj_elapsed_msec64(&g_start_tick, &tick);
216 
217     tv->sec = (long)(g_start_time.QuadPart + msec_elapsed/1000);
218     tv->msec = (long)(msec_elapsed % 1000);
219 #else
220     /* Standard Win32 GetLocalTime */
221     GetLocalTime(&st);
222     SystemTimeToFileTime(&st, &ft);
223 
224     li.LowPart = ft.dwLowDateTime;
225     li.HighPart = ft.dwHighDateTime;
226     li.QuadPart /= SECS_TO_FT_MULT;
227     li.QuadPart -= base_time.QuadPart;
228 
229     tv->sec = li.LowPart;
230     tv->msec = st.wMilliseconds;
231 #endif	/* WINCE_TIME */
232 
233     return PJ_SUCCESS;
234 }
235 
pj_time_decode(const pj_time_val * tv,pj_parsed_time * pt)236 PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
237 {
238     LARGE_INTEGER li;
239     FILETIME ft;
240     SYSTEMTIME st;
241 
242     li.QuadPart = tv->sec;
243     li.QuadPart += base_time.QuadPart;
244     li.QuadPart *= SECS_TO_FT_MULT;
245 
246     ft.dwLowDateTime = li.LowPart;
247     ft.dwHighDateTime = li.HighPart;
248     FileTimeToSystemTime(&ft, &st);
249 
250     pt->year = st.wYear;
251     pt->mon = st.wMonth-1;
252     pt->day = st.wDay;
253     pt->wday = st.wDayOfWeek;
254 
255     pt->hour = st.wHour;
256     pt->min = st.wMinute;
257     pt->sec = st.wSecond;
258     pt->msec = tv->msec;
259 
260     return PJ_SUCCESS;
261 }
262 
263 /**
264  * Encode parsed time to time value.
265  */
pj_time_encode(const pj_parsed_time * pt,pj_time_val * tv)266 PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv)
267 {
268     SYSTEMTIME st;
269     FILETIME ft;
270     LARGE_INTEGER li;
271 
272     pj_bzero(&st, sizeof(st));
273     st.wYear = (pj_uint16_t) pt->year;
274     st.wMonth = (pj_uint16_t) (pt->mon + 1);
275     st.wDay = (pj_uint16_t) pt->day;
276     st.wHour = (pj_uint16_t) pt->hour;
277     st.wMinute = (pj_uint16_t) pt->min;
278     st.wSecond = (pj_uint16_t) pt->sec;
279     st.wMilliseconds = (pj_uint16_t) pt->msec;
280 
281     SystemTimeToFileTime(&st, &ft);
282 
283     li.LowPart = ft.dwLowDateTime;
284     li.HighPart = ft.dwHighDateTime;
285     li.QuadPart /= SECS_TO_FT_MULT;
286     li.QuadPart -= base_time.QuadPart;
287 
288     tv->sec = li.LowPart;
289     tv->msec = st.wMilliseconds;
290 
291     return PJ_SUCCESS;
292 }
293 
294 /**
295  * Convert local time to GMT.
296  */
pj_time_local_to_gmt(pj_time_val * tv)297 PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv)
298 {
299 #ifdef WINCE_TIME
300     return PJ_ENOTSUP;
301 #else
302     LARGE_INTEGER li;
303     FILETIME ft_local;
304     FILETIME ft_gmt;
305 
306     li.QuadPart = tv->sec;
307     li.QuadPart += base_time.QuadPart;
308     li.QuadPart *= SECS_TO_FT_MULT;
309 
310     ft_local.dwLowDateTime = li.LowPart;
311     ft_local.dwHighDateTime = li.HighPart;
312 
313     if (LocalFileTimeToFileTime(&ft_local, &ft_gmt) == 0)
314 	return PJ_EINVAL;
315 
316     li.LowPart = ft_gmt.dwLowDateTime;
317     li.HighPart = ft_gmt.dwHighDateTime;
318     li.QuadPart /= SECS_TO_FT_MULT;
319     li.QuadPart -= base_time.QuadPart;
320 
321     tv->sec = li.LowPart;
322 
323     return PJ_SUCCESS;
324 #endif
325 }
326 
327 /**
328  * Convert GMT to local time.
329  */
pj_time_gmt_to_local(pj_time_val * tv)330 PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv)
331 {
332 #ifdef WINCE_TIME
333     return PJ_ENOTSUP;
334 #else
335     LARGE_INTEGER li;
336     FILETIME ft_local;
337     FILETIME ft_gmt;
338 
339     li.QuadPart = tv->sec;
340     li.QuadPart += base_time.QuadPart;
341     li.QuadPart *= SECS_TO_FT_MULT;
342 
343     ft_gmt.dwLowDateTime = li.LowPart;
344     ft_gmt.dwHighDateTime = li.HighPart;
345 
346     if (FileTimeToLocalFileTime(&ft_gmt, &ft_local) == 0)
347 	return PJ_EINVAL;
348 
349     li.LowPart = ft_local.dwLowDateTime;
350     li.HighPart = ft_local.dwHighDateTime;
351     li.QuadPart /= SECS_TO_FT_MULT;
352     li.QuadPart -= base_time.QuadPart;
353 
354     tv->sec = li.LowPart;
355 
356     return PJ_SUCCESS;
357 #endif
358 }
359 
360 
361