📄 idle.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
#include <windows.h>
#include <bldver.h>
#include <nkintr.h>
#include <oalintr.h>
#include <clocks.h>
#include <hwdefs.h>
//#include "platform.h"
//#include <board.h>
/*
@doc EXTERNAL KERNEL HAL
@module cfwp2.c - P2 HW Support |
OEM support Functions for the Windows CE P2 Platform.
@xref <f OEMInit> <f OEMInterruptEnable> <f OEMInterruptDisable>
<f OEMInterruptDone> <l HAL Overview.Windows CE Kernel OEM Interface>
@topic Windows CE Kernel OEM Interface |
This defines the HAL layer - OEM and platform dependent pieces of
code which we expect the OEM to deliver to us. There are three pieces
of OEM deliverable code - the bootstrap loader & monitor (for
debugging), the HAL portions which are interfaces between the kernel
and the firmware, and the driver interface. This topic covers just
the HAL portion.
The philosophy is to keep the HAL layer as simple as possible. The
HAL should not be confused with the machine or CPU independence. HAL
is specific for a particular CPU and platform. It includes interfaces
for the following devices:<nl>
Real Time Clock<nl>
Interval Timer (used for the scheduler operation) <nl>
Interrupt handlers and support <nl>
Note that it does not include abstractions for devices like the DMA
controller etc. since the kernel does not use one. Also note that the
list could change for different CPU's and platforms - for instance,
some chips might include a lot of peripheral devices (like the
interval timer) on the CPU chip itself, removing the need for a
separate interface for them.
The interfaces for the real time clock and interval timer are still
being developed. But they should in general be extremely simple and
straightforward. For details on the interrupt support model in the
kernel look at <l Interrupt Support Overview.Kernel Interrupt Support>
@xref <l Interrupt Support Overview.Kernel Interrupt Support>
<f OEMInit> <f OEMInterruptEnable> <f OEMInterruptDisable>
<f OEMInterruptDone> <f HookInterrupt>
*/
//
// Kernel global variables used by GetIdleTime( ) to determine CPU utilization
//
extern DWORD idleconv; // translation constant in 1 ms units
extern DWORD curridlehigh, curridlelow; // 64-bit idle time in ms
// The kernel exports...
#if (CE_MAJOR_VER == 0x0003)
//
// cedar
//
#ifdef AddrCurMSec
// Some kernels export a pointer to the CurMSec variable.
static volatile DWORD * pCurMSec = (volatile DWORD *) AddrCurMSec;
static volatile DWORD * pDiffMSec = (volatile DWORD *) AddrDiffMSec;
#else
extern volatile DWORD CurMSec;
extern volatile DWORD DiffMSec;
static volatile DWORD * pCurMSec = &CurMSec;
static volatile DWORD * pDiffMSec = &DiffMSec;
#endif
extern DWORD dwSleepMin;
extern DWORD dwPartialDiffMSec;
extern DWORD ticksleft;
#else
//
// dougfir or later
//
#ifdef AddrCurMSec
// Some kernels export a pointer to the CurMSec variable.
static volatile DWORD * pCurMSec = (volatile DWORD *) AddrCurMSec;
#else
extern volatile DWORD CurMSec;
static volatile DWORD * pCurMSec = &CurMSec;
#endif
extern DWORD dwReschedTime;
#endif
extern BOOL fIntrTime;
extern BOOL bProfileTimerRunning;
volatile ULARGE_INTEGER CurTicks = { 0, 0 };
volatile ULARGE_INTEGER * pCurTicks = &CurTicks;
extern DWORD dwReschedIncrement;
extern DWORD OEMCount1ms;
extern ULONG gulCS8950KitlUsed;
extern volatile DWORD gdwInterruptMask1, gdwInterruptMask2;
extern volatile DWORD gdwInterruptWakeMask1, gdwInterruptWakeMask2;
extern volatile BYTE gfSysIntrWakeupMask[SYSINTR_MAXIMUM];
extern volatile DWORD gdwLastWakeupSource;
extern volatile BOOL gfResumeFlag;
extern volatile DWORD dwTimeValueWhenSuspend;
//
// CPU-specific functions for OEMIdle
//
extern void CPUEnterIdle(DWORD dwIdleParam);
extern DWORD CPUGetSysTimerCountMax(DWORD dwIdleMSecRequested);
extern void CPUSetSysTimerCount(DWORD dwIdleMSec);
extern BOOL CPUClearSysTimerIRQ(void);
extern BOOL SysIntrNumToInterruptMask
(
DWORD dwSysIntr,
PULONG pulInterruptMask1,
PULONG pulInterruptMask2
);
#if (CE_MAJOR_VER == 0x0003)
//
// cedar
//
extern DWORD
CPUGetSysTimerCountElapsed(
DWORD dwTimerCountdownMSec,
volatile DWORD *pCurMSec,
volatile DWORD *pDiffMSec,
DWORD *pPartialCurMSec,
DWORD *pPartialDiffMSec,
volatile ULARGE_INTEGER *pCurTicks
);
//------------------------------------------------------------------------------
//
// This routine is called by the kernel when there are no threads ready to
// run. The CPU should be put into a reduced power mode and halted. It is
// important to be able to resume execution quickly upon receiving an interrupt.
// Note: It is assumed that interrupts are off when OEMIdle is called. Interrrupts
// are turned off when OEMIdle returns.
//
//------------------------------------------------------------------------------
void
OEMIdle(
DWORD dwIdleParam
)
{
DWORD dwIdleMSec;
DWORD dwDiffMSecPrev = *pDiffMSec;
static DWORD dwPartialCurMSec = 0; // Keep CPU-specific sub-millisecond leftover.
// Use for 64-bit math
ULARGE_INTEGER currIdle = {
curridlelow,
curridlehigh
};
if (ticksleft) {
// if ticksleft, potentially there are threads on the sleep queue waiting to run, so don't idle
return;
}
if (bProfileTimerRunning) {
// system timer is running at CPU specific profiling value - just call CPUEnterIdle and return
CPUEnterIdle(dwIdleParam);
return;
}
if (dwSleepMin == 0 || fIntrTime) {
//
// No minimum sleep time specified. Wakeup on the normal schedule tick.
//
CPUEnterIdle(dwIdleParam);
// Update global idle time and return
currIdle.QuadPart += RESCHED_PERIOD;
curridlelow = currIdle.LowPart;
curridlehigh = currIdle.HighPart;
return;
}
if (dwDiffMSecPrev >= dwSleepMin) {
//
// According to the globals, our sleep time has already passed!
//
return;
}
//
// Calculate the idle period
//
dwIdleMSec = dwSleepMin - dwDiffMSecPrev;
//
// The system timer may not be capable of arbitrary timeouts. Get the
// CPU-specific highest possible timeout available.
//
dwIdleMSec = CPUGetSysTimerCountMax(dwIdleMSec);
//
// Since OEMIdle( ) is being called in the middle of a normal reschedule
// period, CurMSec, dwPartialCurMSec, dwPartialDiffMSec and CurTicks need
// to be updated accordingly.
//
CPUGetSysTimerCountElapsed(RESCHED_PERIOD, pCurMSec, pDiffMSec, &dwPartialCurMSec, &dwPartialDiffMSec, pCurTicks);
dwDiffMSecPrev = *pDiffMSec;
//
// Set the timer to wake up much later than usual, if needed.
//
CPUSetSysTimerCount(dwIdleMSec);
CPUClearSysTimerIRQ( );
//
// Enable wakeup on any interrupt, then go to sleep.
//
CPUEnterIdle(dwIdleParam);
INTERRUPTS_OFF( );
//
// We're awake! The wake-up ISR (or any other ISR) has already run.
//
if (dwDiffMSecPrev != *pDiffMSec) {
//
// We completed the full period we asked to sleep. Update the counters.
//
*pCurMSec += (dwIdleMSec - RESCHED_PERIOD); // Subtract resched period, because ISR also incremented.
*pDiffMSec += (dwIdleMSec - RESCHED_PERIOD);
CurTicks.QuadPart += (dwIdleMSec - RESCHED_PERIOD) * dwReschedIncrement;
currIdle.QuadPart += dwIdleMSec;
} else {
//
// Some other interrupt woke us up before the full idle period was
// complete. Determine how much time has elapsed.
//
currIdle.QuadPart += CPUGetSysTimerCountElapsed(dwIdleMSec, pCurMSec, pDiffMSec, &dwPartialCurMSec, &dwPartialDiffMSec, pCurTicks);
}
// Re-arm counters
CPUSetSysTimerCount(RESCHED_PERIOD);
CPUClearSysTimerIRQ( );
// Update global idle time
curridlelow = currIdle.LowPart;
curridlehigh = currIdle.HighPart;
return;
}
#else
//
// dougfir or later
//
extern DWORD
CPUGetSysTimerCountElapsed(
DWORD dwTimerCountdownMSec,
volatile DWORD *pCurMSec,
DWORD *pPartialCurMSec,
volatile ULARGE_INTEGER *pCurTicks
);
//------------------------------------------------------------------------------
//
// This routine is called by the kernel when there are no threads ready to
// run. The CPU should be put into a reduced power mode and halted. It is
// important to be able to resume execution quickly upon receiving an interrupt.
// Note: It is assumed that interrupts are off when OEMIdle is called. Interrrupts
// are turned off when OEMIdle returns.
//
//------------------------------------------------------------------------------
static DWORD dwPartialCurMSec = 0; // Keep CPU-specific sub-millisecond leftover.
void
OEMIdle(
DWORD dwIdleParam
)
{
DWORD dwIdleMSec;
DWORD dwPrevMSec = *pCurMSec;
// Use for 64-bit math
ULARGE_INTEGER currIdle =
{
curridlelow,
curridlehigh
};
if ((int) (dwIdleMSec = dwReschedTime - dwPrevMSec) <= 0) {
// already time to wakeup
return;
}
// just idle till tick if profiling or running iltiming
if (bProfileTimerRunning || fIntrTime) {
// idle till end of 'tick'
CPUEnterIdle(dwIdleParam);
// Update global idle time and return
currIdle.QuadPart += RESCHED_PERIOD;
curridlelow = currIdle.LowPart;
curridlehigh = currIdle.HighPart;
return;
}
//
// Since OEMIdle( ) is being called in the middle of a normal reschedule
// period, CurMSec, dwPartialCurMSec, and CurTicks need
// to be updated accordingly.
// Once we reach this point, we must re-program the timer (if we ever did)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -