Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygentimer.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS HAL 00003 * LICENSE: GPL - See COPYING in the top level directory 00004 * FILE: hal/halx86/generic/timer.c 00005 * PURPOSE: HAL Timer Routines 00006 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 00007 * Timo Kreuzer (timo.kreuzer@reactos.org) 00008 */ 00009 00010 /* INCLUDES ******************************************************************/ 00011 00012 #include <hal.h> 00013 #define NDEBUG 00014 #include <debug.h> 00015 00016 /* GLOBALS *******************************************************************/ 00017 00018 #define PIT_LATCH 0x00 00019 00020 LARGE_INTEGER HalpLastPerfCounter; 00021 LARGE_INTEGER HalpPerfCounter; 00022 ULONG HalpPerfCounterCutoff; 00023 BOOLEAN HalpClockSetMSRate; 00024 ULONG HalpCurrentTimeIncrement; 00025 ULONG HalpCurrentRollOver; 00026 ULONG HalpNextMSRate = 14; 00027 ULONG HalpLargestClockMS = 15; 00028 00029 static struct _HALP_ROLLOVER 00030 { 00031 ULONG RollOver; 00032 ULONG Increment; 00033 } HalpRolloverTable[15] = 00034 { 00035 {1197, 10032}, 00036 {2394, 20064}, 00037 {3591, 30096}, 00038 {4767, 39952}, 00039 {5964, 49984}, 00040 {7161, 60016}, 00041 {8358, 70048}, 00042 {9555, 80080}, 00043 {10731, 89936}, 00044 {11949, 100144}, 00045 {13125, 110000}, 00046 {14322, 120032}, 00047 {15519, 130064}, 00048 {16695, 139920}, 00049 {17892, 149952} 00050 }; 00051 00052 /* PRIVATE FUNCTIONS *********************************************************/ 00053 00054 FORCEINLINE 00055 ULONG 00056 HalpRead8254Value(void) 00057 { 00058 ULONG TimerValue; 00059 00060 /* Send counter latch command for channel 0 */ 00061 __outbyte(TIMER_CONTROL_PORT, PIT_LATCH); 00062 __nop(); 00063 00064 /* Read the value, LSB first */ 00065 TimerValue = __inbyte(TIMER_CHANNEL0_DATA_PORT); 00066 __nop(); 00067 TimerValue |= __inbyte(TIMER_CHANNEL0_DATA_PORT) << 8; 00068 00069 return TimerValue; 00070 } 00071 00072 VOID 00073 NTAPI 00074 HalpSetTimerRollOver(USHORT RollOver) 00075 { 00076 ULONG_PTR Flags; 00077 TIMER_CONTROL_PORT_REGISTER TimerControl; 00078 00079 /* Disable interrupts */ 00080 Flags = __readeflags(); 00081 _disable(); 00082 00083 /* Program the PIT for binary mode */ 00084 TimerControl.BcdMode = FALSE; 00085 00086 /* 00087 * Program the PIT to generate a normal rate wave (Mode 3) on channel 0. 00088 * Channel 0 is used for the IRQ0 clock interval timer, and channel 00089 * 1 is used for DRAM refresh. 00090 * 00091 * Mode 2 gives much better accuracy than Mode 3. 00092 */ 00093 TimerControl.OperatingMode = PitOperatingMode2; 00094 TimerControl.Channel = PitChannel0; 00095 00096 /* Set the access mode that we'll use to program the reload value */ 00097 TimerControl.AccessMode = PitAccessModeLowHigh; 00098 00099 /* Now write the programming bits */ 00100 __outbyte(TIMER_CONTROL_PORT, TimerControl.Bits); 00101 00102 /* Next we write the reload value for channel 0 */ 00103 __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver & 0xFF); 00104 __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver >> 8); 00105 00106 /* Restore interrupts if they were previously enabled */ 00107 __writeeflags(Flags); 00108 } 00109 00110 VOID 00111 NTAPI 00112 INIT_FUNCTION 00113 HalpInitializeClock(VOID) 00114 { 00115 ULONG Increment; 00116 USHORT RollOver; 00117 00118 DPRINT("HalpInitializeClock()\n"); 00119 00120 /* Get increment and rollover for the largest time clock ms possible */ 00121 Increment = HalpRolloverTable[HalpLargestClockMS - 1].Increment; 00122 RollOver = (USHORT)HalpRolloverTable[HalpLargestClockMS - 1].RollOver; 00123 00124 /* Set the maximum and minimum increment with the kernel */ 00125 KeSetTimeIncrement(Increment, HalpRolloverTable[0].Increment); 00126 00127 /* Set the rollover value for the timer */ 00128 HalpSetTimerRollOver(RollOver); 00129 00130 /* Save rollover and increment */ 00131 HalpCurrentRollOver = RollOver; 00132 HalpCurrentTimeIncrement = Increment; 00133 } 00134 00135 #ifdef _M_IX86 00136 #ifndef _MINIHAL_ 00137 VOID 00138 FASTCALL 00139 HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame) 00140 { 00141 ULONG LastIncrement; 00142 KIRQL Irql; 00143 00144 /* Enter trap */ 00145 KiEnterInterruptTrap(TrapFrame); 00146 00147 /* Start the interrupt */ 00148 if (HalBeginSystemInterrupt(CLOCK2_LEVEL, PRIMARY_VECTOR_BASE, &Irql)) 00149 { 00150 /* Update the performance counter */ 00151 HalpPerfCounter.QuadPart += HalpCurrentRollOver; 00152 HalpPerfCounterCutoff = KiEnableTimerWatchdog; 00153 00154 /* Save increment */ 00155 LastIncrement = HalpCurrentTimeIncrement; 00156 00157 /* Check if someone changed the time rate */ 00158 if (HalpClockSetMSRate) 00159 { 00160 /* Update the global values */ 00161 HalpCurrentTimeIncrement = HalpRolloverTable[HalpNextMSRate - 1].Increment; 00162 HalpCurrentRollOver = HalpRolloverTable[HalpNextMSRate - 1].RollOver; 00163 00164 /* Set new timer rollover */ 00165 HalpSetTimerRollOver((USHORT)HalpCurrentRollOver); 00166 00167 /* We're done */ 00168 HalpClockSetMSRate = FALSE; 00169 } 00170 00171 /* Update the system time -- the kernel will exit this trap */ 00172 KeUpdateSystemTime(TrapFrame, LastIncrement, Irql); 00173 } 00174 00175 /* Spurious, just end the interrupt */ 00176 KiEoiHelper(TrapFrame); 00177 } 00178 00179 VOID 00180 FASTCALL 00181 HalpProfileInterruptHandler(IN PKTRAP_FRAME TrapFrame) 00182 { 00183 KIRQL Irql; 00184 00185 /* Enter trap */ 00186 KiEnterInterruptTrap(TrapFrame); 00187 00188 /* Start the interrupt */ 00189 if (HalBeginSystemInterrupt(PROFILE_LEVEL, PRIMARY_VECTOR_BASE + 8, &Irql)) 00190 { 00191 /* Profiling isn't yet enabled */ 00192 UNIMPLEMENTED; 00193 ASSERT(FALSE); 00194 } 00195 00196 /* Spurious, just end the interrupt */ 00197 KiEoiHelper(TrapFrame); 00198 } 00199 #endif 00200 00201 #endif 00202 00203 /* PUBLIC FUNCTIONS ***********************************************************/ 00204 00205 /* 00206 * @implemented 00207 */ 00208 VOID 00209 NTAPI 00210 HalCalibratePerformanceCounter(IN volatile PLONG Count, 00211 IN ULONGLONG NewCount) 00212 { 00213 ULONG_PTR Flags; 00214 00215 /* Disable interrupts */ 00216 Flags = __readeflags(); 00217 _disable(); 00218 00219 /* Do a decrement for this CPU */ 00220 _InterlockedDecrement(Count); 00221 00222 /* Wait for other CPUs */ 00223 while (*Count); 00224 00225 /* Restore interrupts if they were previously enabled */ 00226 __writeeflags(Flags); 00227 } 00228 00229 /* 00230 * @implemented 00231 */ 00232 ULONG 00233 NTAPI 00234 HalSetTimeIncrement(IN ULONG Increment) 00235 { 00236 /* Round increment to ms */ 00237 Increment /= 10000; 00238 00239 /* Normalize between our minimum (1 ms) and maximum (variable) setting */ 00240 if (Increment > HalpLargestClockMS) Increment = HalpLargestClockMS; 00241 if (Increment <= 0) Increment = 1; 00242 00243 /* Set the rate and tell HAL we want to change it */ 00244 HalpNextMSRate = Increment; 00245 HalpClockSetMSRate = TRUE; 00246 00247 /* Return the increment */ 00248 return HalpRolloverTable[Increment - 1].Increment; 00249 } 00250 00251 LARGE_INTEGER 00252 NTAPI 00253 KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFrequency) 00254 { 00255 LARGE_INTEGER CurrentPerfCounter; 00256 ULONG CounterValue, ClockDelta; 00257 KIRQL OldIrql; 00258 00259 /* If caller wants performance frequency, return hardcoded value */ 00260 if (PerformanceFrequency) PerformanceFrequency->QuadPart = PIT_FREQUENCY; 00261 00262 /* Check if we were called too early */ 00263 if (HalpCurrentRollOver == 0) return HalpPerfCounter; 00264 00265 /* Check if interrupts are disabled */ 00266 if(!(__readeflags() & EFLAGS_INTERRUPT_MASK)) return HalpPerfCounter; 00267 00268 /* Raise irql to DISPATCH_LEVEL */ 00269 OldIrql = KeGetCurrentIrql(); 00270 if (OldIrql < DISPATCH_LEVEL) KfRaiseIrql(DISPATCH_LEVEL); 00271 00272 do 00273 { 00274 /* Get the current performance counter value */ 00275 CurrentPerfCounter = HalpPerfCounter; 00276 00277 /* Read the 8254 counter value */ 00278 CounterValue = HalpRead8254Value(); 00279 00280 /* Repeat if the value has changed (a clock interrupt happened) */ 00281 } while (CurrentPerfCounter.QuadPart != HalpPerfCounter.QuadPart); 00282 00283 /* After someone changed the clock rate, during the first clock cycle we 00284 might see a counter value larger than the rollover. In this case we 00285 pretend it already has the new rollover value. */ 00286 if (CounterValue > HalpCurrentRollOver) CounterValue = HalpCurrentRollOver; 00287 00288 /* The interrupt is issued on the falling edge of the OUT line, when the 00289 counter changes from 1 to max. Calculate a clock delta, so that directly 00290 after the interrupt it is 0, going up to (HalpCurrentRollOver - 1). */ 00291 ClockDelta = HalpCurrentRollOver - CounterValue; 00292 00293 /* Add the clock delta */ 00294 CurrentPerfCounter.QuadPart += ClockDelta; 00295 00296 /* Check if the value is smaller then before, this means, we somehow 00297 missed an interrupt. This is a sign that the timer interrupt 00298 is very inaccurate. Probably a virtual machine. */ 00299 if (CurrentPerfCounter.QuadPart < HalpLastPerfCounter.QuadPart) 00300 { 00301 /* We missed an interrupt. Assume we will receive it later */ 00302 CurrentPerfCounter.QuadPart += HalpCurrentRollOver; 00303 } 00304 00305 /* Update the last counter value */ 00306 HalpLastPerfCounter = CurrentPerfCounter; 00307 00308 /* Restore previous irql */ 00309 if (OldIrql < DISPATCH_LEVEL) KfLowerIrql(OldIrql); 00310 00311 /* Return the result */ 00312 return CurrentPerfCounter; 00313 } 00314 00315 /* EOF */ Generated on Sat May 26 2012 04:17:08 for ReactOS by
1.7.6.1
|