/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE. 
Copyright (c) 2002  Microsoft Corporation

Module Name:	FMD.CPP

Abstract:		FLASH Media Driver Interface Samsung K9F2808UOB NAND Flash Chip
                on AT-Rise development board.

Notes:			Currently, *only* the CE 3.0 (and earlier) power management logic
				is implemented (i.e. the PowerUp() and PowerDown() APIs).

Environment:	As noted, this media driver works on behalf of the FAL to directly
				access the underlying FLASH hardware.  Consquently, this module
				needs to be linked with FAL.LIB to produce the device driver
				named FLASHDRV.DLL.

-----------------------------------------------------------------------------*/
#include <fmd.h>

//#ifdef CEDAR_ONLY
	#ifndef NOSYSCALL
//		#include "utldrv.h"
		#include <nkintr.h>
		#include "oalintr.h"
	#endif
//#endif // CEDAR_ONLY

#ifdef NOBINFS
	#include "utldrv.h"			// Still needed for driver->driver fast method calling in 4.x
#endif

#include <s2440.h>
#include "cfnand.h"
#include "loader.h"

#ifndef NOSYSCALL
	#ifndef BOOT_LOADER
//		#define USENANDDMA	1
//		#define USESETKMODE 1
//		#define NAND_BUFFER_LENG	512
	#endif
#endif

#ifdef NOBINFS
//#ifdef CEDAR_ONLY			// Still needed in 4.x
//  Globals
HANDLE          g_hUTLObject = NULL;
UTL_FASTCALL    g_tblFastCall;
//#endif // CEDAR_ONLY
#endif 

// Globals needed for updatexip
HANDLE g_hMutex = NULL;

// BUGBUG: For now, we always will take the mutex
BOOL g_bTakeMutex = TRUE;

void GRABMUTEX();
void RELEASEMUTEX();

#ifdef NOSYSCALL
#ifndef BOOT_LOADER
// function prototypes for kernel functions
extern "C"
{
HANDLE SC_CreateMutex(LPSECURITY_ATTRIBUTES lpsa, BOOL bInitialOwner, LPCTSTR lpName);
DWORD SC_WaitForMultiple(DWORD cObjects, CONST HANDLE *lphObjects, BOOL fWaitAll, DWORD dwTimeout);
BOOL SC_ReleaseMutex(HANDLE hMutex);
BOOL SC_CloseHandle(HANDLE hObj);
}
#endif
#endif


//  Use Macros here to avoid extra over head for c function calls
#define READ_REGISTER_BYTE(p) (*(volatile PBYTE) (p))
#define WRITE_REGISTER_BYTE(p, v) (*(volatile PBYTE)(p)) = (v)
#define READ_REGISTER_USHORT(p) (*(volatile PUSHORT) (p))
#define WRITE_REGISTER_USHORT(p, v) (*(volatile PUSHORT)(p)) = (v)
#define READ_REGISTER_ULONG(p)  (*(volatile PULONG) (p))
#define WRITE_REGISTER_ULONG(p, v) (*(volatile PULONG)(p)) = (v)

//  Registers
volatile PUSHORT pNFReg;
volatile PUSHORT pNFCONF;
volatile PUSHORT pNFCONT;
volatile PUSHORT pNFCMD;
volatile PUSHORT pNFADDR;
volatile PULONG  pNFDATA;
volatile PUSHORT pNFSTAT;
volatile PULONG  pNFECC;
volatile CLKPWRreg *v_s2440CLKPWR;
volatile DMAreg *v_pDMAregs;
volatile INTreg *v_pINTregs;    

#define NFDATA 0x4E000010

// Event
HANDLE	gDMA3IntrEvent;
PBYTE	pDMABuffer;

//  Status bit pattern
#define STATUS_READY                0x40
#define STATUS_ERROR                0x01

// HCLK=133Mhz
#define TACLS		0
#define TWRPH0		6
#define TWRPH1		2

//  MACROS
#define NF_CE_L()			WRITE_REGISTER_USHORT(pNFCONT, (USHORT) (READ_REGISTER_USHORT(pNFCONT) & ~(1<<1)))
#define NF_CE_H()			WRITE_REGISTER_USHORT(pNFCONT, (USHORT) (READ_REGISTER_USHORT(pNFCONT) | (1<<1)))
#define NF_CMD(cmd)			WRITE_REGISTER_USHORT(pNFCMD, (USHORT) (cmd))
#define NF_ADDR(addr)		WRITE_REGISTER_USHORT(pNFADDR, (USHORT) (addr))
#define NF_DATA_R()			READ_REGISTER_BYTE(pNFDATA)
#define NF_DATA_W(val)		WRITE_REGISTER_BYTE(pNFDATA, (BYTE) (val))
#define NF_DATA_R4()		READ_REGISTER_ULONG(pNFDATA)
#define NF_DATA_W4(val)		WRITE_REGISTER_ULONG(pNFDATA, (ULONG) (val))
#define NF_STAT()			READ_REGISTER_USHORT(pNFSTAT)
#define NF_MECC_UnLock()	WRITE_REGISTER_USHORT(pNFCONT, (USHORT) (READ_REGISTER_USHORT(pNFCONT) & ~(1<<5)))
#define NF_MECC_Lock()		WRITE_REGISTER_USHORT(pNFCONT, (USHORT) (READ_REGISTER_USHORT(pNFCONT) | (1<<5)))
#define NF_RSTECC()			WRITE_REGISTER_USHORT(pNFCONT, (USHORT) (READ_REGISTER_USHORT(pNFCONT) | (1<<4)))
#define NF_WAITRB()			{while(!(NF_STAT() & (1<<1))) ;}
#define NF_CLEAR_RB()		WRITE_REGISTER_USHORT(pNFSTAT, (USHORT) (READ_REGISTER_USHORT(pNFSTAT) | (1<<2)))
#define NF_DETECT_RB()		{while(!(NF_STAT() & (1<<2)));}
#define NF_ECC()			READ_REGISTER_ULONG(pNFECC)

//  External function
extern "C" {
BOOL ECC_CorrectData(LPBYTE pData, LPBYTE pExistingECC, LPBYTE pNewECC);
}

