//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//

/*++

Module Name:

  pwrbutton.c

Abstract:

  This module is intended as a home for threads that monitor and manage platform features.
  Intial implementation was for for monitoring SW21 on a Mainstone platform which is used
  to trigger SUSPEND/RESUME.

Notes:


--*/

#include <windows.h>
#include <devload.h>
#include <windev.h>
#include <pm.h>
#include <bsp.h>


#define QUEUE_ENTRIES                   16
#define MAX_NAMELEN                     64
#define QUEUE_SIZE                      (QUEUE_ENTRIES*(sizeof(POWER_BROADCAST)+MAX_NAMELEN))

// Registry
#define REG_DEBOUNCE_DELAY_TIME_NAME              L"DebounceDelayTime"
#define REG_DEBOUNCE_DELAY_TIME_DEFAULT           10
#define REG_DEBOUNCE_CHECK_SAME_TIMES_NAME        L"DebounceCheckSameTimes"
#define REG_DEBOUNCE_CHECK_SAME_TIMES_DEFAULT     10
#define REG_POWER_CHANGE_INTERVAL_NAME            L"PowerChangeInterval"
#define REG_POWER_CHANGE_INTERVAL_DEFAULT         500
#define REG_PWRBUTTON_THREAD_PRIORITY_NAME        L"Priority256"
#define REG_PWRBUTTON_THREAD_PRIORITY_DEFAULT     240
#define REG_PWRBUTTON_ACTIVE_RELEASE_NAME         L"ActiveRelease"
#define REG_PWRBUTTON_ACTIVE_RELEASE_DEFAULT      1
#define REG_PWRBUTTON_KEY_PRESSED_ISLOW_NAME      L"KeyPressedIsLow"
#define REG_PWRBUTTON_KEY_PRESSED_ISLOW_DEFAULT   1

typedef struct {
   DWORD dwDebounceDelayTime;
   DWORD dwDebounceCheckSameTimes;
   DWORD dwPowerChangeInterval;
   DWORD dwThreadPriority;
   BOOL  fActiveRelease;
   BOOL  fKeyPressedIsLow;
} TRegistryData;


volatile BOOL g_FlagExitThrd = FALSE;
UINT32 g_Irq          = GET_GPIOPINNAME_IRQ(uP_nWAKEUP);
UINT32 g_SysIntr;
HANDLE g_ButtonEvent  = NULL;
HANDLE g_ButtonThread = NULL;
HANDLE g_PwrMonThread = NULL;
HANDLE m_hReadMsgQ    = NULL;
volatile BOOL g_fExpectSuspend = TRUE;
volatile DWORD g_dwButtonPressedTime;
CRITICAL_SECTION g_csPwrThreadTime;
TRegistryData gRegistryData;

static BOOL IsKeyPressed(void);
static BOOL IsActiveOccured(void);
static BOOL ReadRegistryData(LPCTSTR pContext, TRegistryData *pRegistryData);

#ifdef DEBUG
#define ZONE_DATA                       DEBUGZONE(0)
#define ZONE_INTRTHREAD                 DEBUGZONE(1)
#define ZONE_OPEN                       DEBUGZONE(2)
#define ZONE_CLOSE                      DEBUGZONE(3)
#define ZONE_IOCTL                      DEBUGZONE(4)
#define ZONE_GPIO                       DEBUGZONE(5)
#define ZONE_READ                       DEBUGZONE(6)
#define ZONE_WRITE                      DEBUGZONE(7)
#define ZONE_SEEK                       DEBUGZONE(8)
#define ZONE_PMTHREAD                   DEBUGZONE(9)
#define ZONE_VALUE                      DEBUGZONE(11)
#ifdef ZONE_INIT
#undef ZONE_INIT
#endif
#define ZONE_INIT                       DEBUGZONE(12)
#define ZONE_FUNCTION                   DEBUGZONE(13)
#define ZONE_WARN                       DEBUGZONE(14)
#ifdef ZONE_ERROR
#undef ZONE_ERROR
#endif
#define ZONE_ERROR                      DEBUGZONE(15)
DBGPARAM dpCurSettings = {
   TEXT("PwrButton"), {
      TEXT("Data"),TEXT("IntrThread"),TEXT("Open"),TEXT("Close"),
      TEXT("Ioctl"),TEXT("GPIO"),TEXT("Read"),TEXT("Write"),
      TEXT("Seek"),TEXT("PwrMonThread"),TEXT("10"),TEXT("Values"),
      TEXT("Init"),TEXT("Function"),TEXT("Warning"),TEXT("Error")},
   0xFFFF
};
#endif

/*---------------------------------------------------------------------------*/
// Power state monitor thread
INT WINAPI PwrMonThread(void)
{
   HANDLE hNotifications;
   MSGQUEUEOPTIONS msgOptions = {0};

   DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT("+\r\n")));

   CeSetThreadPriority(g_PwrMonThread, gRegistryData.dwThreadPriority);

   // Create a message queue for Power Manager notifications.
   msgOptions.dwSize        = sizeof(MSGQUEUEOPTIONS);
   msgOptions.dwFlags       = 0;
   msgOptions.dwMaxMessages = QUEUE_ENTRIES;
   msgOptions.cbMaxMessage  = sizeof(POWER_BROADCAST) + MAX_NAMELEN;
   msgOptions.bReadAccess   = TRUE;

   m_hReadMsgQ = CreateMsgQueue(NULL, &msgOptions);
   if (m_hReadMsgQ == NULL) {
      DEBUGMSG(ZONE_PMTHREAD||ZONE_ERROR, (TEXT(__FUNCTION__) TEXT(": CreateMsgQueue(Read): error %d\r\n"), GetLastError()));
      g_FlagExitThrd = TRUE;
   }

   // Request Power notifications
   hNotifications = RequestPowerNotifications(m_hReadMsgQ, POWER_NOTIFY_ALL);
   if (!hNotifications) {
      DEBUGMSG(ZONE_PMTHREAD||ZONE_ERROR, (TEXT(__FUNCTION__) TEXT(": RequestPowerNotifications: error %d\r\n"), GetLastError()));
      g_FlagExitThrd = TRUE;
   }

   while (!g_FlagExitThrd) {
      DWORD dwBytesInQueue = 0;
      DWORD dwFlags;
      UCHAR buf[QUEUE_SIZE];
      PPOWER_BROADCAST pB = (PPOWER_BROADCAST) buf;

      memset(buf, 0, QUEUE_SIZE);

      DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": Waiting for PM state transition notification\r\n")));

      // Read message from queue.
      if (!ReadMsgQueue(m_hReadMsgQ, buf, QUEUE_SIZE, &dwBytesInQueue, INFINITE, &dwFlags)) {
         if (g_FlagExitThrd)
            break;
         DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": ReadMsgQueue: ERROR:%d\r\n"), GetLastError()));
      }
      else if (dwBytesInQueue < sizeof(POWER_BROADCAST)) {
         DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": Received short message: %d bytes, expected: %d\r\n"),
                                dwBytesInQueue, sizeof(POWER_BROADCAST)));
      }
      else {
         DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": ReadMsgQueue: Event Fired.\r\n")));
         EnterCriticalSection(&g_csPwrThreadTime);
         g_dwButtonPressedTime = GetTickCount();
         LeaveCriticalSection(&g_csPwrThreadTime);

         switch (pB->Message) {
            case PBT_TRANSITION:

               DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": PBT_TRANSITION to system power state [Flags: 0x%x]: '%s'\r\n"), pB->Flags, pB->SystemPowerState));

               switch (POWER_STATE(pB->Flags)) {
                  case POWER_STATE_ON:
                     DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": POWER_STATE_ON\r\n")));
                     break;

                  case POWER_STATE_OFF:
                     DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": POWER_STATE_OFF\r\n")));
                     break;

                  case POWER_STATE_CRITICAL:
                     DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": POWER_STATE_CRITICAL\r\n")));
                     break;

                  case POWER_STATE_BOOT:
                     DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": POWER_STATE_BOOT\r\n")));
                     break;

                  case POWER_STATE_IDLE:
                     DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": POWER_STATE_IDLE\r\n")));
                     break;

                  case POWER_STATE_SUSPEND:
                     g_fExpectSuspend = FALSE;
                     DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": POWER_STATE_SUSPEND\r\n")));
                     break;

                  case POWER_STATE_RESET:
                     DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": POWER_STATE_RESET\r\n")));
                     break;

                  case POWER_STATE_PASSWORD:
                     DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": POWER_STATE_PASSWORD\r\n")));
                     break;

                  case 0:
                     DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": Power State Flags:0x%x\r\n"), pB->Flags));
                     break;

                  default:
                     DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT(": Unknown Power State Flags:0x%x\r\n"), pB->Flags));
                     break;
               }
               break;

            case PBT_RESUME:
            {
               DWORD wakeSrc = SYSWAKE_UNKNOWN;
               DWORD bytesRet= 0;

               g_fExpectSuspend = TRUE;

               DEBUGMSG(1, (TEXT(__FUNCTION__) TEXT(": PBT_RESUME\r\n")));

               // If GPIO1 resumed the system set the system state to ON
               if (KernelIoControl(IOCTL_HAL_GET_WAKE_SOURCE, NULL, 0, &wakeSrc, sizeof(wakeSrc), &bytesRet) && (bytesRet == sizeof(wakeSrc))) {
                  if (wakeSrc == g_SysIntr) {
                     SetSystemPowerState(NULL, POWER_STATE_ON, POWER_FORCE);
                  }
               }
               else {
                  NKDbgPrintfW(L"PWM: Error getting wake source\r\n");
               }

               break;
            }
            case PBT_POWERSTATUSCHANGE:
               DEBUGMSG(1, (TEXT(__FUNCTION__) TEXT(": PBT_POWERSTATUSCHANGE\r\n")));
               break;

            case PBT_POWERINFOCHANGE:
            {
               PPOWER_BROADCAST_POWER_INFO ppbpi = (PPOWER_BROADCAST_POWER_INFO) pB->SystemPowerState;

               DEBUGMSG(1, (TEXT(__FUNCTION__) TEXT(": PBT_POWERINFOCHANGE\r\n")));
               DEBUGMSG(1, (TEXT(__FUNCTION__) TEXT(": \tAC line status %u, battery flag %u, backup flag %u, %d levels\r\n"),
                       ppbpi->bACLineStatus, ppbpi->bBatteryFlag,
                       ppbpi->bBackupBatteryFlag, ppbpi->dwNumLevels));
               break;
            }

            default:
               DEBUGMSG(ZONE_PMTHREAD||ZONE_WARN, (TEXT(__FUNCTION__) TEXT(": Unknown Message:%d\r\n"), pB->Message));
               break;
         }
      }
   }

   if (hNotifications) {
      StopPowerNotifications(hNotifications);
      hNotifications = NULL;
   }

   if (m_hReadMsgQ) {
      CloseMsgQueue(m_hReadMsgQ);
      m_hReadMsgQ = NULL;
   }

   DEBUGMSG(ZONE_PMTHREAD, (TEXT(__FUNCTION__) TEXT("-\r\n")));

   return 0;
}

/*---------------------------------------------------------------------------*/
// Power Button event thread
INT WINAPI ButtonThread(void)
{
#if 0
   DWORD dwPwrFlags;
   TCHAR szPowerState[MAX_PATH];
#endif
   BOOL fOldActiveOccured, fNewActiveOccured;
   DWORD ii;

   DEBUGMSG(ZONE_INTRTHREAD, (TEXT(__FUNCTION__) TEXT("+\r\n")));

   CeSetThreadPriority(g_ButtonThread, gRegistryData.dwThreadPriority+1);

   while (!g_FlagExitThrd) {
      WaitForSingleObject(g_ButtonEvent, INFINITE);
      if (g_FlagExitThrd) {
         break;
      }

      // To avoid suspend/wakeup too frequently to conflict other driver power handler, force entry time gap
      EnterCriticalSection(&g_csPwrThreadTime);
      if ((g_dwButtonPressedTime + gRegistryData.dwPowerChangeInterval) > GetTickCount()) {
         g_dwButtonPressedTime = GetTickCount();
         LeaveCriticalSection(&g_csPwrThreadTime);
         InterruptDone(g_SysIntr);
         continue;
      }
      LeaveCriticalSection(&g_csPwrThreadTime);

      // Debounce
      fNewActiveOccured = IsActiveOccured();
      for (ii=0; ii<gRegistryData.dwDebounceCheckSameTimes; ii++) {
         fOldActiveOccured = fNewActiveOccured;
         Sleep(gRegistryData.dwDebounceDelayTime);
         fNewActiveOccured = IsActiveOccured();
         if (fOldActiveOccured != fNewActiveOccured) {
            // Key bounced, clear counter and re-count
            ii = 0;
         }
      }

      DEBUGMSG(ZONE_INTRTHREAD, (TEXT(__FUNCTION__) TEXT(": Event(%c, %c) Fired!\r\n"), g_fExpectSuspend?'T':'F', fNewActiveOccured?'T':'F'));

#if 0
      // Back to "on" state if we are in "unattaneded" mode or "screenoff" mode.
      GetSystemPowerState(szPowerState, ARRAYSIZE(szPowerState), &dwPwrFlags);

      if ((wcscmp(szPowerState, L"unattended") == 0) || (wcscmp(szPowerState, L"screenoff") == 0)) {
         // We are in"unattended" or "screenoff" mode. Go back to "on" state.
         SetSystemPowerState(NULL, POWER_STATE_ON, POWER_FORCE);

         // Block until the system is back to the "on" state.
         do {
            Sleep(10);
            GetSystemPowerState(szPowerState, ARRAYSIZE(szPowerState), &dwPwrFlags);
            DEBUGMSG(ZONE_INTRTHREAD, (TEXT("PWB: Power State: '%s'\r\n"), szPowerState));
         } while((POWER_STATE(dwPwrFlags) & POWER_STATE_ON) == 0);
      }
#else
      // Suspend the device
      g_fExpectSuspend = TRUE;
      if (g_fExpectSuspend && fNewActiveOccured) {
         SetSystemPowerState(NULL, POWER_STATE_SUSPEND, 0);
      }

      // ZZZzzzz.....

      //Note: After system resumes, PwrMonThread (PBT_RESUME) will set the system to 'ON' state
#endif

      InterruptDone(g_SysIntr);
   }

   DEBUGMSG(ZONE_INTRTHREAD, (TEXT(__FUNCTION__) TEXT("-\r\n")));

   return 0;
} // PWB_ButtonThread()

/*---------------------------------------------------------------------------*/
BOOL WINAPI DllEntry(HANDLE hInstDll, DWORD dwReason, LPVOID lpvReserved)
{
   switch (dwReason) {
      case DLL_PROCESS_ATTACH:
         DEBUGMSG(ZONE_INIT, (TEXT(__FUNCTION__) TEXT(": DLL_PROCESS_ATTACH (PID=x%08x)\r\n"), GetCurrentProcessId()));

         // don't need thread attach/detach messages
         DisableThreadLibraryCalls ((HMODULE)hInstDll);
         break;

      case DLL_PROCESS_DETACH:
         DEBUGMSG(ZONE_INIT, (TEXT(__FUNCTION__) TEXT(": DLL_PROCESS_DETACH (PID=x%08x)\r\n"), GetCurrentProcessId()));
         break;
   }
   return TRUE;
}

/*---------------------------------------------------------------------------*/
void ConfigGPIO(void)
{
   DEBUGMSG(ZONE_GPIO, (TEXT(__FUNCTION__) TEXT("+\r\n")));

   // Config PCC_POWER_nEN pin as GPIO Output1
   GPIO_DDKIomuxSetPinMux(uP_nWAKEUP);
   GPIO_DDKIomuxSetPadConfig(uP_nWAKEUP);
   if ((!gRegistryData.fActiveRelease && gRegistryData.fKeyPressedIsLow) || (gRegistryData.fActiveRelease && !gRegistryData.fKeyPressedIsLow)) {
      DEBUGMSG(ZONE_GPIO, (__WFUNCTION__ TEXT(": Config uP_nWAKEUP interrupt as falling edge.\r\n")));
      GPIO_DDKGpioSetConfigIntSetting(uP_nWAKEUP, DIR_IN, DDK_GPIO_INTR_FALL_EDGE);
   }
   else {
      DEBUGMSG(ZONE_GPIO, (__WFUNCTION__ TEXT(": Config uP_nWAKEUP interrupt as rising edge.\r\n")));
      GPIO_DDKGpioSetConfigIntSetting(uP_nWAKEUP, DIR_IN, DDK_GPIO_INTR_RISE_EDGE);
   }
   GPIO_DDKGpioClearIntrPin(uP_nWAKEUP);

   DEBUGMSG(ZONE_GPIO, (TEXT(__FUNCTION__) TEXT("-\r\n")));

   return;
}

/*---------------------------------------------------------------------------*/
BOOL IsKeyPressed(void)
{
   BOOL fKeyPressed=FALSE;
   UINT32 dwData;

   DEBUGMSG(ZONE_FUNCTION, (TEXT(__FUNCTION__) TEXT("+\r\n")));

   if (GPIO_DDKGpioReadDataPin(uP_nWAKEUP, &dwData)) {
      if (gRegistryData.fKeyPressedIsLow) {
         // Key is pressed when gpio pin uP_nWAKEUP is low
         fKeyPressed = (dwData == 0);
      }
      else {
         // Key is pressed when gpio pin uP_nWAKEUP is high
         fKeyPressed = (dwData != 0);
      }
   }
   GPIO_DDKGpioClearIntrPin(uP_nWAKEUP);

   DEBUGMSG(ZONE_FUNCTION, (TEXT(__FUNCTION__) TEXT("(%c)-\r\n"), fKeyPressed?'T':'F'));

   return fKeyPressed;
}

/*---------------------------------------------------------------------------*/
BOOL IsActiveOccured(void)
{
   BOOL fKeyPressed;
   BOOL fActiveOccured=FALSE;

   DEBUGMSG(ZONE_FUNCTION, (TEXT(__FUNCTION__) TEXT("+\r\n")));

   fKeyPressed = IsKeyPressed();
   fActiveOccured = ((gRegistryData.fActiveRelease && !fKeyPressed) || (!gRegistryData.fActiveRelease && fKeyPressed));

   DEBUGMSG(ZONE_FUNCTION, (TEXT(__FUNCTION__) TEXT("(%c)-\r\n"), fActiveOccured?'T':'F'));

   return fActiveOccured;
}

/*---------------------------------------------------------------------------*/
BOOL Deinit(DWORD hDeviceContext)
{
   DEBUGMSG(ZONE_INIT, (TEXT(__FUNCTION__) TEXT("(0x%08X)+\r\n"), hDeviceContext));

   g_FlagExitThrd = TRUE;

   if (g_ButtonEvent) {
      SetEvent(g_ButtonEvent);
      InterruptDisable(g_SysIntr);
      CloseHandle(g_ButtonEvent);
   }

   // Signal PwrMonThread to finish (closing the handle will force ReadMsgQueue to return)
   if (m_hReadMsgQ) {
      CloseMsgQueue(m_hReadMsgQ);
      m_hReadMsgQ = NULL;
   }

   // Wait for threads to finish
   WaitForSingleObject(g_ButtonThread, INFINITE);
   WaitForSingleObject(g_PwrMonThread, INFINITE);

   if (g_ButtonThread)
      CloseHandle(g_ButtonThread);

   if (g_PwrMonThread)
      CloseHandle(g_PwrMonThread);

   DeleteCriticalSection(&g_csPwrThreadTime);

   DEBUGMSG(ZONE_INIT, (TEXT(__FUNCTION__) TEXT("-\r\n")));

   return TRUE;
}

