Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318/* * PROJECT: ReactOS HAL * LICENSE: GPL - See COPYING in the top level directory * PURPOSE: HAL Timer Routines * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) * Timo Kreuzer (timo.kreuzer@reactos.org) */
/* INCLUDES ******************************************************************/
#include <hal.h>
#define NDEBUG#include <debug.h>
/* GLOBALS *******************************************************************/
#define PIT_LATCH 0x00
extern HALP_ROLLOVER HalpRolloverTable[15];
LARGE_INTEGER HalpLastPerfCounter;LARGE_INTEGER HalpPerfCounter;ULONG HalpPerfCounterCutoff;BOOLEAN HalpClockSetMSRate;ULONG HalpCurrentTimeIncrement;ULONG HalpCurrentRollOver;ULONG HalpNextMSRate = 14;ULONG HalpLargestClockMS = 15;
/* PRIVATE FUNCTIONS *********************************************************/
FORCEINLINEULONGHalpRead8254Value(void){ ULONG TimerValue;
/* Send counter latch command for channel 0 */ __outbyte(TIMER_CONTROL_PORT, PIT_LATCH); __nop();
/* Read the value, LSB first */ TimerValue = __inbyte(TIMER_CHANNEL0_DATA_PORT); __nop(); TimerValue |= __inbyte(TIMER_CHANNEL0_DATA_PORT) << 8;
return TimerValue;}
VOIDNTAPIHalpSetTimerRollOver(USHORT RollOver){ ULONG_PTR Flags; TIMER_CONTROL_PORT_REGISTER TimerControl;
/* Disable interrupts */ Flags = __readeflags(); _disable();
/* Program the PIT for binary mode */ TimerControl.BcdMode = FALSE;
/* * Program the PIT to generate a normal rate wave (Mode 2) on channel 0. * Channel 0 is used for the IRQ0 clock interval timer, and channel * 1 is used for DRAM refresh. * * Mode 2 gives much better accuracy than Mode 3. */ TimerControl.OperatingMode = PitOperatingMode2; TimerControl.Channel = PitChannel0;
/* Set the access mode that we'll use to program the reload value */ TimerControl.AccessMode = PitAccessModeLowHigh;
/* Now write the programming bits */ __outbyte(TIMER_CONTROL_PORT, TimerControl.Bits);
/* Next we write the reload value for channel 0 */ __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver & 0xFF); __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver >> 8);
/* Restore interrupts if they were previously enabled */ __writeeflags(Flags);}
CODE_SEG("INIT")VOIDNTAPIHalpInitializeClock(VOID){ ULONG Increment; USHORT RollOver;
DPRINT("HalpInitializeClock()\n");
#if defined(SARCH_PC98) HalpInitializeClockPc98();#endif
/* Get increment and rollover for the largest time clock ms possible */ Increment = HalpRolloverTable[HalpLargestClockMS - 1].Increment; RollOver = (USHORT)HalpRolloverTable[HalpLargestClockMS - 1].RollOver;
/* Set the maximum and minimum increment with the kernel */ KeSetTimeIncrement(Increment, HalpRolloverTable[0].Increment);
/* Set the rollover value for the timer */ HalpSetTimerRollOver(RollOver);
/* Save rollover and increment */ HalpCurrentRollOver = RollOver; HalpCurrentTimeIncrement = Increment;}
#ifdef _M_IX86#ifndef _MINIHAL_VOIDFASTCALLHalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame){ ULONG LastIncrement; KIRQL Irql;
/* Enter trap */ KiEnterInterruptTrap(TrapFrame);
/* Start the interrupt */ if (HalBeginSystemInterrupt(CLOCK2_LEVEL, PRIMARY_VECTOR_BASE + PIC_TIMER_IRQ, &Irql)) { /* Update the performance counter */ HalpPerfCounter.QuadPart += HalpCurrentRollOver; HalpPerfCounterCutoff = KiEnableTimerWatchdog;
/* Save increment */ LastIncrement = HalpCurrentTimeIncrement;
/* Check if someone changed the time rate */ if (HalpClockSetMSRate) { /* Update the global values */ HalpCurrentTimeIncrement = HalpRolloverTable[HalpNextMSRate - 1].Increment; HalpCurrentRollOver = HalpRolloverTable[HalpNextMSRate - 1].RollOver;
/* Set new timer rollover */ HalpSetTimerRollOver((USHORT)HalpCurrentRollOver);
/* We're done */ HalpClockSetMSRate = FALSE; }
/* Update the system time -- the kernel will exit this trap */ KeUpdateSystemTime(TrapFrame, LastIncrement, Irql); }
/* Spurious, just end the interrupt */ KiEoiHelper(TrapFrame);}
VOIDFASTCALLHalpProfileInterruptHandler(IN PKTRAP_FRAME TrapFrame){ KIRQL Irql;
/* Enter trap */ KiEnterInterruptTrap(TrapFrame);
/* Start the interrupt */ if (HalBeginSystemInterrupt(PROFILE_LEVEL, PRIMARY_VECTOR_BASE + PIC_RTC_IRQ, &Irql)) {#if defined(SARCH_PC98) /* Clear the interrupt flag */ HalpAcquireCmosSpinLock(); (VOID)__inbyte(RTC_IO_i_INTERRUPT_RESET); HalpReleaseCmosSpinLock();#else /* Spin until the interrupt pending bit is clear */ HalpAcquireCmosSpinLock(); while (HalpReadCmos(RTC_REGISTER_C) & RTC_REG_C_IRQ) NOTHING; HalpReleaseCmosSpinLock();#endif
/* If profiling is enabled, call the kernel function */ if (!HalpProfilingStopped) { KeProfileInterrupt(TrapFrame); }
/* Finish the interrupt */ _disable(); HalEndSystemInterrupt(Irql, TrapFrame); }
/* Spurious, just end the interrupt */ KiEoiHelper(TrapFrame);}#endif /* !_MINIHAL_ */
#endif /* _M_IX86 */
/* PUBLIC FUNCTIONS ***********************************************************/
/* * @implemented */VOIDNTAPIHalCalibratePerformanceCounter(IN volatile PLONG Count, IN ULONGLONG NewCount){ ULONG_PTR Flags;
/* Disable interrupts */ Flags = __readeflags(); _disable();
/* Do a decrement for this CPU */ _InterlockedDecrement(Count);
/* Wait for other CPUs */ while (*Count);
/* Restore interrupts if they were previously enabled */ __writeeflags(Flags);}
/* * @implemented */ULONGNTAPIHalSetTimeIncrement(IN ULONG Increment){ /* Round increment to ms */ Increment /= 10000;
/* Normalize between our minimum (1 ms) and maximum (variable) setting */ if (Increment > HalpLargestClockMS) Increment = HalpLargestClockMS; if (Increment <= 0) Increment = 1;
/* Set the rate and tell HAL we want to change it */ HalpNextMSRate = Increment; HalpClockSetMSRate = TRUE;
/* Return the increment */ return HalpRolloverTable[Increment - 1].Increment;}
LARGE_INTEGERNTAPIKeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFrequency){ LARGE_INTEGER CurrentPerfCounter; ULONG CounterValue, ClockDelta; KIRQL OldIrql;
/* If caller wants performance frequency, return hardcoded value */ if (PerformanceFrequency) PerformanceFrequency->QuadPart = PIT_FREQUENCY;
/* Check if we were called too early */ if (HalpCurrentRollOver == 0) return HalpPerfCounter;
/* Check if interrupts are disabled */ if(!(__readeflags() & EFLAGS_INTERRUPT_MASK)) return HalpPerfCounter;
/* Raise irql to DISPATCH_LEVEL */ OldIrql = KeGetCurrentIrql(); if (OldIrql < DISPATCH_LEVEL) KfRaiseIrql(DISPATCH_LEVEL);
do { /* Get the current performance counter value */ CurrentPerfCounter = HalpPerfCounter;
/* Read the 8254 counter value */ CounterValue = HalpRead8254Value();
/* Repeat if the value has changed (a clock interrupt happened) */ } while (CurrentPerfCounter.QuadPart != HalpPerfCounter.QuadPart);
/* After someone changed the clock rate, during the first clock cycle we might see a counter value larger than the rollover. In this case we pretend it already has the new rollover value. */ if (CounterValue > HalpCurrentRollOver) CounterValue = HalpCurrentRollOver;
/* The interrupt is issued on the falling edge of the OUT line, when the counter changes from 1 to max. Calculate a clock delta, so that directly after the interrupt it is 0, going up to (HalpCurrentRollOver - 1). */ ClockDelta = HalpCurrentRollOver - CounterValue;
/* Add the clock delta */ CurrentPerfCounter.QuadPart += ClockDelta;
/* Check if the value is smaller then before, this means, we somehow missed an interrupt. This is a sign that the timer interrupt is very inaccurate. Probably a virtual machine. */ if (CurrentPerfCounter.QuadPart < HalpLastPerfCounter.QuadPart) { /* We missed an interrupt. Assume we will receive it later */ CurrentPerfCounter.QuadPart += HalpCurrentRollOver; }
/* Update the last counter value */ HalpLastPerfCounter = CurrentPerfCounter;
/* Restore previous irql */ if (OldIrql < DISPATCH_LEVEL) KfLowerIrql(OldIrql);
/* Return the result */ return CurrentPerfCounter;}
/* EOF */