//=====================================================================
//TITLE:
// TCC89x的NAND隐藏区域的设置和读写
//AUTHOR:
// norains
//DATE:
// Wednesday 03-November-2010
//Environment:
// Windows CE 6.0
// Telechips TCC89x
//=====================================================================
在默认的情况下,TCC89x使用一个NAND的隐藏分区存储TCBOOT和NK,另一个隐藏分区存储开机logo,还有一个NAND Data分区用来在系统启动后存储相应的数据。除此以外,经过设置以后,还能多出两个隐藏分区,以供用户使用。不过,这个两个隐藏分区的设置,就没有那么直观了。不过,没关系,跟着我,一步一步来吧。
首先,要在系统中对这两个分区进行设置,后续应用程序才能对其进行操作。打开Magellan/Src/LIB/SOC/NAND_DRV/nand_drv_v7.c文件,找到TNFTL_EXTENDED_PARTITION_NUM宏。其默认的数值是2。这个刚好是和文章开头所对应,分别是用来存储系统和开机logo。现在我们想多出两个隐藏分区,那么在这里就将TNFTL_EXTENDED_PARTITION_NUM更改为4。这里有个很有意思的地方,这个隐藏分区的最多可以设置多少个?其实最多的设置个数,已经定义于TNFTL_EXTENDED_PARTITION_MAX_NUM宏,其值刚好为4。
采用默认设置的话,这两个隐藏分区的大小分别是2MB和3MB。如果对这个容量不满意的话,自然也是有办法进行更改的。修改方法很简单,变更NAND_HIDDEN_2_PAGESIZE和NAND_HIDDEN_3_PAGESIZE这两个宏定义的数值即可。
设置完毕之后,剩下的事情就是点击Sysgen编译系统。编译完毕,打开烧录软件,载入刚刚编译好的系统,启动机器开始烧录,就能发现烧录软件已经能够识别这两个隐藏分区了,如图所示:
接下来就是应用程序的事情了。关于这两个隐藏分区的读写,其实可以参考BSP下的TCCDIAGNOSTIC程序。不过该程序掺杂了一些控件的属性,以致于对隐藏分区的操作不是很直观,所以在这里稍微理顺一下。
首先明确一点的是,NAND的读写,是以PAGE为单位的。在TCC89x里,一页的大小是512Byte,这个可以查看NAND_PAGE_SIZE就能看出来。也就是说,即使你写入的数据是600Byte,因为刚好大于一个PAGE,所以实际写入的容量是512 x 2 = 1024Byte。还有一点需要注意的是,一个PAGE为一个Sector,而读写的位置是以Sector为起点的,比如Sector 0,Sector 1等等。
我们先来看看如何对隐藏进行读取操作。对任何一个设备进行操作,首要的是通过CreateFile进行设备的打开,对于隐藏分区也是如此。所以,首先是打开设备,如:
hNand = CreateFile(L"DSK1:", GENERIC_READ|GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, NULL);
因为Windows CE没有规定对隐藏分区的操作函数,所以在成功打开设备之后,我们接下来就需要调用DeviceIoConrtol进行操作。调用该函数,自然少不了形参,对于隐藏分区而言,应该传入一个MULTIHIDDENINFO结构体。该结构体的声明如下:
typedef struct _MULTI_HIDDENINFO_{ unsigned int DriveNum; unsigned int HiddenAddress; unsigned int SectorNum; unsigned char* DataBuffer; }MULTIHIDDENINFO;
DriveNum是隐藏分区的序号。因为使用DISK_MULTI_HD_WRITE或DISK_MULTI_HD_READ标志是无法对存储系统的隐藏分区进行操作的,所以当其为0时,操作的其实是Boot Logo这个隐藏分区。由此可以知道,该值的范围只能是0~2。
HiddenAddress是开始的Sector地址。如果是从最初读取,那么直接赋值为0。如果不是,则可以是1,2,3等等。上限的数值,以分区的最大值来界定。
SectorNum代表的是一次性读取的Sector数,而DataBuffer是存储数据的缓存。可以这么说,DataBuffer的容量至少应该大于或等于SectorNum * NAND_PAGE_SIZE。
以一个最简单的例子做一下赋值的说明:
MULTIHIDDENINFO MHDInfo; MHDInfo.DriveNum = 0; MHDInfo.SectorNum = 1; MHDInfo.DataBuffer = &vtData[0]; MHDInfo.HiddenAddress = 0;
赋值完毕之后,就可以传入DISK_MULTI_HD_WRITE或DISK_MULTI_HD_READ进行读写了,如:
//进行隐藏分区的写操作 ::DeviceIoControl(hNand, DISK_MULTI_HD_WRITE, &MHDInfo, sizeof(MHDInfo), &dwDummy, sizeof(dwDummy), &dwReturn, NULL); //进行隐藏分区的读操作 ::DeviceIoControl(hNand, DISK_MULTI_HD_READ, NULL, NULL, &MHDInfo, sizeof(MHDInfo), &dwReturn, NULL)
当操作完毕之后,还要记得使用CloseHandle对设备进行关闭,如:
CloseHandle(hNand);