/*---------------------------------------------------------------------------*/
BOOL ReadRegistryData(LPCTSTR pContext, TRegistryData *pRegistryData)
{
   HKEY  hKey;
   LONG  regError;
   DWORD dwDataSize;
   DWORD dwTemp;

   DEBUGMSG(ZONE_FUNCTION, (TEXT(__FUNCTION__) TEXT("(\"%s\")+\r\n"), pContext));

   if ((pContext == NULL) || (pRegistryData == NULL)) {
      ERRORMSG(1, (__WFUNCTION__ TEXT("(0x%X, 0x%X) parameter is invalid.\r\n"), pContext, pRegistryData));
      return FALSE;
   }

   pRegistryData->dwDebounceDelayTime      = REG_DEBOUNCE_DELAY_TIME_DEFAULT;
   pRegistryData->dwDebounceCheckSameTimes = REG_DEBOUNCE_CHECK_SAME_TIMES_DEFAULT;
   pRegistryData->dwPowerChangeInterval    = REG_POWER_CHANGE_INTERVAL_DEFAULT;
   pRegistryData->dwThreadPriority         = REG_PWRBUTTON_THREAD_PRIORITY_DEFAULT;
   pRegistryData->fActiveRelease           = (REG_PWRBUTTON_ACTIVE_RELEASE_DEFAULT != 0);
   pRegistryData->fKeyPressedIsLow         = (REG_PWRBUTTON_KEY_PRESSED_ISLOW_DEFAULT != 0);

   hKey = OpenDeviceKey(pContext);
   if (hKey == NULL) {
      DEBUGMSG(ZONE_ERROR, (TEXT("Failed OpenDeviceKey(\"%s\")\r\n"), pContext));
      return FALSE;
   }

   // Get DebounceDelayTime Default Setting
   dwDataSize = sizeof(pRegistryData->dwDebounceDelayTime);
   regError = RegQueryValueEx(hKey, REG_DEBOUNCE_DELAY_TIME_NAME, NULL, NULL, (LPBYTE)&pRegistryData->dwDebounceDelayTime, &dwDataSize);
   if (regError != ERROR_SUCCESS) {
      DEBUGMSG(ZONE_WARN, (TEXT("Failed to get \"") REG_DEBOUNCE_DELAY_TIME_NAME TEXT("\" value, Error 0x%X\r\n"), regError));
      pRegistryData->dwDebounceDelayTime = REG_DEBOUNCE_DELAY_TIME_DEFAULT;
   }
   DEBUGMSG(ZONE_FUNCTION, (TEXT("\"") REG_DEBOUNCE_DELAY_TIME_NAME TEXT("\" = %d\r\n"), pRegistryData->dwDebounceDelayTime));

   // Get DebounceCheckSameTimes Default Setting
   dwDataSize = sizeof(pRegistryData->dwDebounceCheckSameTimes);
   regError = RegQueryValueEx(hKey, REG_DEBOUNCE_CHECK_SAME_TIMES_NAME, NULL, NULL, (LPBYTE)&pRegistryData->dwDebounceCheckSameTimes, &dwDataSize);
   if (regError != ERROR_SUCCESS) {
      DEBUGMSG(ZONE_WARN, (TEXT("Failed to get \"") REG_DEBOUNCE_CHECK_SAME_TIMES_NAME TEXT("\" value, Error 0x%X\r\n"), regError));
      pRegistryData->dwDebounceCheckSameTimes = REG_DEBOUNCE_CHECK_SAME_TIMES_DEFAULT;
   }
   DEBUGMSG(ZONE_FUNCTION, (TEXT("\"") REG_DEBOUNCE_CHECK_SAME_TIMES_NAME TEXT("\" = %d\r\n"), pRegistryData->dwDebounceCheckSameTimes));

   // Get PowerChangeInterval Default Setting
   dwDataSize = sizeof(pRegistryData->dwPowerChangeInterval);
   regError = RegQueryValueEx(hKey, REG_POWER_CHANGE_INTERVAL_NAME, NULL, NULL, (LPBYTE)&pRegistryData->dwPowerChangeInterval, &dwDataSize);
   if (regError != ERROR_SUCCESS) {
      DEBUGMSG(ZONE_WARN, (TEXT("Failed to get \"") REG_POWER_CHANGE_INTERVAL_NAME TEXT("\" value, Error 0x%X\r\n"), regError));
      pRegistryData->dwPowerChangeInterval = REG_POWER_CHANGE_INTERVAL_DEFAULT;
   }
   DEBUGMSG(ZONE_FUNCTION, (TEXT("\"") REG_POWER_CHANGE_INTERVAL_NAME TEXT("\" = %d\r\n"), pRegistryData->dwPowerChangeInterval));

   // Get Priority Default Setting
   dwDataSize = sizeof(pRegistryData->dwThreadPriority);
   regError = RegQueryValueEx(hKey, REG_PWRBUTTON_THREAD_PRIORITY_NAME, NULL, NULL, (LPBYTE)&pRegistryData->dwThreadPriority, &dwDataSize);
   if (regError != ERROR_SUCCESS) {
      DEBUGMSG(ZONE_WARN, (TEXT("Failed to get \"") REG_PWRBUTTON_THREAD_PRIORITY_NAME TEXT("\" value, Error 0x%X\r\n"), regError));
      pRegistryData->dwThreadPriority = REG_PWRBUTTON_THREAD_PRIORITY_DEFAULT;
   }
   DEBUGMSG(ZONE_FUNCTION, (TEXT("\"") REG_PWRBUTTON_THREAD_PRIORITY_NAME TEXT("\" = %d\r\n"), pRegistryData->dwThreadPriority));

   // Get ActiveRelease Default Setting
   dwDataSize = sizeof(dwTemp);
   regError = RegQueryValueEx(hKey, REG_PWRBUTTON_ACTIVE_RELEASE_NAME, NULL, NULL, (LPBYTE)&dwTemp, &dwDataSize);
   if (regError != ERROR_SUCCESS) {
      DEBUGMSG(ZONE_WARN, (TEXT("Failed to get \"") REG_PWRBUTTON_ACTIVE_RELEASE_NAME TEXT("\" value, Error 0x%X\r\n"), regError));
      dwTemp = REG_PWRBUTTON_ACTIVE_RELEASE_DEFAULT;
   }
   DEBUGMSG(ZONE_FUNCTION, (TEXT("\"") REG_PWRBUTTON_ACTIVE_RELEASE_NAME TEXT("\" = %d\r\n"), dwTemp));
   pRegistryData->fActiveRelease = (dwTemp != 0);

   // Get KeyPressedIsLow Default Setting
   dwDataSize = sizeof(dwTemp);
   regError = RegQueryValueEx(hKey, REG_PWRBUTTON_KEY_PRESSED_ISLOW_NAME, NULL, NULL, (LPBYTE)&dwTemp, &dwDataSize);
   if (regError != ERROR_SUCCESS) {
      DEBUGMSG(ZONE_WARN, (TEXT("Failed to get \"") REG_PWRBUTTON_KEY_PRESSED_ISLOW_NAME TEXT("\" value, Error 0x%X\r\n"), regError));
      dwTemp = REG_PWRBUTTON_KEY_PRESSED_ISLOW_DEFAULT;
   }
   DEBUGMSG(ZONE_FUNCTION, (TEXT("\"") REG_PWRBUTTON_KEY_PRESSED_ISLOW_NAME TEXT("\" = %d\r\n"), dwTemp));
   pRegistryData->fKeyPressedIsLow = (dwTemp != 0);

   RegCloseKey(hKey);

   return TRUE;
}

/*---------------------------------------------------------------------------*/
DWORD Init(LPCTSTR pContext, LPCVOID lpvBusContext)
{
   BOOL fResult;

   DEBUGMSG(ZONE_INIT, (TEXT(__FUNCTION__) TEXT("(0x%08X, \"%s\")+\r\n"), pContext, pContext));

   InitializeCriticalSection(&g_csPwrThreadTime);

   if (!ReadRegistryData(pContext, &gRegistryData)) {
      DEBUGMSG(ZONE_INIT||ZONE_ERROR, (TEXT(__FUNCTION__) TEXT(": ReadRegistryData Failed\r\n")));
      goto CleanUp;
   }

   ConfigGPIO();

   // Call the OAL to translate the IRQ into a SysIntr value.
   if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &g_Irq, sizeof(DWORD), &g_SysIntr, sizeof(DWORD), NULL)) {
      RETAILMSG(1, (TEXT("ERROR: Failed to obtain sysintr value for power button interrupt.\r\n")));
      g_SysIntr = SYSINTR_UNDEFINED;
      return FALSE;
   }

   g_ButtonEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
   if (!g_ButtonEvent) {
      DEBUGMSG(ZONE_INIT||ZONE_ERROR, (TEXT(__FUNCTION__) TEXT(": Failed to create Intterupt Event\r\n")));
      goto CleanUp;
   }

   fResult = InterruptInitialize(g_SysIntr, g_ButtonEvent, NULL, 0);
   if (!fResult) {
      DEBUGMSG(ZONE_INIT||ZONE_ERROR, (TEXT("InterruptInitialize() failed. GetLastError=0x%x\r\n"),GetLastError()));
      goto CleanUp;
   }

   g_ButtonThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ButtonThread, NULL, 0, NULL);
   if (g_ButtonThread == NULL) {
      DEBUGMSG(ZONE_INIT||ZONE_ERROR, (TEXT(__FUNCTION__) TEXT(": Failed to create Button Intterupt Thread\r\n")));
      goto CleanUp;
   }

   g_PwrMonThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PwrMonThread, NULL, 0, NULL);
   if (g_PwrMonThread == NULL) {
      DEBUGMSG(ZONE_INIT||ZONE_ERROR, (TEXT(__FUNCTION__) TEXT(": Failed to create Power Monitor Thread\r\n")));
      goto CleanUp;
   }

   KernelIoControl(IOCTL_HAL_ENABLE_WAKE, &g_SysIntr, sizeof(g_SysIntr), NULL, 0, NULL);

   DEBUGMSG(ZONE_INIT, (TEXT(__FUNCTION__) TEXT(": Success\r\n")));
   return(TRUE);

CleanUp:

   Deinit(0);
   return FALSE;
}

