⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 power.c

📁 LX 800 WindowsCE 6.0 BSP
💻 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.
//

//
// Power off/Suspend routines
//

#include <windows.h>
#include <nkintr.h>
#include <oalintr.h>
#include "pc.h"
#include <oal.h>

static UCHAR GetPICInterruptMask(BOOL fMaster);
static BOOL SetPICInterruptMask(BOOL fMaster, UCHAR ucMask);

void InitiateVSAPM (unsigned short);

#define GEODELX_POWER_STATE_ENABLED		0x0000
#define GEODELX_POWER_STATE_STANDBY		0x0001
#define GEODELX_POWER_STATE_SUSPEND		0x0002
#define GEODELX_POWER_STATE_OFF			0x0003

//
// This routine is invoked when the OFF button is pressed. It is responsible
// for any final power off state and putting the cpu into standby.
//
static CpuSleepX86(void) {

#ifdef NOTDEF
    // NOTE : This bit of code will reboot the system. Some people build this way
    // so that they can do a soft reboot simply by choosing suspend from the run menu.
    __asm {
        cli                     ; Disable Interrupts
        mov     al, 0FEh        ; Reset CPU
        out     064h, al
        jmp     near $          ; Should not get here
    }
#endif

    __asm {
        sti
        hlt
        cli
    }
}


static UCHAR GetPICInterruptMask(BOOL fMaster) {
    UCHAR ucMask;
    UCHAR ucPort = (fMaster)?0x21:0xa1;

    __asm {
        pushfd
        cli
        xor dx, dx
        mov dl, ucPort
        in al, dx
        mov ucMask, al
        sti
        popfd
    }

    return ucMask;
}


static BOOL SetPICInterruptMask(BOOL fMaster, UCHAR ucMask) {
    WORD wPort = (fMaster)?0x21:0xa1;

    __asm {
        pushfd                      ; Save interrupt state
        cli
        mov     dx, wPort
        mov     al, ucMask
        out     dx, al
        sti
        popfd                       ; Restore interrupt state
    }

    return TRUE;
}

static DWORD dwLastWakeupSource=SYSWAKE_UNKNOWN;

static BYTE fInterruptWakeup[SYSINTR_MAXIMUM];
static BYTE fInterruptWakeupMask[SYSINTR_MAXIMUM];

static void SetInterruptMask(PBYTE pMaster,PBYTE pSlave)
{
    DWORD sysIntr;
    *pMaster=*pSlave=0;
    for (sysIntr = 0; sysIntr < SYSINTR_MAXIMUM; sysIntr++) {
        if (fInterruptWakeupMask[sysIntr]) {
             UINT32 count, *pIrqs, i;
             count = 1;
             if (!OALIntrTranslateSysIntr(sysIntr, &count, &pIrqs)) continue;
             for (i = 0; i < count; i++) {
                 if (pIrqs[i] != -1)  {
                    if (pIrqs[i] < 8) 
                        *pMaster|=(1 << pIrqs[i]);
                    else 
                        *pSlave |=(1 << (pIrqs[i] - 8));
                }
             }                 
        }
    }

}

DWORD OEMPowerManagerInit(void)
{
    InitiateVSAPM (GEODELX_POWER_STATE_ENABLED);
#ifdef INIT_WAKE
    // OEMPowerOff used to hardcode the keyboard and mouse to wake the system.
    // Device drivers can now tell the OAL to enable/disable it's wake interrupt.
    // Define INIT_WAKE if you want to use the hardcoded values.
    OEMSetWakeupSource(SYSINTR_KEYBOARD);
    OEMSetWakeupSource(SYSINTR_TOUCH);
#endif
    return 0;
}

DWORD OEMSetWakeupSource( DWORD dwSources)
{
    if (dwSources<SYSINTR_MAXIMUM) {
        fInterruptWakeupMask[dwSources]=1;
        return 1;
    } 
    return 0;
}

DWORD OEMResetWakeupSource( DWORD dwSources)
{
    if (dwSources<SYSINTR_MAXIMUM) {
        fInterruptWakeupMask[dwSources]=0;
        return 1;
    } 
    return 0;
}

DWORD OEMGetWakeupSource(void)
{
    return dwLastWakeupSource;
}

void OEMIndicateIntSource(DWORD dwSources)
{
    if (dwSources<SYSINTR_MAXIMUM ) {
        fInterruptWakeup[dwSources]=1;
    }
}

void OEMClearIntSources()
{
    memset(fInterruptWakeup,0,SYSINTR_MAXIMUM);
}

DWORD OEMFindFirstWakeSource()
{
    DWORD dwCount;

    for (dwCount=0;dwCount<SYSINTR_MAXIMUM;dwCount++) {
        if (fInterruptWakeup[dwCount]&& fInterruptWakeupMask[dwCount])
            break;
    }

    return (dwCount == SYSINTR_MAXIMUM) ? SYSWAKE_UNKNOWN : dwCount;
}

void OEMPowerOff(void)
{
    BYTE bOldMaster,bOldSlave;
    BYTE bMaster,bSlave;
    DWORD dwWake;
    BOOL fWakePending = FALSE;

    INTERRUPTS_OFF();

    // clear our wake source, which is the reason we are exiting this routine
    dwLastWakeupSource = SYSWAKE_UNKNOWN;
        
    // get the current interrupt mask
    bOldMaster=GetPICInterruptMask(TRUE);
    bOldSlave=GetPICInterruptMask(FALSE);

    // configure the new mask and clear wake source globals
    SetInterruptMask(&bMaster,&bSlave);
    SetPICInterruptMask( TRUE, (UCHAR)~(bMaster| (1<<INTR_PIC2)));
    SetPICInterruptMask( FALSE, (UCHAR)~bSlave );

    //    do {
    INTERRUPTS_ON();
    // wait for an interrupt that we've enabled as a wake source
    //CpuSleepX86();
#if USE_SUSPENDS3
    InitiateVSAPM (GEODELX_POWER_STATE_SUSPEND);
#elif USE_SUSPENDS1
    InitiateVSAPM (GEODELX_POWER_STATE_STANDBY);
#else
    InitiateVSAPM (GEODELX_POWER_STATE_STANDBY);
#endif
    INTERRUPTS_OFF();
    // determine what woke us up
    dwWake = OEMFindFirstWakeSource();
    //    } while (dwWake == SYSWAKE_UNKNOWN);
    
    // restore our interrupt mask
    SetPICInterruptMask(TRUE, bOldMaster);
    SetPICInterruptMask(FALSE, bOldSlave);

    dwLastWakeupSource = dwWake;
    
    INTERRUPTS_ON();
}


BOOL x86PowerIoctl (
    UINT32 code, VOID *lpInBuf, UINT32 nInBufSize, VOID *lpOutBuf, 
    UINT32 nOutBufSize, UINT32 *lpBytesReturned
) {
    BOOL rc = FALSE;
    switch (code) {
    case IOCTL_HAL_ENABLE_WAKE:
    case IOCTL_HAL_DISABLE_WAKE:
        if (lpBytesReturned) {
            *lpBytesReturned = 0;
        }
        if (lpInBuf && nInBufSize == sizeof(DWORD)) {
            DWORD dwReturn =
                ((code==IOCTL_HAL_ENABLE_WAKE)
                ? OEMSetWakeupSource   (*(PDWORD)lpInBuf)
                : OEMResetWakeupSource (*(PDWORD)lpInBuf));
            if (dwReturn)
                rc = TRUE;
        } else {
            NKSetLastError (ERROR_INVALID_PARAMETER);
        }
        break;
    case IOCTL_HAL_GET_WAKE_SOURCE:
        if (lpBytesReturned) {
            *lpBytesReturned = sizeof(DWORD);
        }
        if (!lpOutBuf && nOutBufSize > 0) {
            NKSetLastError (ERROR_INVALID_PARAMETER);
        } else if (lpOutBuf || nOutBufSize < sizeof(DWORD)) {
            NKSetLastError (ERROR_INSUFFICIENT_BUFFER);
        } else {
            *(PDWORD)lpOutBuf = OEMGetWakeupSource();
            rc = TRUE;
        }
        break;
    case IOCTL_HAL_PRESUSPEND:
        if (lpBytesReturned) {
            *lpBytesReturned = 0;
        }
        OEMClearIntSources();
        rc = TRUE;
    default:
        NKSetLastError (ERROR_INVALID_PARAMETER);
        break;
    }
    return rc;
}

void InitiateVSAPM (unsigned short pmstate)
{
    // Initiate the suspend through virtual registers

    _asm
    {
        // first I/O is an unlock
        mov ax, 0fc53h
            mov dx, 0ac1ch
            out dx, ax

            // make sure that we are in APM mode since PM is disabled
            mov ax, 0400h          ; VRC_PM=04, PowerMode = 0 or APM
            out dx, ax
            add dx, 2
            mov ax, 02             ; PM_APM
            out dx, ax

            // Once again, the unlock is required
            mov ax, 0fc53h
            mov dx, 0ac1ch
            out dx, ax

            mov ax, 0401h          ; VRC_PM=04, PowerState=01
            out dx, ax

            add dx, 2
            mov ax, word ptr pmstate
            out dx, ax
    }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -