]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
[Bug 708] nt_clockstuff.c has unintentionally been messed up with the previous change...
authorMartin Burnicki <burnicki@ntp.org>
Tue, 10 Oct 2006 16:15:47 +0000 (16:15 +0000)
committerMartin Burnicki <burnicki@ntp.org>
Tue, 10 Oct 2006 16:15:47 +0000 (16:15 +0000)
bk: 452bc733ZEuL9RTUaMadkCKE3Uo8AQ

ports/winnt/ntpd/nt_clockstuff.c

index c4a7407032706c581a8128d87d33164fa21b1c7b..bbb4c2036b0b8ab40c577241dfa2f1b7cdfcd06c 100644 (file)
-/* This file implementes system calls that are not compatible with UNIX */
+/* Windows NT Clock Routines
+ *
+ *
+ * Revision History:
+ * $Log$
+ * Revision 1.9  2000/11/19 09:02:12  dietrich
+ * From: Ron Thornton [rthornto@pictel.com]
+ * Sent: Thu 11/16/00 8:51 AM
+ * On Windows 2000 it requires a privilege on the current process token
+ * that is disabled by default on Windows 2000.
+ *
+ * I set the token by adding the following code at the beginning of the
+ * init_winnt_time() function in nt_clockstuff.c.
+ *
+ * Revision 1.8  2000/11/19 08:03:20  dietrich
+ * From: "Colin Dancer" <colin.dancer@pyrochrome.net>
+ * To: <bugs@ntp.org>
+ * Sent: 10 November 2000 12:59
+ * Subject: NT bug in NTP 4.0.99j
+ *
+ * I've found a bug in (and produced a fix for) the NT clock interpolation
+ * code in NTP 4.0.99j.
+ *
+ * The symptoms of the problem are that gettimeofday() function on NT
+ * can be wrong by hundreds of seconds if, while a gettimeofday() call
+ * is being processed, an APC completes after the query of the performance
+ * counter but before the lock is grabbed.  The most obvious fix is to move
+ * the lock to include the querying of the performance counter, but this
+ * could affect the predictability of the timestamp so I have instead
+ * tweaked the code to detect and sidestep the duff calculation.
+ *
+ * I've also found that on a loaded system the execution of the APC can be
+ * delayed, leading to errors of upto 10ms.  There is no easy fix to this,
+ * but I do have code for an alternative interpolation scheme which avoids
+ * the problem on single processor systems. I'm currently integrating this
+ * along with code for deciding which algorithm to use based on whether
+ * the system is SP or MP.
+ *
+ * Created by Sven Dietrich  sven@inter-yacht.com
+ *
+ */
+
 
 #ifdef HAVE_CONFIG_H
-# include <config.h>
+#include "config.h"
 #endif
 
-
-#include <stdio.h>
-#include "ntp_machine.h"
 #include "ntp_stdlib.h"
-#include "ntp_syslog.h"
-#include "ntp_fp.h"
-#include "ntp.h"
-#include "ntp_refclock.h"
-#include "win32_io.h"
+#include "clockstuff.h"
+#include "ntservice.h"
+#include "ntp_timer.h"
+#include "ntpd.h"
+
+/*
+ * Include code to possibly modify the MM timer while the service is active. 
+ */
+
+  /*
+   * Whether or not MM timer modifications takes place is still controlled 
+   * by the variable below which is initialized by a default value but 
+   * might be changed depending on a command line switch.
+   */
+  int modify_mm_timer = MM_TIMER_LORES;
+
+  #define MM_TIMER_INTV   1  /* the interval we'd want to set the MM timer to [ms] */
 
+  static UINT wTimerRes;
+  static TIMECAPS tc;
 
-int NT_set_process_priority(void)
+
+extern double sys_residual;    /* residual from previous adjustment */
+
+char szMsgPath[255];
+BOOL init_randfile();
+
+static long last_Adj = 0;
+
+#define LS_CORR_INTV_SECS  2   /* seconds to apply leap second correction */
+#define LS_CORR_INTV   ( (LONGLONG) HECTONANOSECONDS * LS_CORR_INTV_SECS )  
+#define LS_CORR_LIMIT  ( (LONGLONG) HECTONANOSECONDS / 2 )  // half a second
+
+typedef union
 {
-       if (!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)) 
-               {
-               msyslog(LOG_ERR, "SetPriorityClass: %m"); 
-               return 0;
-               }
-       else 
-               return 1;
-}
+       FILETIME ft;
+       ULONGLONG ull;
+} FT_ULL;
+
+static FT_ULL ls_ft;
+static DWORD ls_time_adjustment;
+static LARGE_INTEGER ls_ref_perf_cnt;
+static LONGLONG ls_elapsed;
+
+static void StartClockThread(void);
+static void StopClockThread(void);
+
+
+static CRITICAL_SECTION TimerCritialSection; /* lock for LastTimerCount & LastTimerTime */
+
+static ULONGLONG RollOverCount = 0;
+static ULONGLONG LastTimerCount = 0;
+static ULONGLONG LastTimerTime = 0;
+
+static HANDLE ClockThreadHandle = NULL;
+static HANDLE TimerThreadExitRequest = NULL;
+
+static DWORD every = 0;
+static DWORD initial_units_per_tick = 0;
+static DWORD lastLowTimer = 0;
+
+ULONGLONG PerfFrequency = 0;
+static DWORD units_per_tick = 0;
+static DOUBLE ppm_per_adjust_unit = 0.0;
 
 /*
- * refclock_open - open serial port for reference clock
- *
- * This routine opens a serial port for I/O and sets default options. It
- * returns the file descriptor if success and zero if failure.
+ * Request Multimedia Timer
+ */
+void
+set_mm_timer(int timerres)
+{
+       modify_mm_timer = timerres;
+}
+/*
+ * adj_systime - called once every second to make system time adjustments.
+ * Returns 1 if okay, 0 if trouble.
  */
 int
-refclock_open(
-       char *dev,              /* device name pointer */
-       u_int speed,            /* serial port speed (code) */
-       u_int flags             /* line discipline flags */
+adj_systime(
+       double now
        )
 {
-       HANDLE Handle = INVALID_HANDLE_VALUE;
-       COMMTIMEOUTS timeouts;
-       DCB dcb = {0};
-
-       //
-       // open communication port handle
-       //
-       Handle = CreateFile(dev,
-               GENERIC_READ | GENERIC_WRITE,
-               0, // no sharing
-               NULL, // no security
-               OPEN_EXISTING,
-               FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
-               NULL); // not template
-       if (Handle == INVALID_HANDLE_VALUE) {  
-                
-               msyslog(LOG_ERR, "NT_COM: Device %s: CreateFile error: %m", dev);
-               return -1;
+       double dtemp;
+       u_char isneg = 0;
+       int rc;
+       long dwTimeAdjustment;
+
+       /*
+        * Add the residual from the previous adjustment to the new
+        * adjustment, bound and round.
+        */
+       dtemp = sys_residual + now;
+       sys_residual = 0;
+       if (dtemp < 0)
+       {
+               isneg = 1;
+               dtemp = -dtemp;
        }
 
-       /*  Change the input/output buffers to be large.
-       */
-       if (!SetupComm( Handle, 1024, 1024)) {
-               msyslog(LOG_ERR, "NT_COM: Device %s: SetupComm error: %m", dev);
-               return -1;
-       }
+       if (dtemp > NTP_MAXFREQ)
+               dtemp = NTP_MAXFREQ;
 
-       dcb.DCBlength = sizeof(dcb);
-       if (!GetCommState(Handle, &dcb)) {
-               // Error getting current DCB settings
-               msyslog(LOG_ERR, "NT_COM: Device %s: GetCommState error: %m", dev);
-               return -1;
-       }
+       dtemp = dtemp * 1e6;
 
-       switch (speed) {
-         case B300 :   dcb.BaudRate = 300; break;
-         case B1200 :  dcb.BaudRate = 1200; break;
-         case B2400 :  dcb.BaudRate = 2400; break;
-         case B4800 :  dcb.BaudRate = 4800; break;
-         case B9600 :  dcb.BaudRate = 9600; break;
-         case B19200 : dcb.BaudRate = 19200; break;
-         case B38400 : dcb.BaudRate = 38400; break;
-         default :
-               msyslog(LOG_ERR, "NT_COM: Device %s: unsupported baud rate", dev);
-               return -1;
-       }
+       if (isneg)
+               dtemp = -dtemp;
 
+       /* dtemp is in micro seconds. NT uses 100 ns units,
+        * so a unit change in dwTimeAdjustment corresponds
+        * to slewing 10 ppm on a 100 Hz system.
+        * Calculate the number of 100ns units to add,
+        * using OS tick frequency as per suggestion from Harry Pyle,
+        * and leave the remainder in dtemp */
+       dwTimeAdjustment = (DWORD)( dtemp / ppm_per_adjust_unit + (isneg ? -0.5 : 0.5)) ;
+       dtemp += (double) -dwTimeAdjustment * ppm_per_adjust_unit;      
 
-       dcb.ByteSize = 8;
-       dcb.fBinary = TRUE;
-       dcb.fParity = TRUE;
-       dcb.fOutxCtsFlow = 0;
-       dcb.fOutxDsrFlow = 0;
-       dcb.fDtrControl = DTR_CONTROL_DISABLE;
-       dcb.fDsrSensitivity = 0;
-       dcb.fTXContinueOnXoff = FALSE;
-       dcb.fOutX = 0; 
-       dcb.fInX = 0;
-       dcb.fErrorChar = 0;
-       dcb.fNull = 0;
-       dcb.fRtsControl = RTS_CONTROL_DISABLE; // RTS_CONTROL_DISABLE;
-       dcb.fAbortOnError = 0;
-       dcb.ByteSize = 8;
-       dcb.StopBits = ONESTOPBIT;
-       dcb.Parity = NOPARITY;
-       dcb.ErrorChar = 0;
-       dcb.EvtChar = 0;
-       dcb.EofChar = 0;
-
-       if (!SetCommState(Handle, &dcb)) {
-               msyslog(LOG_ERR, "NT_COM: Device %s: SetCommState error: %m", dev);
-               return -1;
-       }
 
-       timeouts.ReadIntervalTimeout = 20; 
-       timeouts.ReadTotalTimeoutMultiplier = 0;
-       timeouts.ReadTotalTimeoutConstant = 5000;
-       timeouts.WriteTotalTimeoutMultiplier = 0;
-       timeouts.WriteTotalTimeoutConstant = 5000;
+  /* If a leap second is pending then determine the UTC time stamp 
+        * of when the insertion must take place */
+       if (leap_next & LEAP_ADDSECOND)  
+       {
+               if ( ls_ft.ull == 0 )  /* time stamp has not yet been computed */
+               {
+                       FT_ULL ft;
+                       SYSTEMTIME st;
+                       int itmp;
+
+                       GetSystemTimeAsFileTime(&ft.ft);   
+                       FileTimeToSystemTime(&ft.ft, &st);
+
+                       /* Accept leap announcement only 1 month in advance,
+                        * for end of March, June, September, or December.
+                        */
+                       if ( ( st.wMonth % 3 ) == 0 )
+                       {
+                               /* The comarison time stamp is computed according 
+                                * to 0:00h UTC of the following day */   
+                               if ( ++st.wMonth > 12 )
+                               {
+                                       st.wMonth -= 12;
+                                       st.wYear++;
+                               }
+                               
+                               st.wDay = 1;
+                               st.wHour = 0;
+                               st.wMinute = 0;
+                               st.wSecond = 0;
+                               st.wMilliseconds = 0;
+
+                               SystemTimeToFileTime(&st, &ls_ft.ft);
+                               msyslog(LOG_INFO, "Detected positive leap second announcement "
+                                                 "for %04d-%02d-%02d %02d:%02d:%02d UTC",
+                                                                                                       st.wYear, st.wMonth, st.wDay,
+                                                 st.wHour, st.wMinute, st.wSecond);
+                       }
+               }
+  }
 
-          // Error setting time-outs.
-       if (!SetCommTimeouts(Handle, &timeouts)) {
-               msyslog(LOG_ERR, "NT_COM: Device %s: SetCommTimeouts error: %m", dev);
-               return -1;
-       }
+  /* If the time stamp for the next leap second has been set
+        * then check if the leap second must be handled */
+       if ( ls_ft.ull )
+       {
+               LARGE_INTEGER this_perf_count;
 
-       return (int) Handle;
-}
+               QueryPerformanceCounter( &this_perf_count );
 
+               if ( ls_time_adjustment == 0 ) /* has not yet been scheduled */
+               {
+                       FT_ULL curr_ft;
+
+                       GetSystemTimeAsFileTime(&curr_ft.ft);   
+                       if ( curr_ft.ull >= ls_ft.ull )
+                       {
+                               ls_time_adjustment = every / LS_CORR_INTV_SECS;
+                               ls_ref_perf_cnt = this_perf_count;
+                               ls_elapsed = 0;
+                               msyslog(LOG_INFO, "Inserting positive leap second.");
+                       }
+               }
+               else  /* leap sec adjustment has been scheduled previously */
+               {
+                       ls_elapsed = ( this_perf_count.QuadPart - ls_ref_perf_cnt.QuadPart ) 
+                                      * HECTONANOSECONDS / PerfFrequency;
+               }
+
+               if ( ls_time_adjustment )  /* leap second adjustment is currently active */
+               {
+                       if ( ls_elapsed > ( LS_CORR_INTV - LS_CORR_LIMIT ) )
+                       {
+                               ls_time_adjustment = 0;  /* leap second adjustment done */
+                               ls_ft.ull = 0;
+                       }
+
+               /* NOTE: While the system time is slewed during the leap second 
+                * the interpolation function which is based on the performance 
+                * counter does not account for the slew.
+                */
+               dwTimeAdjustment -= ls_time_adjustment;
+               }
+       }
 
-int 
-ioctl(int fd,
-         int cmd,
-         int *x) {
 
-       if ((cmd == TIOCMSET) && (*x & TIOCM_RTS)) {
-               if (!EscapeCommFunction((HANDLE) fd, SETRTS)) 
-                       return -1;
+       /* only adjust the clock if adjustment changes */
+       if (last_Adj != dwTimeAdjustment) {     
+                       last_Adj = dwTimeAdjustment;
+# ifdef DEBUG
+               if (debug > 1)
+                       printf("SetSystemTimeAdjustment( %ld) + (%ld)\n", dwTimeAdjustment, units_per_tick);                    
+# endif
+                       dwTimeAdjustment += units_per_tick;
+                       rc = !SetSystemTimeAdjustment(dwTimeAdjustment, FALSE);
+       }
+       else rc = 0;
+       if (rc)
+       {
+               msyslog(LOG_ERR, "Can't adjust time: %m");
+               return 0;
        }
-       else if ((cmd == TIOCMSET) && !(*x & TIOCM_RTS)){
-               if (!EscapeCommFunction((HANDLE) fd, CLRRTS)) 
-                       return -1;
+       else {
+               sys_residual = dtemp / 1000000.0;
        }
 
-       return 0;
+#ifdef DEBUG
+       if (debug > 6)
+               printf("adj_systime: adj %.9f -> remaining residual %.9f\n", now, sys_residual);
+#endif
+       return 1;
 }
 
 
-int    
-tcsetattr(
-       int fd, 
-       int optional_actions, 
-       const struct termios * s)
+void init_winnt_time(void)
 {
-       DCB dcb = { 0 };
-       HANDLE Handle = (HANDLE) fd;
-       dcb.DCBlength = sizeof(dcb);
-       if (!GetCommState(Handle, &dcb)) {
-               // Error getting current DCB settings
-               msyslog(LOG_ERR, "NT_COM: GetCommState error: %m");
-               return FALSE;
+       BOOL noslew;
+       HANDLE hToken = INVALID_HANDLE_VALUE;
+       TOKEN_PRIVILEGES tkp;
+
+       /*
+        * Make sure the service is initialized
+        * before we do anything else
+        */
+       ntservice_init();
+
+       /* Set the Event-ID message-file name. */
+       if (!GetModuleFileName(NULL, szMsgPath, sizeof(szMsgPath))) 
+       {
+               msyslog(LOG_ERR, "GetModuleFileName(PGM_EXE_FILE) failed: %m\n");
+               exit(1);
        }
 
-       switch (max(s->c_ospeed, s->c_ispeed)) {
-               case B300 :   dcb.BaudRate = 300; break;
-               case B1200 :  dcb.BaudRate = 1200; break;
-               case B2400 :  dcb.BaudRate = 2400; break;
-               case B4800 :  dcb.BaudRate = 4800; break;
-               case B9600 :  dcb.BaudRate = 9600; break;
-               case B19200 : dcb.BaudRate = 19200; break;
-               case B38400 : dcb.BaudRate = 38400; break;
-               default :
-                       msyslog(LOG_ERR, "NT_COM: unsupported baud rate");
-                       return FALSE;
-       }
+       /* Initialize random file before OpenSSL checks */
+       if(!init_randfile())
+               msyslog(LOG_ERR, "Unable to initialize .rnd file\n");
 
-       switch (s->c_cflag & CSIZE) {
-               case CS5 : dcb.ByteSize = 5; break;
-               case CS6 : dcb.ByteSize = 6; break;
-               case CS7 : dcb.ByteSize = 7; break;
-               case CS8 : dcb.ByteSize = 8; break;
-               default :
-                       msyslog(LOG_ERR, "NT_COM: unsupported word size");
-                       return FALSE;
-       }
+       /*
+        * Get privileges needed for fiddling with the clock
+        */
 
-       if (s->c_cflag & PARENB) {
-               dcb.fParity = TRUE;
-               if (s->c_cflag & PARODD) {
-                       dcb.Parity = ODDPARITY;
-               }
-               else {
-                       dcb.Parity = EVENPARITY;
-               }
+         /* get the current process token handle */
+       if (!OpenProcessToken(GetCurrentProcess(),
+            TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) 
+       {
+               msyslog(LOG_ERR, "OpenProcessToken failed: %m");
+               exit(-1);
        }
-       else {
-               dcb.fParity = FALSE;
-               dcb.Parity = NOPARITY;
+         /* get the LUID for system-time privilege. */
+       LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid);
+       tkp.PrivilegeCount = 1;  /* one privilege to set */
+       tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+       /* get set-time privilege for this process. */
+       AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
+               (PTOKEN_PRIVILEGES) NULL, 0);
+
+       /* cannot use return value of AdjustTokenPrivileges. */
+       /* (success does not indicate all privileges were set) */
+       if (GetLastError() != ERROR_SUCCESS) 
+       {
+               msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m");
+               /* later set time call will probably fail */
        }
 
+       /* Reset the Clock to a reasonable increment */
+       if (!GetSystemTimeAdjustment(&initial_units_per_tick, &every,&noslew))
+       {
+               msyslog(LOG_ERR, "GetSystemTimeAdjustment failed: %m\n");
+               exit(-1);
+       }
+
+       units_per_tick = initial_units_per_tick;
+
+       /* Calculate the time adjustment resulting from incrementing
+        * units per tick by 1 unit for 1 second */
+       ppm_per_adjust_unit = 1000000.0 / (double) every;
+
+#ifdef DEBUG
+       msyslog(LOG_INFO, "Initial Clock increment %7.1f us",
+                       (float) (units_per_tick / 10));
+       msyslog(LOG_INFO, "Adjustment rate %5.3f ppm/s", ppm_per_adjust_unit);
+#endif
 
-       dcb.fOutxCtsFlow = 0;
-       dcb.fOutxDsrFlow = 0;
-       dcb.fDtrControl = DTR_CONTROL_DISABLE;
-       dcb.fDsrSensitivity = 0;
-       dcb.fOutX = 0; 
-       dcb.fInX = 0;
-       dcb.fErrorChar = 0;
-       dcb.fNull = 0;
-       dcb.fRtsControl = RTS_CONTROL_DISABLE;
-       dcb.fAbortOnError = 0;
-       dcb.ErrorChar = 0;
-       dcb.EvtChar = 0;
-       dcb.EofChar = 0;
-
-       if (!SetCommState(Handle, &dcb)) {
-               msyslog(LOG_ERR, "NT_COM: SetCommState error: %m");
-               return FALSE;
+       StartClockThread();
+
+       /* Set up the Console Handler */
+       if (!SetConsoleCtrlHandler(OnConsoleEvent, TRUE))
+       {
+               msyslog(LOG_ERR, "Can't set console control handler: %m");
        }
-       return TRUE;
 }
 
-extern int     
-tcgetattr(
-       int fd, struct termios *s)
+
+void reset_winnt_time(void)
 {
-       DCB dcb = { 0 };
-       HANDLE Handle = (HANDLE) fd;
-       dcb.DCBlength = sizeof(dcb);
-       if (!GetCommState(Handle, &dcb)) {
-               // Error getting current DCB settings
-               msyslog(LOG_ERR, "NT_COM: GetCommState error: %m");
-               return FALSE;
+       /* restore the clock frequency back to its original value */
+       if (!SetSystemTimeAdjustment(0, TRUE)) 
+       {
+               msyslog(LOG_ERR, "Failed to reset clock state, SetSystemTimeAdjustment(): %m");
        }
+       /* read the current system time, and write it back to
+           force CMOS update: */
+      /************ Added back in 2003-01-26 *****************/
+       {
+               SYSTEMTIME st;
+               GetSystemTime(&st);
+               SetSystemTime(&st);
+       }
+}
 
-       /*  Set c_ispeed & c_ospeed
-       */
-       switch (dcb.BaudRate) {
-               case 300 : s->c_ispeed = s->c_ospeed = B300; break;
-               case 1200 : s->c_ispeed = s->c_ospeed = B1200; break;
-               case 2400 : s->c_ispeed = s->c_ospeed = B2400; break;
-               case 4800 : s->c_ispeed = s->c_ospeed = B4800; break;
-               case 9600 : s->c_ispeed = s->c_ospeed = B9600; break;
-               case 19200 : s->c_ispeed = s->c_ospeed = B19200; break;
-               case 38400 : s->c_ispeed = s->c_ospeed = B38400; break;
-               default : s->c_ispeed = s->c_ospeed = B9600;
+
+
+int
+gettimeofday(
+       struct timeval *tv
+       )
+{
+       /*  Use the system time (roughly synchronised to the tick, and
+        *  extrapolated using the system performance counter.
+        */
+
+       ULONGLONG Count;
+       LARGE_INTEGER LargeIntNowCount;
+       ULONGLONG Time;
+       ULONGLONG NowCount;
+       ULONGLONG PreCount;                                                  /*FIX*/
+       LONGLONG TicksElapsed;
+       LONG time_adjustment;
+
+       /*  Mark a mark ASAP. The latency to here should
+        *  be reasonably deterministic
+        */
+
+       PreCount = LastTimerCount;                                           /*FIX*/
+
+       if (!QueryPerformanceCounter(&LargeIntNowCount)) {
+               msyslog(LOG_ERR, "QueryPeformanceCounter failed: %m");
+               exit(1);
        }
-       
 
-       s->c_cflag = 0;
-       switch (dcb.ByteSize) {
-               case 5 : s->c_cflag |= CS5; break;
-               case 6 : s->c_cflag |= CS6; break;
-               case 7 : s->c_cflag |= CS7; break;
-               case 8 : s->c_cflag |= CS8; break;
+       NowCount = LargeIntNowCount.QuadPart;
+
+       /*  Get base time we are going to extrapolate from
+        */     
+       EnterCriticalSection(&TimerCritialSection);
+       Count = LastTimerCount;
+       Time = LastTimerTime;
+       LeaveCriticalSection(&TimerCritialSection);
+
+       /*  Calculate when now is.
+        *
+        *  Result = LastTimerTime +  (NowCount - LastTimerCount) / PerfFrequency
+        */
+
+       if (NowCount >= Count)
+       {
+               TicksElapsed = NowCount - Count; /* linear progression of ticks */
        }
-       if (dcb.fParity) {
-                 s->c_cflag |= PARENB;
+       else
+       {
+       /************************************************************************/
+       /* Differentiate between real rollover and the case of taking a         */
+       /* perfcount then the APC coming in.                                    */
+       /************************************************************************/
+               if (Count > PreCount)                                           /*FIX*/
+               {                                                               /*FIX*/
+                       TicksElapsed = 0;                                       /*FIX*/
+               }                                                               /*FIX*/
+               else                                                            /*FIX*/
+               {                                                               /*FIX*/
+                       TicksElapsed = NowCount + (RollOverCount - Count);      /*FIX*/
+               }                                                               /*FIX*/
        }
-       switch (dcb.Parity) {
-               case EVENPARITY : break;
-               case MARKPARITY : break;
-               case NOPARITY : break;
-               case ODDPARITY : s->c_cflag |= PARODD; break;
-               case SPACEPARITY : break;
+
+       /*  Calculate the new time (in 100's of nano-seconds)
+        */
+       time_adjustment = (long) ((TicksElapsed * HECTONANOSECONDS) / PerfFrequency);
+       Time += time_adjustment;
+
+       /* Convert the hecto-nano second time to tv format
+        */
+       Time -= FILETIME_1970;
+       tv->tv_sec = (LONG) ( Time / 10000000ui64);
+       tv->tv_usec = (LONG) (( Time % 10000000ui64) / 10);
+
+       return 0;
+}
+
+static void CALLBACK
+TimerApcFunction(
+       LPVOID lpArgToCompletionRoutine,
+       DWORD dwTimerLowValue,
+       DWORD dwTimerHighValue
+       )
+{
+       LARGE_INTEGER LargeIntNowCount;
+       (void) lpArgToCompletionRoutine; /* not used */
+
+       if (dwTimerLowValue == lastLowTimer) return;
+       
+       /* Grab the counter first of all */
+       QueryPerformanceCounter(&LargeIntNowCount);
+
+       /* Save this for next time */
+       lastLowTimer = dwTimerLowValue;
+
+       /* Check to see if the counter has rolled. This happens
+          more often on Multi-CPU systems */
+
+       if ((ULONGLONG) LargeIntNowCount.QuadPart < LastTimerCount) {
+               /* Counter Rolled - try and estimate the rollover point using
+                 the nominal counter frequency divided by an estimate of the
+                 OS frequency */
+               RollOverCount = LastTimerCount + PerfFrequency * every /  HECTONANOSECONDS -
+                       (ULONGLONG) LargeIntNowCount.QuadPart;
+#ifdef DEBUG
+               msyslog(LOG_INFO,
+                       "Performance Counter Rollover %I64u:\rLast Timer Count %I64u\rCurrent Count %I64u",
+                               RollOverCount, LastTimerCount, LargeIntNowCount.QuadPart);
+#endif
        }
 
-       s->c_iflag = 0;
-       s->c_lflag = 0;
-       s->c_line = 0;
-       s->c_oflag = 0;
+       /* Now we can hang out and wait for the critical section to free up;
+          we will get the CPU this timeslice. Meanwhile other tasks can use
+          the last value of LastTimerCount */
+               
+       EnterCriticalSection(&TimerCritialSection);
+       LastTimerCount = (ULONGLONG) LargeIntNowCount.QuadPart;
+       LastTimerTime = ((ULONGLONG) dwTimerHighValue << 32) +
+                        (ULONGLONG) dwTimerLowValue;
+       LeaveCriticalSection(&TimerCritialSection);
+}
+
+
 
-       return TRUE; /* ok */
+DWORD WINAPI ClockThread(void *arg)
+{
+
+       LARGE_INTEGER DueTime;
+       HANDLE WaitableTimerHandle = CreateWaitableTimer(NULL, FALSE, NULL);
+
+       (void) arg; /* not used */
+
+       /*++++ Gerhard Junker
+       * see Platform SDK for QueryPerformanceCounter
+       * On a multiprocessor machine, it should not matter which processor is called. 
+       * However, you can get different results on different processors due to bugs in the BIOS or the HAL. 
+       * To specify processor affinity for a thread, use the SetThreadAffinityMask function. 
+       * ... we will hope, the apc routine will run on the same processor
+       */
+
+       SetThreadAffinityMask(GetCurrentThread(), 1L);
+
+       /*---- Gerhard Junker */
+
+       if (WaitableTimerHandle != NULL) {
+               DueTime.QuadPart = 0i64;
+               if (SetWaitableTimer(WaitableTimerHandle, &DueTime, 1L /* ms */, TimerApcFunction, &WaitableTimerHandle, FALSE) != NO_ERROR) {
+                       for(;;) {
+                               if (WaitForSingleObjectEx(TimerThreadExitRequest, INFINITE, TRUE) == WAIT_OBJECT_0) {
+                                       break; /* we've been asked to exit */
+                               }
+                       }
+               }
+               CloseHandle(WaitableTimerHandle);
+               WaitableTimerHandle = NULL;
+       }
+       return 0;
 }
 
 
-extern int tcflush(int fd, int mode) {
+static void StartClockThread(void)
+{
+       DWORD tid;
+       FILETIME StartTime;
+       LARGE_INTEGER Freq = { 0, 0 };
+       
+       /* get the performance counter freq */
+       if (!QueryPerformanceFrequency(&Freq))
+       {
+               msyslog(LOG_ERR, "QueryPerformanceFrequency failed: %m\n");
+               exit (-1);
+       }
+
+       PerfFrequency = Freq.QuadPart;
+
+
+       if ( modify_mm_timer != 0)
+       {
+               if (timeGetDevCaps( &tc, sizeof( tc ) ) == TIMERR_NOERROR ) 
+               {
+                       wTimerRes = min( max( tc.wPeriodMin, MM_TIMER_INTV ), tc.wPeriodMax );
+
+                       timeBeginPeriod( wTimerRes );
+#ifdef DEBUG
+                       msyslog( LOG_INFO, "MM timer resolution: %u..%u ms, set to %u ms\n",
+                                tc.wPeriodMin, tc.wPeriodMax, wTimerRes );
+#endif
+               }
+               else
+                       msyslog( LOG_ERR, "Failed to get MM timer caps\n" );
+       }
 
 
+       /* init variables with the time now */
+       GetSystemTimeAsFileTime(&StartTime);
+       LastTimerTime = (((ULONGLONG) StartTime.dwHighDateTime) << 32) +
+                         (ULONGLONG) StartTime.dwLowDateTime;
 
+       /* init sync objects */
+       InitializeCriticalSection(&TimerCritialSection);
+       TimerThreadExitRequest = CreateEvent(NULL, FALSE, FALSE, "TimerThreadExitRequest");
 
-return 0;
+       ClockThreadHandle = CreateThread(NULL, 0, ClockThread, NULL, 0, &tid);
+       if (ClockThreadHandle != NULL)
+       {
+               /* remember the thread priority is only within the process class */
+               if (!SetThreadPriority(ClockThreadHandle, THREAD_PRIORITY_TIME_CRITICAL)) 
+               {
+#ifdef DEBUG
+                       printf("Error setting thread priority\n");
+#endif
+                 }
+       }
 
+       atexit( StopClockThread );
 }
 
-extern int cfsetispeed(struct termios *tio, int speed) {
-               
-return 0;              
-};     
+static void StopClockThread(void)
+{      
+       if ( wTimerRes )  /* if not 0 then the MM timer has been modified at startup */
+       {
+               timeEndPeriod( wTimerRes ); 
+               wTimerRes = 0;
+#ifdef DEBUG
+               msyslog( LOG_INFO, "MM timer set to default\n" );
+#endif
+       }
 
+       if (SetEvent(TimerThreadExitRequest) &&
+           WaitForSingleObject(ClockThreadHandle, 10000L) == 0)
+       {
+               CloseHandle(TimerThreadExitRequest);
+               TimerThreadExitRequest = NULL;
 
-extern int cfsetospeed(struct termios *tio, int speed) {
-               
-return 0;              
-};     
+               CloseHandle(ClockThreadHandle);
+               ClockThreadHandle = NULL;
 
+               DeleteCriticalSection(&TimerCritialSection);
+       }
+       else
+       {
+               msyslog(LOG_ERR, "Failed to stop clock thread.");
+               Sleep( 100 );
+       }
+}