ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

timer.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.