//  Flags and pointers
#define BADBLOCKMARK                0x00
//  VALIDADDR is 5 << 8
//
//  Explain:    5 means the 6th byte in spare area (517 byte in the sector)
//              Shift 8 bit to the left to form the correct address for 16bit port
//
#define VALIDADDR   0x05

#define OEMADDR		0x04					// 5th byte in spare area

#ifdef CEDAR_ONLY
void PowerOnCallback()
{
    DEBUGMSG(1, (TEXT("FlashDrv::FMD:: Come back form Power Off!\r\n")));
}
#endif // CEDAR_ONLY

//  Reset the chip
//
void NF_Reset()
{
	int i;
    GRABMUTEX();
    NF_CE_L();

	NF_CLEAR_RB();
    NF_CMD(CMD_RESET);
	for(i=0;i<10;i++);  //tWB = 100ns. //??????

    NF_CE_H();
    RELEASEMUTEX();
}

/*
 Here is the statement from SamSung's K9F2808U0B datasheet:

 All device locations are erased (FFh) except location where the invalid block(s)
 information is written prior to shipping. The invalid block(s) status is defined
 by the 6th byte in the spare area. Samsung makes sure that either the 1st or 2nd
 page of every invalid block has non-FFh data at the column address of 517. Since
 the invalid block information is also erasable in most cases, it is impossible to
 recover the information once it has been erased.

 Here is the logic we are taking:

 If the block is invalid, non-FFh value in column address of 517, we don't use that
 block anyway. Otherwise, the whole spare area is subject to use by our program. If
 we found additional bad block later on, we don't necessary have to use column 517
 to represent invalid block. We could use any of the 16 byte spare area.

 But for simplicity, in our current implementation, we avoid to touch column 517. So
 the we allocate SectorInfo as following:

  - - - - - - - - - - - - - - - -
 |R|R|R|R|O|V|R|R|E|E|E|E|E|E|E|E|
  - - - - - - - - - - - - - - - -

 Where  Rs are reserved bytes used by the FAL
        O is a byte for use by the OEM
        V is a byte indicating if the block is valid (a.k.a. bad)
        Es are bytes used for ECC

 */

/*
 *  NAND_ReadSectorInfo
 *
 *  Read SectorInfo out of the spare area. The current implementation only handles
 *  one sector at a time.
 */
void NAND_ReadSectorInfo(SECTOR_ADDR sectorAddr, PSectorInfo pInfo)
{
// RETAILMSG(1, (TEXT("NAND_ReadSectorInfo() page 0x%x!\r\n"), sectorAddr));

    GRABMUTEX();
    
    //  Chip enable
    NF_CE_L();
	NF_CLEAR_RB();

    //  Write the command
    NF_CMD(CMD_READ2);

    //  Write the address
    NF_ADDR(0x00);
    NF_ADDR(sectorAddr & 0xff);
    NF_ADDR((sectorAddr >> 8) & 0xff);

    if (NEED_EXT_ADDR) {
        NF_ADDR((sectorAddr >> 16) & 0xff);
    }

	NF_DETECT_RB();	 // Wait tR(max 12us)

    pInfo->dwReserved1  = NF_DATA_R4();

    //  OEM byte
    pInfo->bOEMReserved = (BYTE) NF_DATA_R();
    //  Read the bad block mark
    pInfo->bBadBlock = (BYTE) NF_DATA_R();

    //  Second reserved field (WORD)
    pInfo->wReserved2 = ((BYTE) NF_DATA_R() << 8);
    pInfo->wReserved2 |= ((BYTE) NF_DATA_R());

    NF_CE_H();

    RELEASEMUTEX();
}

/*
 *  NAND_WriteSectorInfo
 *
 *  Write SectorInfo out to the spare area. The current implementation only handles
 *  one sector at a time.
 */
BOOL NAND_WriteSectorInfo(SECTOR_ADDR sectorAddr, PSectorInfo pInfo)
{
    BOOL    bRet = TRUE;
// RETAILMSG(1, (TEXT("NAND_WriteSectorInfo() Programming page 0x%x!\r\n"), sectorAddr));

    GRABMUTEX();

    //  Chip enable
    NF_CE_L();
	NF_CLEAR_RB();

    //  Write the command
    //  First, let's point to the spare area
    NF_CMD(CMD_READ2);
    NF_CMD(CMD_WRITE);

    //  Write the address
    NF_ADDR(0x00);
    NF_ADDR(sectorAddr & 0xff);
    NF_ADDR((sectorAddr >> 8) & 0xff);

    if (NEED_EXT_ADDR) {
        NF_ADDR((sectorAddr >> 16) & 0xff);
    }

    //  Now let's write the SectorInfo data
    //
    //  Write the first reserved field (DWORD)
    NF_DATA_W4( pInfo->dwReserved1 );

    //  Write OEM reserved flag
    NF_DATA_W( (pInfo->bOEMReserved) );

    //  Write the bad block flag
    NF_DATA_W( (pInfo->bBadBlock) );

    //  Write the second reserved field
    NF_DATA_W( (pInfo->wReserved2 >> 8) & 0xff );
    NF_DATA_W( (pInfo->wReserved2) );

    //  Issue the write complete command
    NF_CMD(CMD_WRITE2);

    //  Check ready bit
	NF_DETECT_RB();	 // Wait tR(max 12us)

    //  Check the status of program
    NF_CMD(CMD_STATUS);

    if(NF_DATA_R() & STATUS_ERROR) {
        RETAILMSG(1, (TEXT("NandFlash Error Programming sector info %d!\r\n"), sectorAddr));
        bRet = FALSE;
    }

    NF_CE_H();

    RELEASEMUTEX();
    return bRet;
}

/*-----------------------------------------------------------------------------
 *  FMD Interface functions
 *
 *----------------------------------------------------------------------------*/
//  FMD_Init
//
//  Initialize the flash chip
//
//  Note: Presently, the Flash size characteristics are hardcoded in CFNAND.H
//		  and are NOT stored in the registry.  Refer to the StratFlash FMD in
//		  %WINCEROOT%\PUBLIC\COMMON\OAK\DRIVERS\BLOCK\... for an example of how
//		  to use the registry for storing this information.
//
PVOID FMD_Init(LPCTSTR lpActiveReg, PPCI_REG_INFO pRegIn, PPCI_REG_INFO pRegOut)
{
	RETAILMSG(1, (TEXT("NandFlash FMD_Init\r\n")));
    //  0. Create the Mutex for shared access between the kernel and MSFLASH
#ifndef BOOT_LOADER
    if (g_hMutex == NULL)
    {
#ifdef NOSYSCALL
#ifndef BOOT_LOADER
        g_hMutex = SC_CreateMutex(NULL, FALSE, TEXT("_FLASH_MUTEX_"));
#endif
#else
        g_hMutex = CreateMutex(NULL, FALSE, TEXT("_FLASH_MUTEX_"));
#endif
        // was mutex creation successful?
        if (g_hMutex == NULL) 
        {
            RETAILMSG(1,(TEXT("NandFlash failed to create mutex\r\n")));
            goto ErrExit;
        }
    }
#endif
    
#ifndef NOSYSCALL
#ifdef NOBINFS
    DWORD dwBytes;

    //  Initialize the FASTCALL structure to 0
    memset(&g_tblFastCall, 0, sizeof(UTL_FASTCALL));

    //  1. CreateFile to get the access to UTLDRV.
    //
    g_hUTLObject = CreateFile(TEXT("UTL0:"), GENERIC_READ|GENERIC_WRITE,
                                FILE_SHARE_READ|FILE_SHARE_WRITE,
                                NULL, OPEN_EXISTING, 0, 0);

    if(INVALID_HANDLE_VALUE == g_hUTLObject) {
        RETAILMSG(1,(TEXT("NandFlash failed to create file UTL %x\r\n"), GetLastError()));
        goto ErrExit;
    }

    //  2. Call IOCTL to get the FAST CALL function pointer.
    //
    if(!DeviceIoControl(g_hUTLObject,
                        IOCTL_UTL_GET_FASTCALL,
                        NULL, 0,
                        &g_tblFastCall, sizeof(g_tblFastCall),
                        &dwBytes, NULL)) {
        RETAILMSG(1,(TEXT("NandFlash failed to DeviceIoControl %x\r\n"), GetLastError()));
        goto ErrExit;
    }

    //  3. Call the fast call to get the PVA for the registers
    //
    //  3.1 Get the register for NAND controller
    if (ERROR_SUCCESS != g_tblFastCall.GetRegisterVA(g_tblFastCall.pContext, NFC_BASE,
                                     32, FALSE, (DWORD *)&pNFReg) ) {
        RETAILMSG(1,(TEXT("NandFlash failed to GetRegisterVA \r\n")));
        goto ErrExit;
    }

    //  3.2 Get the register for the CLKPWR register
    if (ERROR_SUCCESS != g_tblFastCall.GetRegisterVA(g_tblFastCall.pContext, CLKPWR_BASE,
                                     32, FALSE, (DWORD *)&v_s2440CLKPWR) ) {
        RETAILMSG(1,(TEXT("NandFlash failed to GetRegisterVA for CLKPWR\r\n")));
        goto ErrExit;
    }
#ifdef CEDAR_ONLY
    // ++ CE 3.0 Specific Code. Not needed for 4.x +

    //  4. Register the Block driver to the Power On Monitor
    //
    if (ERROR_SUCCESS != g_tblFastCall.RegisterBlockDrv(g_tblFastCall.pContext,
                                                        PowerOnCallback)) {
        RETAILMSG(1,(TEXT("NandFlash failed to RegisterBlockDrv %x\r\n"), GetLastError()));
        goto ErrExit;
    }

    // -- CE 3.0 Specific Code. Not needed for 4.x +
#endif // CEDAR_ONLY
#else
//	RETAILMSG(1, (TEXT("FMD::FMD_Init:VirtualAlloc \r\n")));
    pNFReg = (PUSHORT) VirtualAlloc(0, 0x1000, MEM_RESERVE, PAGE_NOACCESS);

    if(!pNFReg) {
        RETAILMSG(1,(TEXT("NandFlash failed to VirtualAlloc \r\n")));
        goto ErrExit;
    }

    if(!VirtualCopy(pNFReg, (LPVOID) NFC_BASE, 0x1000, 
                    PAGE_READWRITE|PAGE_NOCACHE)) {
        RETAILMSG(1,(TEXT("NandFlash failed to VirtualCopy for NFC_BASE\r\n")));
        goto ErrExit;
    }

    //  Allocate for the clock control register

    //  Clock control register
    v_s2440CLKPWR = (CLKPWRreg *)VirtualAlloc(NULL, 0x1000, MEM_RESERVE, PAGE_NOACCESS);
    ASSERT(v_s2440CLKPWR);

    if (!VirtualCopy((LPVOID) v_s2440CLKPWR, (LPVOID) CLKPWR_BASE, 0x1000,
                     PAGE_READWRITE | PAGE_NOCACHE)) {
        RETAILMSG(1,(TEXT("NandFlash failed to VirtualCopy for CLKPWR_BASE\r\n")));
        goto ErrExit;
    }

#ifdef USENANDDMA
	// From HMSEO
	// DMA Virtual alloc
	v_pDMAregs = (volatile DMAreg *) VirtualAlloc(0,sizeof(DMAreg),MEM_RESERVE, PAGE_NOACCESS);
    ASSERT(v_pDMAregs);

	if(!VirtualCopy((PVOID)v_pDMAregs,(PVOID)(DMA_BASE),sizeof(DMAreg),
			PAGE_READWRITE | PAGE_NOCACHE )) {
        RETAILMSG(1,(TEXT("NandFlash failed to VirtualCopy for DMA_BASE\r\n")));
        goto ErrExit;
	}

	// Allocate DMA buffer
	pDMABuffer = (unsigned char *)VirtualAlloc(0, NAND_BUFFER_LENG, MEM_RESERVE, PAGE_NOACCESS);
    ASSERT(pDMABuffer);
                                  
	if(!VirtualCopy(pDMABuffer,
					(PVOID)NAND_DMA_BUFFER_BASE,
					NAND_BUFFER_LENG,
					PAGE_READWRITE | PAGE_NOCACHE) )
	{
        RETAILMSG(1,(TEXT("NandFlash failed to VirtualCopy for NAND_DMA_BUFFER_BASE\r\n")));
        goto ErrExit;
    }

#ifndef USESETKMODE
	// allocate the interrupt event for DMA
	gDMA3IntrEvent = CreateEvent(NULL, FALSE, FALSE,NULL);

	if (NULL == gDMA3IntrEvent) {
        RETAILMSG(1,(TEXT("NandFlash failed to CreateEvent \r\n")));
        goto ErrExit;
	}
    
	if (!InterruptInitialize (SYSINTR_DMA3, gDMA3IntrEvent,NULL,0)) {
        RETAILMSG(1,(TEXT("NandFlash failed to InterruptInitialize \r\n")));
        goto ErrExit;
	}
#else
    v_pINTregs = (volatile INTreg *) VirtualAlloc(0,sizeof(INTreg),MEM_RESERVE, PAGE_NOACCESS);
    ASSERT(v_pINTregs);

	if(!VirtualCopy((PVOID)v_pINTregs,(PVOID)(INT_BASE),sizeof(INTreg),
		    PAGE_READWRITE | PAGE_NOCACHE ))
	{
        RETAILMSG(1,(TEXT("NandFlash failed to VirtualCopy for INT_BASE\r\n")));
        goto ErrExit;
    }
#endif USESETKMODE
	// End HMSEO
#endif USENANDDMA
#endif // NOBINFS

#else
    pNFReg = (PUSHORT) (NFC_BASE | 0x20000000);
    v_s2440CLKPWR = (CLKPWRreg *) ( CLKPWR_BASE | 0x20000000 );
#endif

    //  Enable the clock to NAND controller
    v_s2440CLKPWR->rCLKCON |= (1<<4);

//	RETAILMSG(1, (TEXT("FlashDrv!FMD!FMD_Init: pNFReg = %x \r\n"), pNFReg));

    pNFCONF = pNFReg;
    pNFCONT = (PUSHORT) ((PBYTE) pNFReg + 0x04);
    pNFCMD  = (PUSHORT) ((PBYTE) pNFReg + 0x08);
    pNFADDR = (PUSHORT) ((PBYTE) pNFReg + 0x0C);
    pNFDATA = (PULONG) ((PBYTE) pNFReg + 0x10);
    pNFSTAT = (PUSHORT) ((PBYTE) pNFReg + 0x20);
    pNFECC  = (PULONG)  ((PBYTE) pNFReg + 0x2C);

    //  Now we need enable the NAND Flash controller
    GRABMUTEX();
    WRITE_REGISTER_USHORT(pNFCONF, (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0));
    WRITE_REGISTER_USHORT(pNFCONT, (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(1<<1)|(1<<0));
    WRITE_REGISTER_USHORT(pNFSTAT, 0);
    RELEASEMUTEX();

//	NF_Reset();

	RETAILMSG(1, (TEXT("NandFlash FMD_Init Done\r\n")));

    return (PVOID)pNFCONF;

ErrExit:
    FMD_Deinit((PVOID)pNFCONF);
    return 0;
}

//  FMD_Deinit
//
//  De-initialize the flash chip
//
BOOL    FMD_Deinit(PVOID hFMD)
{
	if((DWORD)hFMD != (DWORD)pNFCONF)
	{
		return FALSE;
	}

	// destroy the mutex
	if (g_hMutex)
	{
#ifdef NOSYSCALL
#ifndef BOOT_LOADER
        SC_CloseHandle(g_hMutex);
#endif
#else
        CloseHandle(g_hMutex);
#endif
	}

#ifndef NOSYSCALL
#ifdef NOBINFS
    //  We don't have to free pNFReg and clock register.
#ifdef CEDAR_ONLY
    // ++ CE 3.0 Specific Code. Not needed for 4.x +
    if (g_tblFastCall.UnRegisterBlockDrv) {
        g_tblFastCall.UnRegisterBlockDrv(g_tblFastCall.pContext, PowerOnCallback);
    }
    // -- CE 3.0 Specific Code. Not needed for 4.x +
#endif // CEDAR_ONLY

    //  Close the handle
    if (g_hUTLObject) {
        CloseHandle(g_hUTLObject);
    }
#else
    if(pNFReg) {
        VirtualFree(pNFReg, 0, MEM_RELEASE);
    }

    if(v_s2440CLKPWR) {
        VirtualFree((LPVOID)v_s2440CLKPWR, 0, MEM_RELEASE);
    }
#endif // NOBINFS

#endif
    return TRUE;
}

//  FMD_GetInfo
//
//  Return the Flash information
//
BOOL    FMD_GetInfo(PFlashInfo pFlashInfo)
{
    pFlashInfo->flashType = NAND;

    //  OK, instead of reading it from the chip, we use the hardcoded
    //  numbers here.

    pFlashInfo->dwNumBlocks			= NUM_BLOCKS - IMAGE_START_BLOCK;
    pFlashInfo->wSectorsPerBlock	= PAGES_PER_BLOCK;
    pFlashInfo->wDataBytesPerSector = SECTOR_SIZE;

    return TRUE;
}

//  FMD_ReadSector
//
//  Read the content of the sector.
//
//  startSectorAddr: Starting page address
//  pSectorBuff  : Buffer for the data portion
//  pSectorInfoBuff: Buffer for Sector Info structure
//  dwNumSectors : Number of sectors
//

typedef union _ECCRegVal
{
    DWORD   dwECCVal;
    BYTE    bECCBuf[4];
} ECCRegVal;

BOOL FMD_ReadSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff,
                        PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
    DWORD       i;
    BYTE        eccBuf[8];
    ECCRegVal   eccRegVal;

    TRANSLATE_SECTOR(startSectorAddr);
    if (!pSectorBuff && !pSectorInfoBuff || dwNumSectors > 1) {
#ifdef BOOT_LOADER
        RETAILMSG(1,(TEXT("NandFlash FMD_ReadSector Invalid parameters\r\n")));
#else
        RETAILMSG(1,(TEXT("NandFlash FMD_ReadSector Invalid parameters\r\n")));
#endif
#ifndef NOSYSCALL
        SetLastError(ERROR_INVALID_PARAMETER);
#endif
        return FALSE;
    }

	NF_Reset();

    if(!pSectorBuff) {
        //  We are reading spare only
        NAND_ReadSectorInfo(startSectorAddr, pSectorInfoBuff);

        //  There is no ECC for the sector info, so the read always succeed.
        return TRUE;
    }

//	RETAILMSG(1, (TEXT("FlashDrv!FMD!FMD_ReadSector:  startSectorAddr = %x \r\n"), startSectorAddr));

	GRABMUTEX();

	//  Initialize ECC register
	NF_RSTECC();
	NF_MECC_UnLock();

	//  Enable the chip
	NF_CE_L();
	NF_CLEAR_RB();

	//  Issue command
	NF_CMD(CMD_READ);

	//  Set up address
	NF_ADDR(0x00);
	NF_ADDR((startSectorAddr) & 0xff);
	NF_ADDR((startSectorAddr >> 8) & 0xff);

	if (NEED_EXT_ADDR) {
		NF_ADDR((startSectorAddr >> 16) & 0xff);
	}

	NF_DETECT_RB();	 // Wait tR(max 12us)

	//  BUGBUG, because Media Player for Pocket PC sometimes pass us un-aligned buffer
	//  we have to waste cycle here to work around this problem
	if( ((DWORD) pSectorBuff) & 0x3) {
		for(i=0; i<SECTOR_SIZE; i++) {
			pSectorBuff[i] = (BYTE) NF_DATA_R();
		}
	}
	else {
		//  The right way.
#ifndef USENANDDMA
		ReadPage512(pSectorBuff, pNFDATA);
#else	// USENANDDMA
#ifdef USESETKMODE
		SetKMode(TRUE);
		v_pINTregs->rSRCPND=BIT_DMA3;	// Init DMA src pending.
#endif	// USESETKMODE
		// Nand to memory dma setting
		v_pDMAregs->rDISRC3  = (unsigned int)NFDATA; 	// Nand flash data register
		v_pDMAregs->rDISRCC3 = (0<<1) | (1<<0); //arc=AHB,src_addr=fix
		v_pDMAregs->rDIDST3  = (int)NAND_DMA_BUFFER_PHYS;
		v_pDMAregs->rDIDSTC3 = (0<<1) | (0<<0); //dst=AHB,dst_addr=inc;
		v_pDMAregs->rDCON3   = (1<<31)|(1<<30)|(1<<29)|(1<<28)|(1<<27)|(0<<23)|(1<<22)|(2<<20)|(512/4/4);
		//Handshake,AHB,interrupt,(4-burst),whole,S/W,no_autoreload,word,count=128;

		// DMA on and start.
		v_pDMAregs->rDMASKTRIG3 = (1<<1)|(1<<0);
#ifndef USESETKMODE
		WaitForSingleObject(gDMA3IntrEvent, INFINITE);
		InterruptDone(SYSINTR_DMA3);
#else // USESETKMODE
		while(!(v_pINTregs->rSRCPND & BIT_DMA3));	// Wait until Dma transfer is done.
		v_pINTregs->rSRCPND=BIT_DMA3;
		SetKMode(FALSE);
#endif // USESETKMODE
		memcpy(pSectorBuff, pDMABuffer, 512);
#endif	// USENANDDMA
    }


    //  Do the ECC thing here
    //  We read the ECC value from the ECC register pFNECC
	NF_MECC_Lock();
    eccRegVal.dwECCVal = NF_ECC();

    //  Read the SectorInfo data
    if(pSectorInfoBuff) {
        //  Read the SectorInfo data (we only need to read first 8 bytes)
        pSectorInfoBuff->dwReserved1  = NF_DATA_R4();

        //  OEM byte
        pSectorInfoBuff->bOEMReserved = (BYTE) NF_DATA_R();

        //  Read the bad block mark
        pSectorInfoBuff->bBadBlock = (BYTE) NF_DATA_R();

        //  Second reserved field (WORD)
        pSectorInfoBuff->wReserved2 = ((BYTE) NF_DATA_R() << 8);
        pSectorInfoBuff->wReserved2 |= ((BYTE) NF_DATA_R());
    }
    else {
        //  Advance the read pointer
        for(i=0; i<sizeof(SectorInfo); i++) {
            eccBuf[i] = (BYTE) NF_DATA_R();
        }
    }

    //  Verify the ECC values
    //
    //  Read the ECC buffer
    for(i=0; i<3; i++) {
        eccBuf[i] = (BYTE) NF_DATA_R();
    }

    NF_CE_H();

    //  Copmare with the ECC generated from the HW

// RETAILMSG(1,(TEXT("NandFlash FMD_ReadSector ECC x%x %x\r\n"),*(DWORD *)eccBuf,eccRegVal.dwECCVal ));
    if(eccBuf[0] != eccRegVal.bECCBuf[0] ||
       eccBuf[1] != eccRegVal.bECCBuf[1]  ||
       eccBuf[2] != eccRegVal.bECCBuf[2] ) {

#ifdef BOOT_LOADER
        RETAILMSG(1,(TEXT("NandFlash FMD_ReadSector ECC Error,page %d parameters\r\n"), startSectorAddr));
#else
        RETAILMSG(1,(TEXT("NandFlash FMD_ReadSector ECC Error,page %d parameters\r\n"), startSectorAddr));
#endif

        //  Now try to correct them
        if(!ECC_CorrectData(pSectorBuff, eccBuf, eccRegVal.bECCBuf)) {
            RETAILMSG(1,(TEXT("NandFlash FMD_ReadSector fatal ECC Error\r\n")));
            RELEASEMUTEX();
			return FALSE;
        }
    }

    RELEASEMUTEX();
    return TRUE;
}

//
//  IsBlockBad
//
//  Check to see if the given block is bad. A block is bad if the 517th byte on
//  the first or second page is not 0xff.
//
//  blockID:    The block address. We need to convert this to page address
//
//
BOOL IsBlockBad(BLOCK_ID blockID)
{
    DWORD   dwPageID = BLOCK_TO_ADDRESS(blockID);
    BOOL    bRet = FALSE;
    BYTE    wFlag;

  GRABMUTEX();

    //  Enable the chip
    NF_CE_L();
	NF_CLEAR_RB();

    //  Issue the command
    NF_CMD(CMD_READ2);

    //  Set up address
    NF_ADDR(VALIDADDR);
    NF_ADDR((dwPageID) & 0xff);
    NF_ADDR((dwPageID >> 8) & 0xff);

    if (NEED_EXT_ADDR) {
        NF_ADDR((dwPageID >> 16) & 0xff);
    }

    //  Wait for Ready bit
	NF_DETECT_RB();	 // Wait tR(max 12us)

    //  Now get the byte we want
    wFlag = (BYTE) NF_DATA_R();

    if(wFlag != 0xff) {
        bRet = TRUE;
    }

    //  Disable the chip
    NF_CE_H();

    RELEASEMUTEX();
    return bRet;
}

//
//  FMD_GetBlockStatus
//
//  Returns the status of a block.  The status information is stored in the spare area of the first sector for
//  the respective block.
//
//  A block is BAD if the bBadBlock byte on the first page is not equal to 0xff.
//
DWORD FMD_GetBlockStatus(BLOCK_ID blockID)
{
	//translate done in FMD_ReadSector,should not use BLOCK_TO_ADDRESS
    SECTOR_ADDR sectorAddr = BLOCK_TO_SECTOR(blockID);
    SectorInfo SI;
    DWORD dwResult = 0;

	if(!FMD_ReadSector(sectorAddr, NULL, &SI, 1))
	{
        return BLOCK_STATUS_UNKNOWN;
	}

    if(!(SI.bOEMReserved & OEM_BLOCK_READONLY))
	{
        dwResult |= BLOCK_STATUS_READONLY;
	}

    if(SI.bBadBlock != 0xFF)
	{
        dwResult |= BLOCK_STATUS_BAD;
	}

    return dwResult;
}




//  FMD_EraseBlock
//
//  Erase the given block
//
BOOL FMD_EraseBlock(BLOCK_ID blockID)
{
    BOOL    bRet = TRUE;
    DWORD   dwPageID = BLOCK_TO_ADDRESS(blockID);
//RETAILMSG(1, (TEXT("# NandFlash FMD_EraseBlock %d...\r\n"), blockID));

    GRABMUTEX();

#ifndef NOSYSCALL
#endif
    //  Enable the chip
    NF_CE_L();
	NF_CLEAR_RB();

    //  Issue command
    NF_CMD(CMD_ERASE);

    //  Set up address
    NF_ADDR((dwPageID) & 0xff);
    NF_ADDR((dwPageID >> 8) & 0xff);

    if (NEED_EXT_ADDR) {
        NF_ADDR((dwPageID >> 16) & 0xff);
    }

    //  Complete erase operation
    NF_CMD(CMD_ERASE2);

    //  Wait for ready bit
	NF_DETECT_RB();	 // Wait tR(max 12us)

    //  Check the status
    NF_CMD(CMD_STATUS);

    if(NF_DATA_R() & STATUS_ERROR) {
        RETAILMSG(1,(TEXT("NandFlash FMD_EraseBlock Error %d \r\n"), blockID));
        bRet = FALSE;
    }

    NF_CE_H();

    RELEASEMUTEX();

    return bRet;
}



//  FMD_WriteSector
//
//  Write dwNumPages pages to the startSectorAddr
//
BOOL FMD_WriteSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff,
                        DWORD dwNumSectors)
{
    DWORD   i;
    BOOL    bRet = TRUE;
    DWORD   dwECCVal;
    BYTE    eccBuf[4];

    TRANSLATE_SECTOR(startSectorAddr);
// RETAILMSG(1, (TEXT("NAND_WriteSector %d\r\n"),startSectorAddr));
    if((!pSectorBuff && !pSectorInfoBuff) || dwNumSectors != 1) {
#ifdef BOOT_LOADER
        RETAILMSG(1, (TEXT("NandFlash NAND_WriteSector Invalid parameters!\r\n")));
#else
        RETAILMSG(1, (TEXT("NandFlash NAND_WriteSector Invalid parameters!\r\n")));
#endif
#ifndef NOSYSCALL
        SetLastError(ERROR_INVALID_PARAMETER);
#endif
        return FALSE;
    }

	NF_Reset();

    if(!pSectorBuff) {
        //  If we are asked just to write the SectorInfo, we will do that separately
        bRet = NAND_WriteSectorInfo(startSectorAddr, pSectorInfoBuff);
		return bRet;			// Do not write the actual sector information...
    }

    GRABMUTEX();

    //  Initialize ECC register
    NF_RSTECC();
	NF_MECC_UnLock();

    //  Enable Chip
    NF_CE_L();

    //  Issue command
    NF_CMD(CMD_READ);
    NF_CMD(CMD_WRITE);

    //  Setup address
    NF_ADDR(0x00);
    NF_ADDR((startSectorAddr) & 0xff);
    NF_ADDR((startSectorAddr >> 8) & 0xff);

    if (NEED_EXT_ADDR) {
        NF_ADDR((startSectorAddr >> 16) & 0xff);
    }
/*
        for(i=0; i<SECTOR_SIZE; i++) {
            RETAILMSG(1, (TEXT("%02X "),pSectorBuff[i]));
            if((i & 0xF) == 0xF) RETAILMSG(1, (TEXT("\r\n")));
        }
*/
    //  Special case to handle un-aligned buffer pointer.
    //
    if( ((DWORD) pSectorBuff) & 0x3) {
        //  Write the data
        for(i=0; i<SECTOR_SIZE; i++) {
            NF_DATA_W(pSectorBuff[i]);
        }
    }
    else {
#ifndef USENANDDMA
        WritePage512(pSectorBuff, pNFDATA);
#else	// USENANDDMA
#ifdef USESETKMODE
		SetKMode(TRUE);
		v_pINTregs->rSRCPND=BIT_DMA3;	// Init DMA src pending.
#endif // USESETKMODE
		memcpy(pDMABuffer, pSectorBuff, 512);
		// Nand to memory dma setting
	    v_pDMAregs->rDISRC3  = (int)SDI_DMA_BUFFER_PHYS; 	// Nand flash data register
	    v_pDMAregs->rDISRCC3 = (0<<1) | (0<<0); //arc=AHB,src_addr=inc
	    v_pDMAregs->rDIDST3  = (int)NFDATA;
	    v_pDMAregs->rDIDSTC3 = (0<<1) | (1<<0); //dst=AHB,dst_addr=fix;
	    v_pDMAregs->rDCON3   = (1<<31)|(1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(1<<22)|(2<<20)|(512/4);
		//  only unit transfer in writing!!!!
		//Handshake,AHB,interrupt,(unit),whole,S/W,no_autoreload,word,count=128;

		// DMA on and start.
	    v_pDMAregs->rDMASKTRIG3 = (1<<1)|(1<<0);

#ifndef USESETKMODE
		WaitForSingleObject(gDMA3IntrEvent, INFINITE);
		InterruptDone(SYSINTR_DMA3);
#else // USESETKMODE
		while(!(v_pINTregs->rSRCPND & BIT_DMA3));	// Wait until Dma transfer is done.
		v_pINTregs->rSRCPND=BIT_DMA3;
		SetKMode(FALSE);
#endif	// USESETKMODE
#endif	// USENANDDMA
    }

    //  Read out the ECC value generated by HW
	NF_MECC_Lock();
    dwECCVal = NF_ECC();

	// Write the SectorInfo data to the media
	// NOTE: This hardware is odd: only a byte can be written at a time and it must reside in the
	//       upper byte of a USHORT.
	if(pSectorInfoBuff)
	{
        //  Write the first reserved field (DWORD)
        NF_DATA_W4(pSectorInfoBuff->dwReserved1);

        //  Write OEM reserved flag
        NF_DATA_W( (pSectorInfoBuff->bOEMReserved) );

        //  Write the bad block flag
        NF_DATA_W( (pSectorInfoBuff->bBadBlock) );

        //  Write the second reserved field
        NF_DATA_W( (pSectorInfoBuff->wReserved2 >> 8) & 0xff );
        NF_DATA_W( (pSectorInfoBuff->wReserved2) & 0xff );

	}else
	{
		// Make sure we advance the Flash's write pointer (even though we aren't writing the SectorInfo data)
		for(i=0; i<sizeof(SectorInfo); i++)
		{
            NF_DATA_W(0xff);
		}
	}
// RETAILMSG(1,(TEXT("NandFlash FMD_WriteSector ECC %x\r\n"),dwECCVal));
    //  ECC stuff should be here
    eccBuf[0] = (BYTE) ((dwECCVal) & 0xff);
    eccBuf[1] = (BYTE) ((dwECCVal >> 8) & 0xff);
    eccBuf[2] = (BYTE) ((dwECCVal >> 16) & 0xff);

    //  Write the ECC value to the flash
    for(i=0; i<3; i++) {
        NF_DATA_W(eccBuf[i]);
    }

    for(i=0; i<5; i++) {
        NF_DATA_W(0xff);
    }

	NF_CLEAR_RB();
    //  Finish up the write operation
    NF_CMD(CMD_WRITE2);

    //  Wait for RB
	NF_DETECT_RB();	 // Wait tR(max 12us)

    //  Check the status
    NF_CMD(CMD_STATUS);

    if(NF_DATA_R() & STATUS_ERROR) {
#ifdef BOOT_LOADER
        RETAILMSG(1, (TEXT("NAND_WriteSector Error Programming page %d!\r\n"), startSectorAddr));
#else
        RETAILMSG(1, (TEXT("NAND_WriteSector Error Programming page %d!\r\n"), startSectorAddr));
#endif
        bRet = FALSE;
    }

    //  Disable the chip
    NF_CE_H();

    RELEASEMUTEX();
    return bRet;
}

/*
 *  MarkBlockBad
 *
 *  Mark the block as a bad block. We need to write a 00 to the 517th byte
 */

BOOL MarkBlockBad(BLOCK_ID blockID)
{
    DWORD   dwStartPage = BLOCK_TO_ADDRESS(blockID);
    BOOL    bRet = TRUE;

    GRABMUTEX();

    //  Enable chip
    NF_CE_L();
	NF_CLEAR_RB();

    //  Issue command
    //  We are dealing with spare area
    NF_CMD(CMD_READ2);
    NF_CMD(CMD_WRITE);

    //  Set up address
    NF_ADDR(VALIDADDR);
    NF_ADDR((dwStartPage) & 0xff);
    NF_ADDR((dwStartPage >> 8) & 0xff);
    if (NEED_EXT_ADDR) {
        NF_ADDR((dwStartPage >> 16) & 0xff);
    }

    NF_DATA_W(BADBLOCKMARK);

    //  Copmlete the write
    NF_CMD(CMD_WRITE2);

    //  Wait for RB
	NF_DETECT_RB();	 // Wait tR(max 12us)

    //  Get the status
    NF_CMD(CMD_STATUS);

    if(NF_DATA_R() &  STATUS_ERROR) {
        RETAILMSG(1, (TEXT("NandFlash Failed to mark bad block %d!\r\n"),blockID));
        bRet = FALSE;
    }

    //  Disable chip select
    NF_CE_H();

    RELEASEMUTEX();
    return bRet;
}

//
//  FMD_SetBlockStatus
//
//  Sets the status of a block.  Only implement for bad blocks for now.
//  Returns TRUE if no errors in setting.
//
BOOL FMD_SetBlockStatus(BLOCK_ID blockID, DWORD dwStatus)
{
	BYTE bStatus = 0;

    if(dwStatus & BLOCK_STATUS_BAD)
	{
        if(!MarkBlockBad (blockID))
        {
            return FALSE;
        }
    }

    // We don't currently support setting a block to read-only, so fail if request is
    // for read-only and block is not currently read-only.
    if(dwStatus & BLOCK_STATUS_READONLY)
	{
        if(!(FMD_GetBlockStatus(blockID) & BLOCK_STATUS_READONLY))
        {
            return FALSE;
        }
    }

    return TRUE;
}



#ifndef NOSYSCALL
//  We don't have to build the following interface functions for the
//  bootloader.
//

//  FMD_PowerUp
//
//  Performs any necessary powerup procedures...
//
VOID FMD_PowerUp(VOID)
{
    if (v_s2440CLKPWR && pNFCONF) {
        //  Enable the clock to NAND controller
        v_s2440CLKPWR->rCLKCON |= (1<<4);

        //  Reinit the NAND controller
        GRABMUTEX();
		WRITE_REGISTER_USHORT(pNFCONF, (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0));
		WRITE_REGISTER_USHORT(pNFCONT, (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(1<<1)|(1<<0));
		WRITE_REGISTER_USHORT(pNFSTAT, 0);
        RELEASEMUTEX();
        
        //  Reset the controller
        NF_Reset();
#ifdef CEDAR_ONLY
        // ++ CE 3.0 Specific Code. Not needed for 4.x +
        SetInterruptEvent(SYSINTR_POWERON);
        // -- CE 3.0 Specific Code. Not needed for 4.x +
#endif // CEDAR_ONLY
    }
}


//  FMD_PowerDown
//
//  Performs any necessary powerdown procedures...
//
VOID FMD_PowerDown(VOID)
{
    if (v_s2440CLKPWR) {
        //  Disable the clock to NAND controller
        v_s2440CLKPWR->rCLKCON &= ~(1<<4);
    }
}


//  FMD_OEMIoControl
//
//  Used for any OEM defined IOCTL operations
//
BOOL  FMD_OEMIoControl(DWORD dwIoControlCode, PBYTE pInBuf, DWORD nInBufSize,
                       PBYTE pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned)
{
    switch(dwIoControlCode)
    {
        case IOCTL_FMD_UPDATEXIP_BEGIN:
            g_bTakeMutex = TRUE;
            break;
            
        case IOCTL_FMD_UPDATEXIP_END:
            g_bTakeMutex = FALSE;
            break;
            
        default:
	        DEBUGMSG(1, (TEXT("FMD::FMD_OEMIoControl = 0x%x\r\n"), dwIoControlCode));
            return FALSE;
    }
	return TRUE;
}

#endif // NOSYSCALL



//------------------------------- Private Interface (NOT used by the FAL) --------------------------

//  FMD_GetOEMReservedByte
//
//  Retrieves the OEM reserved byte (for metadata) for the specified physical sector.
//
//
BOOL FMD_GetOEMReservedByte(SECTOR_ADDR sectorAddr, PBYTE pOEMReserved)
{
//RETAILMSG(1, (TEXT("FMD_GetOEMReservedByte() Programming page 0x%x!\r\n"), sectorAddr));
    GRABMUTEX();
    
    TRANSLATE_SECTOR(sectorAddr);
    //  Enable chip select
    NF_CE_L();
	NF_CLEAR_RB();

    //  Issue command
    NF_CMD(CMD_READ2);

    //  Set up address
    NF_ADDR(OEMADDR);
    NF_ADDR((sectorAddr) & 0xff);
    NF_ADDR((sectorAddr >> 8) & 0xff);

    if (NEED_EXT_ADDR) {
        NF_ADDR((sectorAddr >> 16) & 0xff);
    }

    //  Wait for the ready bit
	NF_DETECT_RB();	 // Wait tR(max 12us)

    //  Read the data
    *pOEMReserved = (BYTE) NF_DATA_R();

    //  Disable chip select
    NF_CE_H();

    RELEASEMUTEX();
	return TRUE;

}


//  FMD_SetOEMReservedByte
//
//  Sets the OEM reserved byte (for metadata) for the specified physical sector.
//
BOOL FMD_SetOEMReservedByte(SECTOR_ADDR sectorAddr, BYTE bOEMReserved)
{
    BOOL    bRet = TRUE;
//RETAILMSG(1, (TEXT("FMD_SetOEMReservedByte() Programming page 0x%x!\r\n"), sectorAddr));

    GRABMUTEX();

    TRANSLATE_SECTOR(sectorAddr);
    //  Enable chip select
    NF_CE_L();
	NF_CLEAR_RB();

    //  Issue command
    NF_CMD(CMD_READ2);
    NF_CMD(CMD_WRITE);

    //  Set up address
    NF_ADDR(OEMADDR);
    NF_ADDR((sectorAddr) & 0xff);
    NF_ADDR((sectorAddr >> 8) & 0xff);

    if (NEED_EXT_ADDR) {
        NF_ADDR((sectorAddr >> 16) & 0xff);
    }

    //  Write the data
    NF_DATA_W(bOEMReserved);

    //  Complete the write
    NF_CMD(CMD_WRITE2);

    //  Wait for the ready bit
	NF_DETECT_RB();	 // Wait tR(max 12us)

    //  Read the status
    NF_CMD(CMD_STATUS);

    //  Check the status
    if(NF_DATA_R() & STATUS_ERROR) {
        RETAILMSG(1, (TEXT("NandFlash Failed to set OEM Reserved byte %d\r\n"),sectorAddr));
        bRet = FALSE;
    }

    //  Disable chip select
    NF_CE_H();

    RELEASEMUTEX();
    return bRet;
}

//---------------------------------------- Helper Functions ----------------------------------------

//  Interface function for testing purpose.
//
BOOL FMD_ReadSpare(DWORD sectorAddr, LPBYTE pBuff, DWORD dwNumPages)
{
    DWORD   i, n;

    GRABMUTEX();

    TRANSLATE_SECTOR(sectorAddr);
    //  Enable chip select
    NF_CE_L();
	NF_CLEAR_RB();

    //  Issue command
    NF_CMD(CMD_READ2);

    //  Set up address
    NF_ADDR(0x00);
    NF_ADDR((sectorAddr) & 0xff);
    NF_ADDR((sectorAddr >> 8) & 0xff);

    if (NEED_EXT_ADDR) {
        NF_ADDR((sectorAddr >> 16) & 0xff);
    }

    //  Wait for Ready bit
	NF_DETECT_RB();	 // Wait tR(max 12us)

    //  Now read out the data
    for(n=0; n<dwNumPages; n++) {
        //  Read the spare area
        for(i=0; i<16; i++) {
            pBuff[n*16+i] = (BYTE) NF_DATA_R();
        }

		NF_DETECT_RB();	 // Wait tR(max 12us)
    }

    NF_CE_H();

    RELEASEMUTEX();
    return TRUE;
}

void GRABMUTEX()
{
#ifdef NOSYSCALL
#ifndef BOOT_LOADER
    // we're in the kernel - always SC_WaitForMultiple
    SC_WaitForMultiple(1, &g_hMutex, TRUE, INFINITE);
#endif
#else
    if (g_bTakeMutex) {
        // we can do a normal WaitForSingleObject
        WaitForSingleObject(g_hMutex, INFINITE);
    }
#endif
}

void RELEASEMUTEX()
{
#ifdef NOSYSCALL
#ifndef BOOT_LOADER
    SC_ReleaseMutex(g_hMutex);
#endif
#else
    if (g_bTakeMutex) {
        ReleaseMutex(g_hMutex);
    }
#endif
}
