在WinCE下,应用程序直接读/写/擦除flash设备的方法

作者:ARM-WINCE

在网上的很多论坛中都看到有人提问:应用程序如何直接读写Flash的扇区,或者是类似的问题。总之,就是希望应用程序能够直接访问Flash设备,直接读写扇区的数据,或者作其他的操作。这几天没事,就尝试着做了一下,把我的方法介绍给大家。

先做个简单的介绍。WinCE支持Flash设备,一般指Nandflash或者是NORFlash,采用的架构一般是FAL+FMD架构,我们实现FMD相关的接口函数,Flash的驱动就算完成了。当WinCE启动以后,我们能够看到Flash设备的磁盘。我们可以操作磁盘上面的文件,但是不能直接操作flash设备,对Flash设备的操作无非就是:读,写,擦除,读ID。

现在开始介绍实现的方法。我们如果想在应用程序中直接调用FMD中的FMD_ReadSector(..),FMD_WriteSector(..),FMD_EraseBlock(..)是不太现实的。这里再补充一下,这三个函数分别是Flash的读扇区,写扇区,擦除块的函数。好像有点罗嗦了。但是我们可以在应用程序中调用到FMD_OEMIoControl(..)函数,这个是可以做到的。所以我们需要改一下Flash设备的驱动程序,也就是改Flash设备驱动中的FMD_OEMIoControl(..)这个函数。我的改动如下:

BOOL FMD_OEMIoControl(DWORD dwIoControlCode, PBYTE pInBuf, DWORD nInBufSize, PBYTE pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned)
{
  PFMDInterface pInterface = (PFMDInterface)pOutBuf;


  RETAILMSG(1, (TEXT("FMD_OEMIoControl: control code is 0x%x/r/n"), dwIoControlCode));
  switch(dwIoControlCode)
  {
  case IOCTL_FMD_GET_INTERFACE:
  if (!pOutBuf || nOutBufSize < sizeof(FMDInterface))
  {
  DEBUGMSG(1, (TEXT("FMD_OEMIoControl: IOCTL_FMD_GET_INTERFACE bad parameter(s)./r/n")));
  return(FALSE);
  }  
  pInterface->cbSize = sizeof(FMDInterface);
  pInterface->pInit = FMD_Init;
  pInterface->pDeInit = FMD_Deinit;
  pInterface->pGetInfo = FMD_GetInfo;  
  pInterface->pGetInfoEx = NULL; //FMD_GetInfoEx;
  pInterface->pGetBlockStatus = FMD_GetBlockStatus;  
  pInterface->pSetBlockStatus = FMD_SetBlockStatus;
  pInterface->pReadSector = FMD_ReadSector;
  pInterface->pWriteSector = FMD_WriteSector;
  pInterface->pEraseBlock = FMD_EraseBlock;
  pInterface->pPowerUp = FMD_PowerUp;
  pInterface->pPowerDown = FMD_PowerDown;
  pInterface->pGetPhysSectorAddr = NULL;  
  pInterface->pOEMIoControl = FMD_OEMIoControl;  
  break;
  case 0xff123456:
  FMD_ReadSector(..); //调用读Sector函数
  break;
  case 0xff654321:
  FMD_WriteSector(..); //调用写Sector函数
  break;
  case 0xff123457:
  FMD_EraseBlock(..); //调用擦除Block函数
  break;
  default:
  DEBUGMSG(1, (TEXT("FMD_OEMIoControl: unrecognized IOCTL (0x%x)./r/n"), dwIoControlCode));
  return(FALSE);
  }

  return(TRUE);


在FMD_OEMIoControl(..)函数里面增加了3个case,这3个case里面调用了读/写/擦除函数。至于Case的值,我是随便定义的。这样Flash设备的驱动部分就改完了。

在改完Flash驱动以后,我下面会提供两种方法,每一种方法都和Flash设备的注册表配置有关:

1. 以Nandflash为例,当然对于NORFlash来说大同小异,注册表配置如下:

[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/NANDFlash]
"Dll"="ep94xxnandflash.dll"
"Prefix"="DSK"
"Order"=dword:4
;"Ioctl"=dword:4
"Profile"="NSFlash"
"IClass"="{A4E7EDDA-E575-4252-9D6B-4195D48BB865}"


; Override names in default profile
[HKEY_LOCAL_MACHINE/System/StorageManager/Profiles/NSFlash]
 "Name"="Ep94xx NAND Flash"
 "Folder"="NANDFlash"
 "PartitionDriver"="MSPart.dll"
 "AutoMount"=dword:1
 "AutoPart"=dword:1
 "AutoFormat"=dword:1

[HKEY_LOCAL_MACHINE/System/StorageManager/Profiles/NSFlash/FATFS]
"EnableCache"=dword:1
"CacheSize"=dword:1000
"MountBootable"=dword:1
"Flags"=dword:00000024
"CheckForFormat"=dword:1

然后编写应用程序,主要就是通过CreateFile来打开DSK1:设备,然后通过DeviceIoControl(..)函数来调用FMD_OEMIoControl(..)函数,来达到直接读/写/擦除Flash设备的目的。应用程序代码如下:

HANDLE hFirm;

 hFirm = CreateFile(TEXT("DSK1:"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
 if(hFirm == INVALID_HANDLE_VALUE)
 {
  printf("Open Flash Device Failed");
  return 0;
 }

 iRet = DeviceIoControl(hFirm, 0xff123456, para1, para2, para3, para4, para5, para6); //Read Flash Sector
 iRet = DeviceIoControl(hFirm, 0xff654321, para1, para2, para3, para4, para5, para6); //Write Flash Sector
 iRet = DeviceIoControl(hFirm, 0xff123457, para1, para2, para3, para4, para5, para6); //Erase Flash Block

 printf("DeviceIoControl OK/r/n");


 while(1)
  ;

通过上面的应用程序,就能够调用到Flash设备驱动中的FMD_OEMIoControl(..)函数,这样根据不同的case就可以调用读/写/擦除函数了。

 

2. 以Nandflash为例,当然对于NORFlash来说大同小异,注册表配置如下:

[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/NANDFlash]
"Dll"="ep94xxnandflash.dll"
"Prefix"="DSK"
"Order"=dword:4
;"Ioctl"=dword:4
"Profile"="NSFlash"
"IClass"="{A4E7EDDA-E575-4252-9D6B-4195D48BB865}"


; Override names in default profile
[HKEY_LOCAL_MACHINE/System/StorageManager/Profiles/NSFlash]
 "Name"="Ep94xx NAND Flash"
 "Folder"="NANDFlash"
 "PartitionDriver"="MSPart.dll"
 "AutoMount"=dword:1
 "AutoPart"=dword:1
 "AutoFormat"=dword:1

[HKEY_LOCAL_MACHINE/System/StorageManager/Profiles/NSFlash/FATFS]
"EnableCache"=dword:1
"CacheSize"=dword:1000
"MountBootable"=dword:1
"Flags"=dword:00000024
"CheckForFormat"=dword:1

[HKEY_LOCAL_MACHINE/System/StorageManager/AutoLoad/NSFlash]
"DriverPath"="Drivers//BuiltIn//NANDFlash"
"LoadFlags"=dword:0
"BootPhase"=dword:1

然后编写应用程序,主要就是通过OpenStore来打开NSFlash,然后通过DeviceIoControl(..)函数来调用FMD_OEMIoControl(..)函数,来达到直接读/写/擦除Flash设备的目的。应用程序代码如下:

 HANDLE hFirm;

 hFirm = OpenStore(L"NSFlash");
 if(hFirm == INVALID_HANDLE_VALUE)
 {
  printf("Open Flash Device Failed");
  return 0;
 }

 iRet = DeviceIoControl(hFirm, 0xff123456, para1, para2, para3, para4, para5, para6);
 iRet = DeviceIoControl(hFirm, 0xff654321, para1, para2, para3, para4, para5, para6);
 iRet = DeviceIoControl(hFirm, 0xff123457, para1, para2, para3, para4, para5, para6);

 printf("DeviceIoControl OK/r/n");


 while(1)
  ;

通过这种方法,也可以在应用程序中调用到FMD_OEMIoControl(..)函数,从而达到直接访问Flash设备的目的。

总结一下,上面的两种方法大致原理其实是一样的,都是通过DeviceIoControl函数来调用FMD_OEMIoControl函数,然后达到直接访问Flash驱动的目的,这样就可以在应用程序中直接读/写/擦除Flash设备了。

最后需要注意的是:你的Flash驱动里面需要对读/写/擦除等直接操作Flash硬件的函数进行保护,因为Flash设备应该是由WinCE的文件系统来管理的,而现在你的应用程序也可以直接访问它了,所以保险起见,添加互斥量保护避免访问冲突。

上面的所有实现,都是在WinCE6.0上面做得,相信在WinCE5.0上面应该差不多。





28楼 suzhbruce 2010-09-13 17:30发表 [回复] 

请问版主nanjianhui:
感谢你的分享, 现要写入数据到NandFlash, 那你上面提到的那个 iRet = DeviceIoControl(hFirm, 0xff123456, para1, para2, para3, para4, para5, para6)调用中, para1的写入数据具体格式是怎样的啊..? 难道是SG_REQ格式? 还是其它 ?
可否举例说明一下? 感谢!!
27楼 nanjianhui 2009-09-15 17:22发表 [回复] 

我认为也是可以的,不过我没有试过。你的问题好像是因为你的驱动中没有识别出正确的IOcontrol操作码。
26楼 ashazhuang 2009-09-09 11:19发表 [回复] 

WINCE5.0 无法用楼主的方法吧。我在fmd_iocontrol()添加自己的case. 出现错误提示“DSK_iocontrol unknown code (.....)”
25楼 nanjianhui 2008-11-13 09:50发表 [回复] 

这里有Nandflash的参考代码,你可以看一下: 
/WINCE600/PUBLIC/COMMON/OAK/DRIVERS/BLOCK/MSFLASHFMD/SDNPCI
24楼 redeg 2008-11-09 21:29发表 [回复] 

楼主,在PB的MSFLASHFMD目录下哪个是NAND FLASH的驱动?FASL目录好像是NOR的驱动对吗?
23楼 lskymate 2008-11-07 18:18发表 [回复] 

版主.謝謝您 
小弟目前改用deviceIoControl 
來取代readFile,writeFile已可成功 
對flash存取. 
感激
22楼 lskymate 2008-11-06 17:43发表 [回复] 

(接續上篇) 
flashReadBuffer, 

512, 

&amp;flashReadCounter, 

NULL); 


if (readFlashOk == FALSE) 
{RETAILMSG(1, (L" Read Mounted Volume Failure!! /n" )); exit(-1);} 
// ReadFile - END 

真的不曉得為什麼會出問題.. 
可以在為小弟指點一下嗎? 
非常感激 

而關於 註冊表的資訊如下 
[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/SKYFMD] 
@="" 
"Prefix"="DSK" 
"Dll"="SKYFMD.dll" 
"Index"=dword:2 
"IClass"=multi_sz:"{A4E7EDDA-E575-4252-9D6B-4195D48BB865}" 
"Profile"="SKYFMD" 

[HKEY_LOCAL_MACHINE/System/StorageManager/Profiles/SKYFMD] 
"DefaultFileSystem"="FATFS" 
"PartitionDriver"="mspart.dll" 
"Name"="SKYFALFMD" 
"AutoMount"=dword:1 
"AutoPart"=dword:1 
"AutoFormat"=dword:1 
"Ioctl"=dword:4 

感激
21楼 lskymate 2008-11-06 17:43发表 [回复] 

(接續上篇) 
flashReadBuffer, 

512, 

&amp;flashReadCounter, 

NULL); 


if (readFlashOk == FALSE) 
{RETAILMSG(1, (L" Read Mounted Volume Failure!! /n" )); exit(-1);} 
// ReadFile - END 

真的不曉得為什麼會出問題.. 
可以在為小弟指點一下嗎? 
非常感激 

而關於 註冊表的資訊如下 
[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/SKYFMD] 
@="" 
"Prefix"="DSK" 
"Dll"="SKYFMD.dll" 
"Index"=dword:2 
"IClass"=multi_sz:"{A4E7EDDA-E575-4252-9D6B-4195D48BB865}" 
"Profile"="SKYFMD" 

[HKEY_LOCAL_MACHINE/System/StorageManager/Profiles/SKYFMD] 
"DefaultFileSystem"="FATFS" 
"PartitionDriver"="mspart.dll" 
"Name"="SKYFALFMD" 
"AutoMount"=dword:1 
"AutoPart"=dword:1 
"AutoFormat"=dword:1 
"Ioctl"=dword:4 

感激
20楼 lskymate 2008-11-06 17:39发表 [回复] 

版主您好 
目前小弟希望可以透過File System來對Flash存取. 
也就是說寫一支AP.利用Logical Sector Address去 
對Flash做讀寫的動作. 

目前是計畫用 writeFile及readFile來寫入及讀取. 
但會發生exception. 

目前 CreateFile 的程式如下 

//----------------- CreateFile ----------------- 

#define FLASHPATH TEXT("//Mounted Volume//Vol:") 


hFlash = CreateFile (FLASHPATH, 

GENERIC_READ | GENERIC_WRITE, 

FILE_SHARE_READ|FILE_SHARE_WRITE, 

NULL, 

OPEN_EXISTING, 

FILE_ATTRIBUTE_NORMAL, 

NULL); 


if (hFlash == INVALID_HANDLE_VALUE) 
{ RETAILMSG(1, (L"Open Mounted Volume Failure!!/n" )); exit(-1); } 
// CreateFile - END 

讀取資料 ReadFile 的程式如下 

//----------------- ReadFile ----------------- 
flashReadBuffer = (char *) malloc (sizeof(char)*512); //接收資料的buffer 
memset (flashReadBuffer, 0, sizeof(char)*512); 


flashAddress = SetFilePointer (hFlash, 

0, 

NULL, 

FILE_BEGIN); 


readFlashOk = ReadFile (hFlash, //執行到這裡時將會發生錯誤(First - chance exception)
19楼 lskymate 2008-09-16 20:36发表 [回复] 

謝謝版主. 
我在Storage Manager Control Panel Applet有看到了. 
非常感激
18楼 nanjianhui 2008-09-16 12:38发表 [回复] 

你可以在控制面板里面,看看有没有一个Storage的Manager,在里面应该可以看到,并且作mount。
17楼 lskymate 2008-09-12 19:28发表 [回复] 

版主您好. 
我發現,C:/WINCE600/PUBLIC/COMMON/OAK/DRIVERS/BLOCK/MSFLASHFMD/RAM 下有一個用 virtualalloc 的作法來產生2M的ram Flash. 

但我不知如何使用.所以把他拉出來 
放到DEVICEEMULATOR(BSP)下將他當成driver 
並BUILD成功.然後attach Emulator時.也可以順利 
看到自己在FMD.c加的資訊. 

但在EMULATOR上.並沒有看到關於這個 flash device .的相關資訊.請問我要如何像 linux 那樣 mount這個 
flash device 呢? 

非常感激
16楼 eagle1597 2008-09-10 16:33发表 [回复] 

1: "因为在WinCE6.0中限制了IOControl的访问" 
这个我不了解, 我的确只在5.0中做过. 谢谢你的指出. 

2: 如果你把srctor留出不给ce管理, 当然也就没有必要写额外信息了.
15楼 nanjianhui 2008-09-10 12:53发表 [回复] 

你的方法可行,但是有个问题,在WinCE5.0下可以,但是在WinCE6.0下就不行了。因为在WinCE6.0中限制了IOControl的访问,只有有限的几个case是可以被应用层访问的,所以你的方法就不适用了,关于这个问题,我以前写过一篇blog叫“WinCE BSP中d的OEMIoControl介绍”,你可以看一下。你要是非要通过IOControl来访问,你就需要改public目录下的代码,这样好像不太好啊,你怎么release BSP给你的客户呢? 
关于你说的要往Sector的额外信息写数据的问题,我认为没有什么必要,这块区域应该不属于WinCE可见的磁盘区域,所以你在FMD_GetInfo中,设置总共有多少个block的时候,把这块区域预留出来就可以了。
14楼 eagle1597 2008-09-10 10:40发表 [回复] 

我来发表看法吧. 

楼主的做法是可行的, 我也提供另一种做法, 那就是在oal层增加ioctl. 

大家知道, 在ioctl_tab.h 中有KernelIOControl的一些OEM增加的额外IOCTL, 我们完全可以在这里增加一个例如IOCTL_HAL_READFLASE等的Oal函数, 在oal层中, 诸如调用FMD_ReadSecotr等是很方便的. 增加后, 应用层就可以通过KernelIOControl来这行flash的读写了. 

还应该指出的是, 在对Flash的某个sector进行物理写后, 还应该对该sector的额外信息块写一定的数据以指示该flash存在数据,以至于不会被fal层把这个sector当作空闲而重写该sector. 

Sector后的sector_info结构如下: 

typedef struct _SectorMappingInfo 

SECTOR_ADDR logicalSectorAddr; 
BYTE bOEMReserved; // For use by OEM 
BYTE bBadBlock; // Indicates if block is BAD 
WORD fDataStatus; 

} SectorMappingInfo, *PSectorMappingInfo; 

而当我们对这个sector进行写后(不通过fal层),我们应该把上面结构体中的fDataStatus置一定的值, 以下值供参考: 
#define FREE_SECTOR 0xFFFF // Indicates sector is free (erased) 
#define DIRTY_SECTOR 0x0001 // Indicates sector is ready to erase 
#define SECTOR_WRITE_IN_PROGRESS 0x0002 // Indicates sector write is in progress 
#define SECTOR_WRITE_COMPLETED 0x0004 // Indicates sector write completed (data is valid) 
#define COMPACTION_IN_PROGRESS 0x0008 // Indicates previous block is being compacted 
#define COMPACTION_COMPLETED 0x0010 // Indicates previous block compaction completed 
#define SECURE_WIPE_IN_
13楼 nanjianhui 2008-09-04 09:34发表 [回复] 

在仿真的环境下,你最好在你的BSP中重写一个NandFlash的驱动,但是在实现驱动的FMD层中的函数的时候,你可以不对硬件操作,只对一块内存进行操作。也就是相当于一块RamDisk,但是使用NandFlash驱动来实现的。这样应该是可行的。
12楼 lskymate 2008-09-03 05:05发表 [回复] 

看了版主這篇文章.真是收穫良多. 

真的十分抱歉.在這裡很唐突的請教版主ㄧ些問題 
如果版主方便的話.不知可否為小弟解惑.. 
非常感激. 

小弟目前使用Wince 6.0 
想在Emulator上.模擬Flash裝置(沒有板子..) 
目前使用的BSP是DEVICEEMULATOR 

而另外勾選的Catalog Item有 
Binary Rom Image file System, 
FAT File System 
Storage Manager Control Panel Applet 
Registry Storage-RAM-based Registry 
RAM and ROM File System 
Storage Device(全部選項). 
PCI NAND Flash Driver. 

在Build時成功.但attach target時會停住.. 
出現訊息如下. 
4294784275 PID:400002 TID:10e0002 DEVICE!RegReadActivationValues RegQueryValueEx(Notify/BusPrefix) returned 2 

4294784289 PID:400002 TID:1070002 Unknown: DEBUGCHK failed in file C:/ymzki/private/winceos/DRIVERS/msflash/src/./falmain.cpp at line 1409 

想請問版主.Emulator下如何模擬像flash 這類的block driver.呢? 

非常非常感激. 
詩凱上
11楼 nanjianhui 2008-08-28 10:09发表 [回复] 

你可以开一个Buffer,每次调用FMD_ReadSector的时候,把数据读到Buffer中,然后把该Bufffer的首地址传回,就可以了。
10楼 nanjianhui 2008-08-28 10:03发表 [回复] 

你可以开一个Buffer,每次调用FMD_ReadSector的时候,把数据读到Buffer中,然后把该Bufffer的首地址传回,就可以了。
9楼 nanjianhui 2008-08-28 10:02发表 [回复] 

你可以开一个Buffer,每次调用FMD_ReadSector的时候,把数据读到Buffer中,然后把该Bufffer的首地址传回,就可以了。
8楼 redeg 2008-08-27 10:35发表 [回复] 

楼主,在调用FMD_ReadSector时如何逐一读取每块数据?该如何传递参数,我是新手,还希望楼主能解答.谢谢.
7楼 nanjianhui 2008-08-19 09:37发表 [回复] 

楼上的,没错你是可以得到。问题是你在什么地方得到呢?是在应用程序中得到么,那你应该是得到了驱动函数的指针,你能在应用程序中直接调用么? 
我没有试过,但是我认为不行。如果你实现了,请你写出具体步骤,谢谢。
6楼 slj0998 2008-08-07 18:09发表 [回复] 

IOCTL_FMD_GET_INTERFACE: 
用这个不就可以直接得到 
PFMDInterface pInterface 

读写直接用 
pInterface-&gt;pReadSector = FMD_ReadSector;pInterface-&gt;pWriteSector = FMD_WriteSector; 
难道不行吗?
5楼 nanjianhui 2008-08-06 12:32发表 [回复] 

NandFlash驱动在WinCE6.0下是运行在内核模式的,所以应用程序想要直接调用内核下的驱动的API因该是不可能的。如果楼上有什么好的方法,并且已经实现了,欢迎指点。
4楼 freasy 2008-08-05 21:17发表 [回复] 

只要暴露的足够多,应用程序什么事都可以做了
3楼 freasy 2008-08-05 21:15发表 [回复] 

只要暴露的足够多,应用程序什么事都可以做了
2楼 nanjianhui 2008-07-30 17:54发表 [回复] 

呵呵,你的方法在WinCE6.0上可行么??
1楼 guopeixin 2008-07-27 15:49发表 [回复] 

1. 首先要说,你这样的方法很是巧妙,但是要修改驱动的代码,这样可能会引起一些未知的问题. 
2. 你可以直接将fmd层的lib连接到ap中,然后直接在ap中对lib暴露出的接口对flash进行操作就可以了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值