//-----------------------------------------------------------------------------
//
// 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.
//
//-----------------------------------------------------------------------------
//
//  Copyright (C) 2004-2007, Freescale Semiconductor, Inc. All Rights Reserved.
//  THIS SOURCE CODE, AND ITS USE AND DISTRIBUTION, IS SUBJECT TO THE TERMS
//  AND CONDITIONS OF THE APPLICABLE LICENSE AGREEMENT
//
//-----------------------------------------------------------------------------
//
//  File: ioctl.c
//
//  This file implements the OEM's IO Control (IOCTL) functions and declares
//  global variables used by the IOCTL component.
//
//-----------------------------------------------------------------------------

#include <bsp.h>
#include "ioctl_pmic.h"
#include <ioctl_batt.h>
#include "imx31_refclock.h"

#include "devload.h"

//-----------------------------------------------------------------------------
// External Functions

extern BOOL OALPmicIoctlCspiLock(UINT32 code, VOID *pInpBuffer, UINT32 inpSize,
                                 VOID *pOutBuffer, UINT32 outSize,
                                 UINT32 *pOutSize);
extern BOOL OALPmicIoctlCspiUnlock(UINT32 code, VOID *pInpBuffer,
                                   UINT32 inpSize,
                                   VOID *pOutBuffer, UINT32 outSize,
                                   UINT32 *pOutSize);

// Need to disable function pointer cast warnings.  We use memcpy to move
// the bus scaling calibration routine into IRAM and then cast this address
// as a function pointer.
#pragma warning(disable: 4054 4055)

extern void OALDvfcBusFreqUpdate(UINT32 pdr0_val, UINT32 hstart_val);

//-----------------------------------------------------------------------------
// Defines

// Default is to use IRAM as codespace for bus scaling.  Define
// BUS_SCALE_FROM_NFC to use the NFC buffer instead.
//#define BUS_SCALE_FROM_NFC

// Define BUS_SCALE_GPIO to assert GPIO3_0 during bus scale sequence.  This
// is useful for calibrating the delay loop to the required 4500 AHB clocks.
#define BUS_SCALE_GPIO

//-----------------------------------------------------------------------------
// Types
typedef void (*FUNC_FREQ_UPDATE) (UINT32 , UINT32 );


//-----------------------------------------------------------------------------
// External Variables
extern PCSP_SDMA_REGS g_pSDMA;
extern PCSP_CCM_REGS g_pCCM;
extern PCSP_UART_REG g_pUART;
extern PDDK_CLK_CONFIG g_pDdkClkConfig;
extern UINT32 g_SREV;

#ifdef BUS_SCALE_GPIO
extern PCSP_GPIO_REGS g_pGPIO3;
extern PCSP_IOMUX_REGS g_pIOMUX;
#endif

//-----------------------------------------------------------------------------
// Global Variables
BOOL g_oalPostInit = FALSE;
CRITICAL_SECTION g_oalPmicMutex;
PUINT32 g_pESDCTL;
BSP_ARGS *g_pBspArgs;


//------------------------------------------------------------------------------
//
//  Global: g_oalIoctlPlatformType/OEM
//
//  Platform Type/OEM
//
LPCWSTR g_oalIoCtlPlatformType = IOCTL_PLATFORM_TYPE;
LPCWSTR g_oalIoCtlPlatformOEM  = IOCTL_PLATFORM_OEM;

//------------------------------------------------------------------------------
//
//  Global: g_oalIoctlProcessorVendor/Name/Core
//
//  Processor information
//
LPCWSTR g_oalIoCtlProcessorVendor = IOCTL_PROCESSOR_VENDOR;
LPCWSTR g_oalIoCtlProcessorName   = IOCTL_PROCESSOR_NAME;
LPCWSTR g_oalIoCtlProcessorCore   = IOCTL_PROCESSOR_CORE;

//------------------------------------------------------------------------------
//
//  Global: g_oalIoctlInstructionSet
//
//  Processor instruction set identifier
//
UINT32 g_oalIoCtlInstructionSet = IOCTL_PROCESSOR_INSTRUCTION_SET;
UINT32 g_oalIoCtlClockSpeed = IOCTL_PROCESSOR_CLOCK_SPEED;


//-----------------------------------------------------------------------------
// Local Variables
static FUNC_FREQ_UPDATE g_pFuncFreqUpdate;
static UINT32 g_sizeFuncFreqUpdate;
#ifdef BSP_DVFC_EMI_DCG
PUINT32 g_pIIM_SCS1;
#endif
UINT32  g_UartRefClk;


//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalPostInit
//
//  This function is the next OAL routine called by the kernel after OEMInit and
//  provides a context for initializing other aspects of the device prior to
//  general boot.
//
//------------------------------------------------------------------------------
BOOL OALIoCtlHalPostInit(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
#ifdef BSP_OAL_DISABLE_ALIGNMENT_FAULT
    UINT32 ctrlReg;
#endif

    // Remove-W4: Warning C4100 workaround
    UNREFERENCED_PARAMETER(code);
    UNREFERENCED_PARAMETER(pInpBuffer);
    UNREFERENCED_PARAMETER(inpSize);
    UNREFERENCED_PARAMETER(pOutBuffer);
    UNREFERENCED_PARAMETER(outSize);
    UNREFERENCED_PARAMETER(pOutSize);

    // Note that WinCE 6.00 only allows the use of critical sections whereas
    // WinCE 5.00 also allowed the use of named mutexes. Therefore, we must
    // now create and use a single critical section instead of a named mutex
    // to provide mutual exclusion between the OAL and all PMIC drivers for
    // accessing the CSPI bus.
    InitializeCriticalSection(&g_oalPmicMutex);

    // Set flag to indicate it is okay to call EnterCriticalSection() and
    // LeaveCriticalSection() within the OAL.
    g_oalPostInit = TRUE;

    g_sizeFuncFreqUpdate = IMAGE_WINCE_DVFC_IRAM_SIZE;

#ifndef BUS_SCALE_FROM_NFC
    g_pFuncFreqUpdate = (FUNC_FREQ_UPDATE) OALPAtoUA(IMAGE_WINCE_DVFC_IRAM_PA_START);

    if (g_pFuncFreqUpdate == NULL)
    {
        OALMSG(OAL_ERROR, (L"ERROR: OALPAtoUA failed for IMAGE_WINCE_DVFC_IRAM_PA_START\r\n"));
        return FALSE;
    }
    // Copy the calibration code to IRAM
    memcpy((void *) g_pFuncFreqUpdate, (void *) OALDvfcBusFreqUpdate, g_sizeFuncFreqUpdate);
#else
    g_pFuncFreqUpdate = (FUNC_FREQ_UPDATE) OALPAtoUA(CSP_BASE_REG_PA_NANDFC);
    if (g_pFuncFreqUpdate == NULL)
    {
        OALMSG(OAL_ERROR, (L"ERROR: OALPAtoUA failed for CSP_BASE_REG_PA_NANDFC\r\n"));
        return FALSE;
    }
#endif

#ifdef BUS_SCALE_GPIO
    // Configure GPIO for monitoring ARM WFI
    OAL_IOMUX_SET_MUX(g_pIOMUX, DDK_IOMUX_PIN_GPIO3_0,
                      DDK_IOMUX_OUT_GPIO, DDK_IOMUX_IN_GPIO);

    INSREG32(&g_pGPIO3->GDIR, GPIO_PIN_MASK(0),
             GPIO_PIN_VAL(GPIO_GDIR_OUTPUT, 0));
#endif

    g_pESDCTL = (PUINT32) OALPAtoUA(CSP_BASE_REG_PA_ESDCTL);
    if (g_pESDCTL == NULL)
    {
        OALMSG(OAL_ERROR, (L"ERROR: OALPAtoUA failed for "
                           L"CSP_BASE_REG_PA_ESDCTL\r\n"));
        return FALSE;
    }

#ifdef BSP_DVFC_EMI_DCG
    g_pIIM_SCS1 = (PUINT32) OALPAtoUA(CSP_BASE_REG_PA_IIM + 0x0030);
    if (g_pIIM_SCS1 == NULL)
    {
        OALMSG(OAL_ERROR, (L"ERROR: OALPAtoUA failed for IIM SCS1\r\n"));
        return FALSE;
    }

    // Configure EMI_DCG idle counter (258 AHB cycles before closing EIM clocks)
    SETREG32(&g_pCCM->CGR[0], CCM_CGR0_IIM_MASK);
    INSREG32(g_pIIM_SCS1, (0x3 << 2), (BSP_DVFC_EMI_DCG_CNT << 2));
    CLRREG32(&g_pCCM->CGR[0], CCM_CGR0_IIM_MASK);
#endif

    g_pBspArgs = (BSP_ARGS *) OALPAtoUA(IMAGE_SHARE_ARGS_RAM_PA_START);
    if (g_pBspArgs == NULL)
    {
        OALMSG(OAL_ERROR, (L"ERROR: OALPAtoUA failed for BSP_ARGS\r\n"));
        return FALSE;
    }

    g_UartRefClk = g_pBspArgs->clockFreq[DDK_CLOCK_SIGNAL_PER];

    // Conditional code to support ARM v6 unaligned data accesses
#ifdef BSP_OAL_DISABLE_ALIGNMENT_FAULT
    // Read the ARM Control Register
    ctrlReg = _MoveFromCoprocessor(15, 0, 1, 0, 0);

    // Clear the A bit (bit[1] to disable strict alignment
    // fault checking
    ctrlReg &= (~(0x1 << 1));

    // Update the ARM Control Register
    _MoveToCoprocessor(ctrlReg, 15, 0, 1, 0, 0);
#endif

    return(TRUE);
}


//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalPresuspend
//
//  This function implements IOCTL_HAL_PRESUSPEND which provides the OAL
//  the time needed to prepare for a suspend operation. Any preparation is
//  completed while the system is still in threaded mode.
//
//------------------------------------------------------------------------------
BOOL OALIoCtlHalPresuspend(
    UINT32 code, VOID* pInpBuffer, UINT32 inpSize, VOID* pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
    // Remove-W4: Warning C4100 workaround
    UNREFERENCED_PARAMETER(code);
    UNREFERENCED_PARAMETER(pInpBuffer);
    UNREFERENCED_PARAMETER(inpSize);
    UNREFERENCED_PARAMETER(pOutBuffer);
    UNREFERENCED_PARAMETER(outSize);
    UNREFERENCED_PARAMETER(pOutSize);

    // Do nothing for now.
    //
    return(TRUE);
}


//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlQueryDispSettings
//
//  This function implements IOCTL_HAL_QUERY_DISPLAYSETTINGS and is used by
//  graphics device interface (GDI) to query the kernel for information about
//  a preferred resolution for the system to use.
//
//------------------------------------------------------------------------------
BOOL OALIoCtlQueryDispSettings (
    UINT32 code, VOID *lpInBuf, UINT32 nInBufSize, VOID *lpOutBuf,
    UINT32 nOutBufSize, UINT32 *lpBytesReturned)
{
    DWORD dwErr = 0;

    // Remove-W4: Warning C4100 workaround
    UNREFERENCED_PARAMETER(code);
    UNREFERENCED_PARAMETER(lpInBuf);
    UNREFERENCED_PARAMETER(nInBufSize);

    if (lpBytesReturned) {
        *lpBytesReturned = 0;
    }

    if (!lpOutBuf) {
        dwErr = ERROR_INVALID_PARAMETER;
    }
    else if (sizeof(DWORD)*3 > nOutBufSize) {
        dwErr = ERROR_INSUFFICIENT_BUFFER;
    } else {
        __try {

            ((PDWORD)lpOutBuf)[0] = (DWORD) BSP_PREF_DISPLAY_WIDTH;
            ((PDWORD)lpOutBuf)[1] = (DWORD) BSP_PREF_DISPLAY_HEIGHT;
            ((PDWORD)lpOutBuf)[2] = (DWORD) BSP_PREF_DISPLAY_BPP;

            if (lpBytesReturned) {
                *lpBytesReturned = sizeof (DWORD) * 3;
            }

        } __except (EXCEPTION_EXECUTE_HANDLER) {
            dwErr = ERROR_INVALID_PARAMETER;
        }
    }

    if (dwErr) {
        NKSetLastError (dwErr);
    }

    return !dwErr;
}


//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlQuerySiVersion
//
//  This function implements IOCTL_HAL_QUERY_SI_VERSION and is used to query the
//  silicon version.  The result returned by this IOCTL may be used to
//  conditionally execute code for a specific silicon version of the SoC.
//
//------------------------------------------------------------------------------
BOOL OALIoCtlQuerySiVersion (
    UINT32 code, VOID *lpInBuf, UINT32 nInBufSize, VOID *lpOutBuf,
    UINT32 nOutBufSize, UINT32 *lpBytesReturned)
{
    DWORD dwErr = 0;

    // Remove-W4: Warning C4100 workaround
    UNREFERENCED_PARAMETER(code);
    UNREFERENCED_PARAMETER(lpInBuf);
    UNREFERENCED_PARAMETER(nInBufSize);

    if (lpBytesReturned) {
        *lpBytesReturned = 0;
    }

    if (!lpOutBuf) {
        dwErr = ERROR_INVALID_PARAMETER;
    }
    else if (sizeof(DWORD) > nOutBufSize) {
        dwErr = ERROR_INSUFFICIENT_BUFFER;
    } else {
        __try {

            // Return silicon rev from the IIM that was
            // was read during OEMInit
            *((PDWORD) lpOutBuf) = (DWORD) g_SREV;

            if (lpBytesReturned) {
                *lpBytesReturned = sizeof (DWORD);
            }

        } __except (EXCEPTION_EXECUTE_HANDLER) {
            dwErr = ERROR_INVALID_PARAMETER;
        }
    }

    if (dwErr) {
        NKSetLastError (dwErr);
    }

    return !dwErr;
}


//------------------------------------------------------------------------------
//
//  Function:  OALDvfcBusScale
//
//  This function scales the frequency of the bus clock (AHB clock).
//
//------------------------------------------------------------------------------
BOOL OALDvfcBusScale(UINT32 pdr0)
{
    UINT32 stop_stat, hstart;
    UINT32 acr;
    BOOL bIntrEnable;

    // SDMA ACR bit configuration can be determined by extracting the
    // IPG_PODF field of the CCM PDR0.  The IPG_PODF specifies the
    // ratio between IPG_CLK (which is the SDMA core clock) and HCLK_CLK
    // (AHB clock).
    if (pdr0 & CSP_BITFMASK(CCM_PDR0_IPG_PODF))
    {
        // IPG_PODF = 1 => 2:1 ratio => ACR = 0
        acr = 0;
    }
    else
    {
        // IPG_PODF = 0 => 1:1 ratio => ACR = 1
        acr = 1;
    }

     // Following sequence should not be interrupted/preempted
    bIntrEnable = INTERRUPTS_ENABLE(FALSE);

#ifdef BUS_SCALE_FROM_NFC

    SETREG32(&g_pCCM->CGR[2], CCM_CGR2_NFC_MASK);
    memcpy((void *) g_pFuncFreqUpdate, (void *) OALDvfcBusFreqUpdate,
           g_sizeFuncFreqUpdate);

#endif

    // Save active DMA channels
    stop_stat = INREG32(&g_pSDMA->STOP_STAT);

    // Determine SDMA channels that can be activated while the DDR
    // is being recalibrated
    hstart = stop_stat & g_pDdkClkConfig->dmaEmiMask;

    // Halt active DMA channels
    OUTREG32(&g_pSDMA->STOP_STAT, stop_stat);

    // Wait for SDMA to go idle
    while ((INREG32(&g_pSDMA->ONCE_STAT) & 0xF000) != 0x6000)
    {
        // OALMSG(TRUE, (_T("Waiting for DMA idle\r\n")));
        // OALMSGS(TRUE, (_T("ONCE_STAT = 0x%x\r\n"), INREG32(&g_pSDMA->ONCE_STAT)));

    }

    // Configure new SDMA ACR setting
    INSREG32BF(&g_pSDMA->CONFIG, SDMA_CONFIG_ACR, acr);

#ifdef BUS_SCALE_GPIO

    SETREG32(&g_pGPIO3->DR, GPIO_PIN_MASK(0));
#endif

    // Perform the AHB frequency shift
    g_pFuncFreqUpdate(pdr0, hstart);

#ifdef BUS_SCALE_GPIO

    CLRREG32(&g_pGPIO3->DR, GPIO_PIN_MASK(0));

#endif

    // Restore SDMA channels active before scaling AHB/IPG
    OUTREG32(&g_pSDMA->HSTART, stop_stat);

#ifdef BUS_SCALE_FROM_NFC

    CLRREG32(&g_pCCM->CGR[2], CCM_CGR2_NFC_MASK);

#endif

#if (DEBUG_PORT == DBG_UART1) || (DEBUG_PORT == DBG_UART3)

    // Check if debug UART BRM needs to be updated
    if (g_UartRefClk != g_pBspArgs->clockFreq[DDK_CLOCK_SIGNAL_PER])
    {
        g_UartRefClk = g_pBspArgs->clockFreq[DDK_CLOCK_SIGNAL_PER];

#if (DEBUG_PORT == DBG_UART1)

        // Make sure UART1 clocks are enabled
        SETREG32(&g_pCCM->CGR[0], CCM_CGR0_UART1_MASK);

#elif (DEBUG_PORT == DBG_UART3)

        // Make sure UART3 clocks are enabled
        SETREG32(&g_pCCM->CGR[1], CCM_CGR1_UART3_MASK);

#endif

        OUTREG32(&g_pUART->UBIR, (115200 / 100) - 1);
        OUTREG32(&g_pUART->UBMR, ((g_UartRefClk / 2) / 1600) - 1);
    }

#endif

    INTERRUPTS_ENABLE(bIntrEnable);

    return TRUE;
}


//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalDvfcBusScale
//
//  This function scales the frequency of the bus clock (AHB clock).
//
//------------------------------------------------------------------------------
BOOL OALIoCtlHalDvfcBusScale (
    UINT32 code, VOID *lpInBuf, UINT32 nInBufSize, VOID *lpOutBuf,
    UINT32 nOutBufSize, UINT32 *lpBytesReturned)
{
    BOOL rc = FALSE;

    // Remove-W4: Warning C4100 workaround
    UNREFERENCED_PARAMETER(code);
    UNREFERENCED_PARAMETER(lpOutBuf);
    UNREFERENCED_PARAMETER(nOutBufSize);
    UNREFERENCED_PARAMETER(lpBytesReturned);

    OALMSG(OAL_IOCTL&&OAL_FUNC, (L"+OALIoCtlHalDvfcBusScale\r\n"));

    // Check input parameters
    if (lpInBuf == NULL || nInBufSize < sizeof(DWORD))
    {
        NKSetLastError(ERROR_INVALID_PARAMETER);
        OALMSG(OAL_WARN, (
            L"WARN: IOCTL_HAL_SET_DVFC_BUS_SCALE invalid parameters\r\n"
        ));
        goto cleanUp;
    }

    OALDvfcBusScale(*(DWORD *)lpInBuf);

    rc = TRUE;

cleanUp:
    OALMSG(OAL_INTR&&OAL_FUNC, (
        L"+OALIoCtlHalDvfcBusScale(rc = %d)\r\n", rc
    ));
    return rc;
}

#define REG_DBG_SERIAL_ROOT_PATH        L"Drivers\\BuiltIn\\COM"
static BOOL ModifyRegistryForLoLo(void)
{
   HKEY hKey;
   DWORD dwStatus;
   DWORD dwSize, dwType;
   DWORD dwIoBase;
   DWORD ii;
   static TCHAR szRegistryPath[MAX_PATH];
   static TCHAR szRegistryData[16];
   BOOL fResult=FALSE;

   // Visit every serial driver registry to disable driver load that is occupied by debug serial port
   if (gLoLoOption.fDbgSerialEnable) {
      for (ii=0; ii<5; ii++) {
         //=====================================================================
         OALLogPrintf(szRegistryPath, ARRAYSIZE(szRegistryPath), REG_DBG_SERIAL_ROOT_PATH L"%d", ii+1);

         // Get Serial Registry Setting
         dwStatus = NKRegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegistryPath, 0, 0, &hKey);
         if (dwStatus != ERROR_SUCCESS) {
            OALMSG(OAL_IOCTL, (L"NKRegOpenKeyEx(%s) Failed!\r\n", szRegistryPath));
            continue;
         }

         // Check "IoBase" for correct driver
         dwSize = sizeof(dwIoBase);
         dwType = REG_DWORD;
         dwStatus = NKRegQueryValueEx(hKey, L"IoBase", NULL, &dwType, (PBYTE)&dwIoBase, &dwSize);
         if (dwStatus != ERROR_SUCCESS) {
            OALMSG(OAL_IOCTL||OAL_ERROR, (L"NKRegQueryValueEx(%s\\IoBase) Failed!\r\n", szRegistryPath));
            NKRegCloseKey(hKey);
            continue;
         }
#if DEBUG_PORT != DBG_UART1
#define Unknow DEBUG_PORT Setting for COM port disable
#endif
         if (dwIoBase == CSP_BASE_REG_PA_UART1) {
            // Clear "Dll"
            szRegistryData[0] = L'\0';
            dwStatus = NKRegSetValueEx(hKey, DEVLOAD_DLLNAME_VALNAME, 0, DEVLOAD_DLLNAME_VALTYPE, (PBYTE)szRegistryData, sizeof(szRegistryData[0]));
            if (dwStatus != ERROR_SUCCESS) {
               NKRegCloseKey(hKey);
               break;
            }
            OALMSG(OAL_INFO||OAL_FUNC, (L"OAL: Debug Serial in Use. \r\nOAL: Disabling \"%s\" driver\r\n", szRegistryPath));
         }

         NKRegCloseKey(hKey);
      }
   }

   fResult = TRUE;

   return fResult;
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalGetHiveCleanFlag
//
//  This function implements IOCTL_HAL_GET_HIVE_CLEAN_FLAG and is used to
//  determine if the registry hive needs to be erased when booting.
//
//------------------------------------------------------------------------------
BOOL OALIoCtlHalGetHiveCleanFlag(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
   BOOL fRetVal = FALSE;
   DWORD *pdwFlags = (DWORD*)pInpBuffer;
   BOOL  *pfClean  = (BOOL*)pOutBuffer;

   OALMSG(OAL_FUNC||OAL_IOCTL, (__WFUNCTION__ L"+\r\n"));

   if ((inpSize < sizeof(DWORD)) || (pInpBuffer == NULL) || (outSize < sizeof(DWORD)) || (pOutBuffer == NULL)) {
      NKSetLastError(ERROR_INVALID_PARAMETER);
      OALMSG(OAL_WARN, (L"WARN: " __WFUNCTION__ L": Invalid parameter.\r\n"));
      return FALSE;
   }

   if (*(DWORD*)pInpBuffer == HIVECLEANFLAG_SYSTEM) {
      OALMSG(OAL_IOCTL, (__WFUNCTION__ L"(HIVECLEANFLAG_SYSTEM)+\r\n"));

      ModifyRegistryForLoLo();

      *pfClean = gLoLoOption.fCleanSysHive;
   }
   else if (*(DWORD*)pInpBuffer == HIVECLEANFLAG_USERS) {
      OALMSG(OAL_IOCTL, (__WFUNCTION__ L"(HIVECLEANFLAG_USERS)+\r\n"));

      ModifyRegistryForLoLo();

      // We are done checking HiveCleanFlag by now (system hive is checked before user hive).
      // Now is the time to clear the global shared Args flag if it is set by switch or software.
      *pfClean = gLoLoOption.fCleanUsrHive;
   }
   else {
      OALMSG(OAL_IOCTL||OAL_ERROR, (__WFUNCTION__ L"(Unknow)+\r\n"));
      *pfClean = FALSE;
   }

   fRetVal = TRUE;

   if (pOutSize != NULL) {
      *pOutSize = sizeof(DWORD);
   }

   OALMSG(OAL_FUNC||OAL_IOCTL, (__WFUNCTION__ L"(retVal=%c)-\r\n", (fRetVal?'T':'F')));
   return fRetVal;
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalGetBootParamValue
//
//  This IOCTL is used by device driver to query the OEM to get the LogicLoader
//     parameter value.
//
//------------------------------------------------------------------------------
BOOL OALIoCtlHalGetBootParamValue(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
   unsigned int cbSize;
   unsigned char *pValue;
   BOOL fRetVal = FALSE;

   OALMSG(OAL_FUNC||OAL_IOCTL, (TEXT(__FUNCTION__) L"+\r\n"));

   if ((inpSize < sizeof(char)) || (pInpBuffer == NULL) || (outSize < sizeof(char)) || (pOutBuffer == NULL)) {
      NKSetLastError(ERROR_INVALID_PARAMETER);
      OALMSG(OAL_ERROR, (L"ERROR: " TEXT(__FUNCTION__) L": Invalid parameter.\r\n"));
      return FALSE;
   }

   if (pOutSize != NULL) {
      *pOutSize = 0;
   }

   /* Search for the key in the boot parameter string. */
   pValue = hal_param_get_value((unsigned char *)pInpBuffer, &cbSize);

   if (pValue == NULL) {
      /* The key wasn't found. */
      NKSetLastError(ERROR_INVALID_DATA);
      fRetVal = FALSE;
   }
   else {
      if (pOutSize != NULL) {
         *pOutSize = cbSize + 1;
      }

      if ((pOutBuffer == NULL) || ((cbSize+1) > outSize)) {
         /* Not enough room to transfer the key. */
         NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
         fRetVal = FALSE;
      }
      else {
         /* Copy the parameter's value into the caller's buffer
          * and NULL-terminate the string. */
         memcpy(pOutBuffer, pValue, cbSize);
         ((unsigned char*)pOutBuffer)[cbSize] = '\0';
         fRetVal = TRUE;
      }
   }

   OALMSG(OAL_FUNC||OAL_IOCTL, (TEXT(__FUNCTION__) L"(retVal=%c)-\r\n", (fRetVal?'T':'F')));
   return fRetVal;
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalRefClockNotifyChanged
//
//  This notifies the kernel that the Reference Clock Frequencies have changed.
//
//------------------------------------------------------------------------------
BOOL OALIoCtlHalRefClockNotifyChanged(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
   BOOL fRetVal = FALSE;

   OALMSG(OAL_FUNC||OAL_IOCTL, (TEXT(__FUNCTION__) L"+\r\n"));

   if (pOutSize != NULL) {
      *pOutSize = 0;
   }

   IMX31RefClockNotifyChanged();

   OALMSG(OAL_FUNC||OAL_IOCTL, (TEXT(__FUNCTION__) L"(retVal=%c)-\r\n", (fRetVal?'T':'F')));
   return fRetVal;
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalRefClockGet
//
//  This input/output control requests the specified clock frequency.
//
//------------------------------------------------------------------------------
BOOL OALIoCtlHalRefClockGet(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
   BOOL fRetVal = FALSE;

   OALMSG(OAL_FUNC||OAL_IOCTL, (TEXT(__FUNCTION__) L"+\r\n"));

   if ((inpSize < sizeof(DDK_CLOCK_SIGNAL)) || (pInpBuffer == NULL) || (pOutBuffer == NULL)) {
      NKSetLastError(ERROR_INVALID_PARAMETER);
      OALMSG(OAL_ERROR||OAL_IOCTL, (L"ERROR: " TEXT(__FUNCTION__) L": Invalid parameter.\r\n"));
      return FALSE;
   }

   if (pOutSize != NULL) {
      *pOutSize = sizeof(UINT32);
   }

   if (outSize < sizeof(UINT32)) {
      NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
      OALMSG(OAL_ERROR||OAL_IOCTL, (L"ERROR: " TEXT(__FUNCTION__) L": Insufficient buffer(In:%d, Expect:%d).\r\n", outSize, sizeof(UINT32)));
      return FALSE;
   }

   fRetVal = IMX31RefClockGet(*(DDK_CLOCK_SIGNAL*)pInpBuffer, (UINT32*)pOutBuffer);

   if (!fRetVal) {
      NKSetLastError(ERROR_INVALID_PARAMETER);
   }

   OALMSG(OAL_FUNC||OAL_IOCTL, (TEXT(__FUNCTION__) L"(retVal=%c)-\r\n", (fRetVal?'T':'F')));
   return fRetVal;
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalCPUClockModeSet
//
//  This input/output control sets the requested clock frequency
//     and voltage necessary to run at that frequency.
//
//------------------------------------------------------------------------------
BOOL OALIoCtlHalCPUClockModeSet(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
   BOOL fRetVal = FALSE;

   OALMSG(OAL_FUNC||OAL_IOCTL, (TEXT(__FUNCTION__) L"+\r\n"));

   if ((inpSize < sizeof(DWORD)) || (pInpBuffer == NULL)) {
      NKSetLastError(ERROR_INVALID_PARAMETER);
      OALMSG(OAL_ERROR||OAL_IOCTL, (L"ERROR: " TEXT(__FUNCTION__) L": Invalid parameter.\r\n"));
      return FALSE;
   }

   if (pOutSize != NULL) {
      *pOutSize = 0;
   }

   fRetVal = IMX31RefClockSetOverclock(*(DWORD*)pInpBuffer);

   if (!fRetVal) {
      NKSetLastError(ERROR_INVALID_PARAMETER);
   }

   OALMSG(OAL_FUNC||OAL_IOCTL, (TEXT(__FUNCTION__) L"(retVal=%c)-\r\n", (fRetVal?'T':'F')));
   return fRetVal;
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalCPUClockModeGet
//
//  This input/output control returns whether we are in overclock mode.
//
//------------------------------------------------------------------------------
BOOL OALIoCtlHalCPUClockModeGet(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize)
{
   BOOL fRetVal = FALSE;

   OALMSG(OAL_FUNC||OAL_IOCTL, (TEXT(__FUNCTION__) L"+\r\n"));

   if (pOutBuffer == NULL) {
      NKSetLastError(ERROR_INVALID_PARAMETER);
      OALMSG(OAL_ERROR||OAL_IOCTL, (L"ERROR: " TEXT(__FUNCTION__) L": Invalid parameter.\r\n"));
      return FALSE;
   }

   if (pOutSize != NULL) {
      *pOutSize = sizeof(DWORD);
   }

   if (outSize < sizeof(DWORD)) {
      NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
      OALMSG(OAL_ERROR||OAL_IOCTL, (L"ERROR: " TEXT(__FUNCTION__) L": Insufficient buffer(In:%d, Expect:%d).\r\n", outSize, sizeof(DWORD)));
      return FALSE;
   }

   *(DWORD*)pOutBuffer = IMX31RefClockGetOverclock();

   if (!fRetVal) {
      NKSetLastError(ERROR_INVALID_PARAMETER);
   }

   OALMSG(OAL_FUNC||OAL_IOCTL, (TEXT(__FUNCTION__) L"(retVal=%c)-\r\n", (fRetVal?'T':'F')));
   return fRetVal;
}


#if defined(BSP_SMARTPHONE) || defined(BSP_POCKETPC)
//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalProfile
//
//  This function implements IOCTL_HAL_GET_PROFILE.
//
//------------------------------------------------------------------------------
BOOL OALIoCtlHalProfile(
    UINT32 code, VOID *lpInBuf, UINT32 nInBufSize, VOID *lpOutBuf,
    UINT32 nOutBufSize, UINT32 *lpBytesReturned)
{
    DWORD dwErr = 0;

    if (lpBytesReturned) {
        *lpBytesReturned = 0;
    }

    if (!lpOutBuf) {
        dwErr = ERROR_INVALID_PARAMETER;
    }

    if (dwErr) {
        NKSetLastError (dwErr);
    }

    // We always return FALSE here because we do not support profiling in
    // normal builds.
    return FALSE;
}

//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalGetPowerOnReason
//
//  This function implements IOCTL_HAL_GET_POWERONREASON and is used for
//  determining why the device powered on.
//
//------------------------------------------------------------------------------
BOOL OALIoCtlHalGetPowerOnReason(
    UINT32 code, VOID *lpInBuf, UINT32 nInBufSize, VOID *lpOutBuf,
    UINT32 nOutBufSize, UINT32 *lpBytesReturned)
{
    DWORD dwErr = 0;

    if (lpBytesReturned) {
        *lpBytesReturned = 0;
    }

    if (!lpOutBuf) {
        dwErr = ERROR_INVALID_PARAMETER;
    }

    else if (sizeof(POWERONREASON) > nOutBufSize) {
        dwErr = ERROR_INSUFFICIENT_BUFFER;
    } else {
        __try {

            POWERONREASON *pReasonStruct = (POWERONREASON *)lpOutBuf;

            // For now, we always give the reason as a reboot. Later on, when
            // we have persistent storage properly implemented, then we can
            // also support the other possible power on reasons (if required):
            //
            //    - due to a reminder (POWERON_REMINDER)
            //    - due to a scheduled time event (POWERON_SCHEDULEDTIME)
            //    - due to a system interrupt
            //

            pReasonStruct->dwFlags  = PO_REASON;
            pReasonStruct->dwReason = POWERON_CPM_REBOOT;

            if (lpBytesReturned) {
                *lpBytesReturned = sizeof (POWERONREASON);
            }

        } __except (EXCEPTION_EXECUTE_HANDLER) {
            dwErr = ERROR_INVALID_PARAMETER;
        }
    }

    if (dwErr) {
        NKSetLastError (dwErr);
    }

    return FALSE;
}


//------------------------------------------------------------------------------
//
//  Function:  OALIoCtlHalGetRndisMacAddr
//
//  This function implements IOCTL_HAL_GET_RNDIS_MACADDR and is used by the
//  OEM to determine the RNDIS MAC address.
//
//------------------------------------------------------------------------------
BOOL OALIoCtlHalGetRndisMacAddr(
    UINT32 code, VOID *lpInBuf, UINT32 nInBufSize, VOID *lpOutBuf,
    UINT32 nOutBufSize, UINT32 *lpBytesReturned)
{
    DWORD dwErr = 0;

    if (lpBytesReturned) {
        *lpBytesReturned = 0;
    }

    if (!lpOutBuf) {
        dwErr = ERROR_INVALID_PARAMETER;
    }
    else if (sizeof(BYTE) * 6 > nOutBufSize) {
        dwErr = ERROR_INSUFFICIENT_BUFFER;
    }

    if (dwErr) {
        NKSetLastError (dwErr);
    }

    // We always return FALSE here because we do not need to override the
    // default RNDIS MAC address.
    return FALSE;
}
#endif // defined(BSP_SMARTPHONE) || defined(BSP_POCKETPC)


//------------------------------------------------------------------------------
//
//  Global: g_oalIoCtlTable[]
//
//  IOCTL handler table. This table includes the IOCTL code/handler pairs
//  defined in the IOCTL configuration file. This global array is exported
//  via oal_ioctl.h and is used by the OAL IOCTL component.
//
const OAL_IOCTL_HANDLER g_oalIoCtlTable[] = {
#include "ioctl_tab.h"
};

//------------------------------------------------------------------------------
