Nucleus的flash media管理FMM
迁移自http://adaishu.blog.163.com/blog/static/17583128620114382015423/
1.FMM (Flash Media Manage)
FMM是将Flash Memory 设备作为Nucleus Plus文件系统的存储介质,快速,简单,安全性好。并有如下特点:
l 自动标注设备域上的OS 段;
l 跟踪设备经历过的读/写次数;
l 掉电恢复;
l 辨认使用的块的好坏;
2.所需支持:
Nucleus Plus;
Nucleus Plus文件系统;
FMM模块;
3.FMM模块:
1. 文件:
dev_intf.c: 定义了设备接口函数,AMD和Intel类型的FLASH,8,16,32位的命令字等。
fmm.c :FMM的特殊函数,包括formatflash,clrdirtysec,Register_FMM等可应用的函数,但与文件系统无关。
fmmflash.c:FMM的控制函数,主要是FlashInit ,FlashIoct,FlashIo,FlashOpen,用于FLASH设备还没有登记为FMM之前。
os_intf.c:与操作系统的接口;
flsutil.c:FMM在文件系统上的应用,如fmm_dos_format,finfo,fmemtst,clrdsec等,还有一文件测试程序;
dev_intf.h
fmm.h
flsutil.h
devtable.c:置于文件系统的目录下,作为FLASH设备到文件系统的接口。
2. 几个重要的数据结构:
几个定义:
sector:由FMM定义,隐含长度定义为512 + 4字节,其中512是内容,4是一个32位的字,对sector标记,编号和状态(INUSE或FREE或DIRTY等等);
block:为实际的设备的段,如AMD29F040,总长512K字节,分8个段,每个段长64K。
driveno:为A,B,C,在此,FALSH定义为“C:”,A是0,则C为2;
在FMM中,有三个贯穿全局的数据结构:
format_info:
flag: FMM_INUSE标记
reg_fmm_t dev_char:见 fmm_reg;
numsect: 要读写的sec的数量;
nextsect:
numfreesect:设备中空闲段的数量,执行读写时系统会重新计算;
numdirtysect:dirtysect是一些垃圾数据的段
numbadsect:设备中损坏段的数量,执行读写时系统会重新计算;
num_id_sect:每8个sector就要用一个字节来标记是否是bad形式,计算方式是:
总共需要的id_sect数 / INT_FMM_SECTOR – sizeof(id_hdr_t)
*sec_map_ptr:sector translation table
*id_sect: bad sector table
*swap_area: swap for data during erase
fmm_smid: fmm 中的semaphore id,相当于semaphore中返回的指针;
erasecount: 擦除记数,每执行一次加1;
eraseblks: 擦除的block的总长度(单位:字节);
secperblk: 每个擦除的block的sector数;
secperwin: sectors in window
nextersblk: 下一个擦除的块(第1,2……块);
formflash:
mode: 是否为MS-DOS格式;
sectors:返回值;
flag: user string 是否有效;
erase_count:此前FLASH擦除的次数;
*user_string:格式化时写在第一段的一个字符串;
fmm_reg:
socket: 因为是onboardflash,定义为0x80;
handle: PCMCIA卡的用户指针;
size: FLASH的总长度;
read_blk 、write_blk:定义数据位,1->8位;2->16位……;
erase_blk:擦除时每个BLK的长度;(单位:字节)
dev_size: 每片FLASH的长度,一片时同size;
flash_start:FLASH的起始地址;
flash_offset:
flash_size: 同size;
secperwrite: 每次写的SEC数量最大值,定义它以保证不会超出buffer的大小。
reclaimcount:
vppctrl vpp_control:
ERWfunc ERWflash:操作的命令,读写等等;
mapaddr map_addr:将虚地址转换为实地址;
3. 应用:
在FMM的应用时,应当先调用以下几个函数:
FlashInit(dummy): 硬件信息的初始化;
pc_memory_init(): 在调用任何文件系统的程序之前都必须调用该函数,它完成的是文件系统所需的分配表;
NU_Become_File_User():在多任务系统中,必须在任务里调用该程序,才能使用API; 调用该函数就是将该任务登记为RTFS的用户。
此外,要注意将FLASH设置为C:驱动器,要在C:下完成对文件的操作,就必须先调用NU_Open_Disk(“C:”)和NU_Set_Default_Drive( 2 )。
FMM的接口:
1.与硬件的接口:在dev_intf.c 定义;
2.与file的接口:devtable.c中,定义FLASH为一个设备;
3.与操作系统的接口:os_intf.c中。
FMM的API (flsutil.c)
ffms :格式化DISK,之前应当使用关闭该打开的DISK;
finfo :显示FMM驱动器的统计信息;
sdelete :删除那些预定义的sector;
clrdsec :清除那些被标记为dirty的sector,使之被释放;
fmemtst :查看存储器是否有损坏,执行它时,设备会被格式化;
cmp :比较两个文件;
根据flsutil.c中的API,以及demo子目录下的nu_shell.c,定制了一组API:
1.在dskutil.c中:
ll_format,完成的是低级的格式化,要将FLASH全部擦除一遍
dos_format,完成了高级格式化。
2.在ftest中
writefile :写一个文件到FLASH,输入参数是文件索引;调用的是文件系统中的NU_Write,引入的参数是文件描述指针,源文件的buffer起始地,以及写的长度;
verifyfile :完成写文件后的校验,输入参数是文件索引;
deletefile :完成文件的删除,输入参数是文件索引;调用文件系统中的NU_Delete,引入的参数仅仅是文件名;
cmp :比较两个文件,输入的参数是文件名;
3.在dtest中:
fn_mkdir :创建子目录,输入参数是子目录的名称;
fn_cd : 进入子目录,输入参数是子目录的名称;
fn_pwd :显示当前路径,定义了一个cur_dir存储该信息;
fn_ls :显示当前路径下的子目录或文件,调用了文件系统中的NU_Get_First,其 中的 参数path可定义为“*.*”或其它字符。
4. FMM调试问题记录
1.在某些文件中类型定义错误:例如Fmm.c中:
rc == ERR_NOSEM,
rc = fmm_create_sem(&name[0],1,SM_FIFO,&fmm_info[unit].fmm_smid);等等;
rc定义为ULONG,但在引用时,又与负数相比较。
2.在FlashInit函数中,涉及到samphore,因此不能在Application_Initialize中调用,而要在任务中调用该函数,并且之前应清掉:sm_id = 0x53454d41(该值在创建semphore时所赋值),所采用方式是在之前清掉first_available_memory之后的内存区;
如果FlashInit完成的不好,formatflash中会出现FMM_NOTINUSE的错误。
3.在PCDISK.H中,
/* ============= ATI DRIVERS =============== */
/* Set the following line to 1 if you purchased the IDE driver */
#define EBS_IDE 0
/* Set the following line to 1 if you purchased the FLOPPY driver */
#define EBS_FLOPPY 0
/* Set the following line to 1 if you purchased the RAMDISK driver */
#define RAMDISK 0
要添加:
#define INC_TRI_FMM 1
4.文件系统中所引用的文件名和目录名都是大写的,因此如果输入是小写,应当做一些转换,其中str2upper就是为此而调用的。
5.对每个sector前的一个32位的字,后8位在formsecmap中定义,包括FMM_SECTOR_FREE 0xf0000000
FMM_SECTOR_DVALID 0xe0000000
FMM_SECTOR_INUSE 0xc0000000
FMM_SECTOR_DIRTY 0x80000000
如FMM_SECTOR_DIRTY,则为0x80;
前面8位从0到0xff记数。
6.对Dirty sector有一个清除的命令,只有在一个BLOCK中被Dirty sector填满后才能执行;
7.在file system中,一些系统调用NU_*函数,都有相应的po??_*内部函数,在1.0版本中,没有NU_Truncate只有PO?_,将它改为NU?_。但要注意这两类函数的返回值正好相反。