📄 fmd.cpp
字号:
// Copyright (c) David Vescovi. All rights reserved.
// Part of Project DrumStix
// Windows Embedded Developers Interest Group (WE-DIG) community project.
// http://www.we-dig.org
// 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.
//
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Module Name: FMD.CPP
Abstract: FLASH Media Driver Interface for Intel XXXXXX StrataFlash Chip
Notes: The FAL expects the Flash media to be broken up into Flash blocks
which are then subdivided into physical sectors. Some types of
Flash memory (i.e. NAND Flash) already have this layout. NOR Flash,
on the other hand, DOES NOT breakup each Flash block into physical
sectors. Instead, each byte is individually addressable (like RAM).
Despite this characteristic, NOR Flash can still logically be broken
up into discrete sectors as follows:
NOR Flash
Sector Data SectorInfo
---------------------------------
|Sector(0) | |
|Sector(1) | |
|Sector(2) | | Block 0
|... | |
|Sector(k) | |
| XXXXXXXXX
-------------------------------
|Sector(k+1) | |
|Sector(k+2) | |
|Sector(k+3) | | Block 1
|... | |
|Sector(2k) | |
| XXXXXXXXX
-------------------------------
| | |
| | |
| | | Block 2
| | |
| | |
| XXXXXXXXX
------------------------------- ...
| | |
| | |
| | | Block N
| | |
| | |
| XXXXXXXXX
---------------------------------
That is, each NOR Flash block is logically subdivided into a "page", where each page
contains space for sector data and SectorInfo metadata. Most often, Flash blocks are
a power of 2 in size but the size of a page is not a power of 2 (i.e. 512 + 8 = 520 bytes).
Thus, each Flash block DOES NOT evenly divide into an integral number of pages and some
bytes in a block are left unused. These unused bytes are denoted above by XXXXX's.
To help clarify how this process works, consider the following example: suppose you have
a NOR Flash memory device that contains 256 Flash blocks each 256K in size. From these
size characteristics, we find:
(256K / (512+8)) ==> 504 sectors + 64 unused bytes
Therefore, each Flash block can map 504 sectors (including SectorInfo metadata) and leave
64 unused bytes per block. Notice that 8 bytes is used for the SectorInfo metadata although
the SectorInfo structure is currently only 6 bytes. The reason for this is to make sure that
all sector addresses are DWORD aligned (i.e. 520 divides by 4 evenly while 518 doesn't divide
by 4 evenly). Furthemore, we see that this NOR Flash memory can map (504*256)==>129,024 physical
sectors.
-------
Two other points are worth mentioning:
1) NOR Flash memory is guaranteed by manufacturing to ship with no bad Flash blocks.
2) NOR Flash memory doesn't suffer from electrical leakage currents (like NAND Flash) and
does not require error-correction codes (ECC) to insure data integrity.
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>
#include <ceddk.h>
#ifdef READ_FROM_REGISTRY
#include <ddkreg.h>
#include "bsp.h"
#endif // READ_FROM_REGISTRY.
#include "strata.h"
//dv static BOOL g_bPairedFlash = TRUE; // Indicates whether or not two flash parts are paired to create a 32-bit data interface.
static BOOL g_bPairedFlash = FALSE; // Indicates whether or not two flash parts are paired to create a 32-bit data interface.
BOOLEAN InitializeFlash(volatile ULONG* pBaseAddress, ULONG FlashLength);
DWORD DoBufferedWrite(volatile ULONG ulBlockAddress,
volatile SECTOR_ADDR physicalSectorAddr,
PUCHAR pBuffer,
USHORT NumWriteBytes);
BOOL CheckStatus(ULONG ulBlockAddress, BOOL fLockCmd);
BOOL SignBlock(volatile ULONG ulBlockAddress, PUCHAR pusSignature);
BOOL VerifySignatures();
VOID DefineLayout();
#define MIN(a, b) (a < b ? a : b)
static BOOL g_bXIPEntire = FALSE;
static DWORD g_dwReservedBlocks = 0;
static DWORD g_dwWriteBufferSize;
static FMD_FLASH_INFO g_FMDInfo;
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function: FMD_Init()
Description: Initializes the Flash memory device.
Returns: Boolean indicating success.
------------------------------------------------------------------------------*/
PVOID FMD_Init(LPCTSTR lpActiveReg, PPCI_REG_INFO pRegIn, PPCI_REG_INFO pRegOut)
{
volatile ULONG* pBaseAddress = NULL;
ULONG FlashLength = 0;
DWORD dwLen = sizeof(DWORD);
#ifdef READ_FROM_REGISTRY
DWORD dwHardwareConfiguration;
if (lpActiveReg != NULL)
{
DDKWINDOWINFO dwi;
HKEY hConfig;
DWORD dwValue;
DWORD dwBytesReturned;
if (!KernelIoControl(IOCTL_HAL_HARDWARE_CNFG, NULL,
0, &dwHardwareConfiguration, sizeof(DWORD), &dwBytesReturned))
{
DEBUGMSG(1, (_T("IOCTL_HAL_HARDWARE_CNFG failed!\r\n")));
return NULL;
}
if (!(dwHardwareConfiguration & GUMCFG_FLASHFILE))
{
DEBUGMSG(1, (_T("FMD: Hardware configuration does not support this driver!\r\n")));
return NULL;
}
// Get flash information from the registry.
hConfig = OpenDeviceKey((LPCTSTR)lpActiveReg);
if (hConfig == NULL)
{
DEBUGMSG(1, (TEXT("ERROR: FMD_Init: OpenDeviceKey failed.\r\n")));
return(NULL);
}
dwi.cbSize = sizeof(dwi);
if (DDKReg_GetWindowInfo(hConfig, &dwi) != ERROR_SUCCESS)
{
DEBUGMSG(1, (TEXT("ERROR: FMD_Init: DDKReg_GetWindowInfo() failed.\r\n")));
return(NULL);
}
// The first memory window contains the base address and length of our flash part.
if (dwi.dwNumMemWindows)
{
pBaseAddress = (volatile ULONG*)(dwi.memWindows[0].dwBase);
// FlashLength = (ULONG)(dwi.memWindows[0].dwLen);
// dynamically determine flash size
if (dwHardwareConfiguration & GUMCFG_XM)
{
FlashLength = BOOT_FLASH_16M_SIZE;
}
else
{
FlashLength = BOOT_FLASH_4M_SIZE;
}
}
HINSTANCE hCoreDll = LoadLibrary (TEXT("coredll.dll"));
if (hCoreDll)
{
pRegQueryValueEx fnRegQueryValueEx = (pRegQueryValueEx)GetProcAddress(hCoreDll, TEXT("RegQueryValueExW"));
if (fnRegQueryValueEx)
{
fnRegQueryValueEx(hConfig, L"XIPEntireFlash", 0, NULL, (LPBYTE)&g_bXIPEntire, &dwLen);
fnRegQueryValueEx(hConfig, L"BlockSize", 0, NULL, (LPBYTE)&g_FMDInfo.BlockSize, &dwLen);
fnRegQueryValueEx(hConfig, L"WriteBufferSize", 0, NULL, (LPBYTE)&g_dwWriteBufferSize, &dwLen);
if (fnRegQueryValueEx(hConfig, L"ReservedBlocks", 0, NULL, (LPBYTE)&g_dwReservedBlocks, &dwLen) == ERROR_SUCCESS)
{
// test if flash image boot
// ..if so fail ... no room for a flash drive
if (!KernelIoControl(IOCTL_HAL_BOOTFLAGS, NULL,
0, &dwValue, sizeof(DWORD), &dwBytesReturned))
{
DEBUGMSG(1, (_T("IOCTL_HAL_BOOTFLAGS failed!\r\n")));
return NULL;
}
if (dwValue & BOOTFLAGS_BOOTEDFLASH)
return NULL;
pBaseAddress = (volatile ULONG*)(dwi.memWindows[0].dwBase + (g_dwReservedBlocks * g_FMDInfo.BlockSize));
if (dwHardwareConfiguration & GUMCFG_PERREG)
FlashLength -= (g_dwReservedBlocks * g_FMDInfo.BlockSize + IMAGE_WINCE_PERREG_SIZE);
else
FlashLength -= (g_dwReservedBlocks * g_FMDInfo.BlockSize);
}
if (fnRegQueryValueEx(hConfig, L"IsPairedFlash", 0, NULL, (LPBYTE)&g_bPairedFlash, &dwLen) != ERROR_SUCCESS) {
DEBUGMSG(1, (TEXT("FMD_Init: Warning, IsPairedFlash not specified in registry. Using value of 0.\r\n")));
}
if (fnRegQueryValueEx(hConfig, L"SectorSize", 0, NULL, (LPBYTE)&g_FMDInfo.SectorSize, &dwLen) != ERROR_SUCCESS) {
SYSTEM_INFO SystemInfo;
GetSystemInfo (&SystemInfo);
g_FMDInfo.SectorSize = SystemInfo.dwPageSize;
}
}
FreeLibrary (hCoreDll);
}
}
#else
// Get flash base address and length from caller.
if (!pRegIn || !pRegIn->MemBase.Num || !pRegIn->MemLen.Num)
{
DEBUGMSG(1, (TEXT("ERROR: FMD_Init: invalid flash memory base and/or length specified by caller.\r\n")));
return(NULL);
}
else
{
pBaseAddress = (volatile ULONG*)pRegIn->MemBase.Reg[0];
FlashLength = pRegIn->MemLen.Reg[0];
}
#endif // READ_FROM_REGISTRY.
// Identify the flash part and collect device information.
//
if (!InitializeFlash(pBaseAddress, FlashLength))
{
DEBUGMSG(1, (TEXT("ERROR: FMD_Init: Failed to initialize flash.\r\n")));
return(NULL);
}
// Verify that the block signatures are all intact
if (!VerifySignatures())
{
DEBUGMSG(1, (TEXT("ERROR: FMD_Init: Error while trying to verify block signatures.\r\n")));
return(NULL);
}
DEBUGMSG(1, (TEXT("INFO: FMD_Init: Flash Address=0x%x Length=0x%x.\r\n"), (ULONG)pBaseAddress, FlashLength));
#ifdef READ_FROM_REGISTRY
if (dwHardwareConfiguration & GUMCFG_XM)
RETAILMSG(1, (TEXT("+FMD_Init 16M %d reserved blocks.\r\n"),g_dwReservedBlocks));
else
RETAILMSG(1, (TEXT("+FMD_Init 4M %d reserved blocks.\r\n"),g_dwReservedBlocks));
#endif // READ_FROM_REGISTRY.
return((PVOID) pBaseAddress);
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function: FMD_Deinit()
Description: De-initializes the Flash memory device.
Returns: Boolean indicating success.
------------------------------------------------------------------------------*/
BOOL FMD_Deinit(PVOID pBaseAddress)
{
return(TRUE);
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function: FMD_GetInfo()
Description: Determines the size characteristics for the Flash memory device.
Notes: Notice that although each byte of a NOR Flash block is individually
addressable, the media is still logically broken up into sectors.
To compute the number of sectors per block, you have to divide the
total Flash block size by the number of bytes per sector and the number
bytes for the sector metadata.
Returns: Boolean indicating success.
------------------------------------------------------------------------------*/
BOOL FMD_GetInfo(PFlashInfo pFlashInfo)
{
pFlashInfo->flashType = NOR;
pFlashInfo->dwNumBlocks = g_FMDInfo.TotalFlashBlocks;
pFlashInfo->dwBytesPerBlock = g_FMDInfo.BlockSize;
pFlashInfo->wDataBytesPerSector = (WORD)g_FMDInfo.SectorSize;
pFlashInfo->wSectorsPerBlock = (WORD)g_FMDInfo.SectorsPerBlock;
return(TRUE);
}
/*
@func BOOLEAN | UnlockBlock | Unlocks the specified flash block.
@rdesc TRUE returned on success, FALSE on failure.
@comm
@xref
*/
BOOLEAN SetBlockLock(BLOCK_ID blockID, ULONG NumBlocks, BOOL bLock)
{
volatile ULONG ulBlockAddress = 0;
DEBUGMSG(1, (TEXT("SetBlockLock: ID %d num %d lock %x\r\n"),blockID,NumBlocks,bLock));
while (NumBlocks--)
{
// Compute the block address.
ulBlockAddress = g_FMDInfo.BaseAddress + (blockID * g_FMDInfo.BlockSize);
// Clear the status register.
WRITE_COMMAND(ulBlockAddress, CLEAR_STATUS_CMD);
// Set or Clear the block lock bit and confirm.
WRITE_COMMAND(ulBlockAddress, BLOCK_LOCK_CMD);
if (bLock)
{
WRITE_COMMAND(ulBlockAddress, BLOCK_SETLOCK_CMD);
}
else
{
WRITE_COMMAND(ulBlockAddress, BLOCK_PROCEED_CMD);
}
if (!CheckStatus (ulBlockAddress, TRUE))
{
return FALSE;
}
//dv fix
// Set the block back to read array mode...
WRITE_COMMAND(ulBlockAddress, READ_ARRAY_CMD);
++blockID;
}
return(TRUE);
}
VOID GetPhysicalSectorAddress (DWORD dwSector, PSECTOR_ADDR pStartSectorAddr, PSECTOR_ADDR pStartSectorInfoAddr)
{
// Determine the block this physical sector resides in
DWORD dwBlockID = dwSector / g_FMDInfo.SectorsPerBlock;
DWORD dwSectorOffset = dwSector % g_FMDInfo.SectorsPerBlock;
SECTOR_ADDR BlockAddr = g_FMDInfo.BaseAddress + (dwBlockID * g_FMDInfo.BlockSize);
*pStartSectorAddr = BlockAddr + (dwSectorOffset * g_FMDInfo.SectorSize);
if (pStartSectorInfoAddr && !g_bXIPEntire) {
*pStartSectorInfoAddr = BlockAddr + g_FMDInfo.SectorInfoOffset + (dwSectorOffset * sizeof(SectorInfo));
}
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function: FMD_GetPhysSectorAddr()
Description: Returns the physical address for the physical sector passed in.
Returns: None
------------------------------------------------------------------------------*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -