📄 flashfile.c
字号:
//----------------------------------------------------
//Copyright (C), 2004-2009, lst.
//版权所有 (C), 2004-2009, lst.
//所属模块:flash文件系统
//作者:lst
//版本:V1.0.0
//文件描述:flash文件系统驱动主模块
//其他说明:
//修订历史:
// 2. ...
// 1. 日期:
// 作者:
// 新版本号:
// 修改说明:
//------------------------------------------------------
/*
各缩写全称:
MDR memory medium description record 存储介质(芯片)描述记录
CDR chip description record MDR中芯片描述部分
DBX deedbox 文件柜,也指MDR中文件柜描述部分
DDR deedbox description record 文件柜描述记录
DBL DDR_mirror blocks list 存储DDR信息的块号列表
ARB abrasion record base 磨损记录基数
ART abrasion record table 磨损记录表
FDSB file description starting block 文件描述表首块块号,基于本文件柜
FDT file description table 文件描述表
MAT memory allocation table 存储器分配表
PCRB power cut restore block 掉电恢复块
特注:MDR表不做整体ECC校验,否则每次滚动修改都需要改ECC码。备份MDR表要做ECC校验。
A、MDR表:文件系统本身不使用,由模块初始化(module_init_)函数读取,其内容包括
1、CDR,芯片初始化标志(8字节=djyosfs),flash芯片总块数(包括坏块),块尺寸,
MDR_bak块号,文件柜总数,占用512字节,其中文件柜总数循环写入,本来设计要
循环记录可用块数的,但因为可用块数是动态性强,记录也没有多大实用意义,还
容易造成MDR表磨损,故用动态产生的方法。
2、1号文件柜DBX,格式化标志(8字节=ready/unready),文件柜名(32字节),起始块号,
占用块数,本掉电恢复块的块号,每交换1024次改变一次。2个DDR表链首块块号
(每使用1024次改一次)。本部分占用1024字节,循环写入以延长寿命。
3、2号文件柜DBX,........
4、一个芯片上可建立的文件柜数量默认上限是3,但可由芯片管理工具修改。
flash存储器,不管是nand还是nor型,总是使用头两个可用块存储,第一块存储主MDR,
第二块存储备份MDR,采用某些机制保证主MDR的写入次数少,擦除次数更少,只有当
擦除主MDR时,才使用备份MDR。读出时,先读主MDR,若有错误再读备份MDR。这样做
可以保证:
1、保证主MDR不会过度磨损。
2、当且仅当写主MDR时恰逢掉电才需要从备份中
3、备份MDR表的擦写次数更少。
B、DDR,以存储器说明中DDR表首块号为基准,
1、DBL,DDR表本身占据的块号列表,每项长度=2或4,主表和备份表个一
2、FDSB文件描述表首块块号,长度=sizeof(mat_size_t),每修改1000次改一次。
3、ARB磨损次数基数,4字节,记录磨损次数的底数,4字节对齐
4、ART磨损次数表,每块占用2字节,加上基数即得该块实际磨损次数。自动对齐
5、MAT存储器分配表:每项8字节,保存前一块和后一块的块号,
首块的前一块保存目录项编号,末块的后一块指向自己,把每个文件串成一个表。
坏块:前后指针相等但不等于本块块号,而是本块块号加1
空闲块:前后指针相等,等于本块块号加2
DDR_main块:前后指针相等,等于本块块号加3
DDR_bak块:前后指针相等,等于本块块号加4
掉电恢复块:前后指针相等,等于本块块号加5
特注: 两份DDR表的DBL部分不相同,其他部分完全相同
文件柜说明(DDR)占用的空间计算如下:
假设需占用x字节,y块,则有:y=(x+block_size-1)/block_size
DBL = 4*y(主表和备份表个一)
ARB = 4
ART = 2*block_sum
FDSB = 4
MAT = 4*2*block_sum,为防止一个MAT表项块块边界,MAT表的起始地址应该是4/8字节对齐的。
x = DBL + ARB + ART + FDSB + MAT+对齐损耗
把y的表达式代入上式,解方程得到:
x= (ARB+ART+FDSB+MAT)*block_size + (block_size -1)*nb_len/(block_size-nb_len)
x应该是一个介乎(n~n+4)之间的数,再加上对齐损耗即可。
E、目录信息表:每个文件/目录用40字节表示,目录表长度是动态的
改一个文件的一个块可能涉及到修改:
1、修改该块本身。
2、若长度生变或改首块,还要修改目录表。
3、改MAT表。
4、如果改目录表或MAT表时发生坏块更替,改A部分。
*/
#include "inc_os.h"
#include "flashfile.h"
#include "mdr.h"
#include "ddr.h"
#include "fdt.h"
#include <string.h>
//flash芯片的资源结点,所有flash芯片都安装在此结点下
static struct rsc_node tg_flash_chip_root_rsc;
//----卸载一个芯片------------------------------------------------------------
//功能: 从文件系统中删除该芯片,删除属于该芯片的所有文件柜,只有在该芯片的所有
// 文件柜都没有打开的文件的时候才可以删除
//参数: chip,待删除的芯片
//返回: 成功删除返回true,否则返回false---db,待完善
//----------------------------------------------------------------------------
bool_t DFFSD_uninstall_chip(struct flash_chip *chip)
{
return true;
}
//----格式化文件柜------------------------------------------------------------
//功能:格式化文件柜,该文件柜应该已经建立,并且经过启动初始化加入到设备链表中。
// A、未格式化的文件柜格式化过程:
// 1、计算存储DDR表所需的块数(2份)
// 2、扫描一遍坏块,在MAT表中标记坏块,并从好块中挑出2份DDR,
// 3、填写DDR中的DBL表、MAT表
// 4、ART表排序,并把DDR表写入flash中。
// 5、分配一块做FDT。
// 6、初始化FDT,然后写入flash中
// 7、更新MDR表,并写入flash中
// B、已经格式化的文件柜格式化操作
// 再说吧
//参数:cmd,格式化参数,暂不用
// DBX_device_tag,待格式化的文件柜,该文件柜已经安装到设备系统中
//返回:true=成功格式化,含原来就已经格式化好而无需再次格式化的。
// false=失败
//-----------------------------------------------------------------------------
bool_t DFFSD_format_DBX(uint32_t cmd,struct st_DBX_device_tag *DBX_device_tag)
{
struct st_DBX_flash_tag *DBX_flash_tag;
struct flash_chip *chip;
struct MAT_table *MAT_item;
uint32_t DDR_blocks,valid_blocks;
uint32_t loop;
if(DBX_device_tag == NULL)
return false;
DBX_flash_tag = (struct st_DBX_flash_tag *)
DBX_device_tag->DBX_medium_tag;
chip = DBX_flash_tag->chip;
//DDR表所占的块数
DDR_blocks=(chip->block_size+DBX_flash_tag->DDR_size-1)/chip->block_size;
if(DBX_device_tag->formatted) //重新格式化已经格式化的文件柜。以后再考虑吧
{
}else //格式化新文件柜。
{
DBX_flash_tag->DDR_ARB = 0; //磨损次数基数清零
MAT_item = DBX_flash_tag->DDR_MAT;
valid_blocks=0;
for(loop = 0; loop < DBX_flash_tag->block_sum; loop++)
{
MAT_item[loop].previous = loop;
MAT_item[loop].next = loop;
DBX_flash_tag->ART_times[loop] = 0;
DBX_flash_tag->ART_position[loop] = loop;
DBX_flash_tag->ART_block_no[loop] = loop;
if(chip->check_block(loop + DBX_flash_tag->start_block))
{
if(valid_blocks < DDR_blocks)
{//这些块分配给DDR_main表
if(valid_blocks == 0)
DBX_flash_tag->DDR_main=loop;
//填写DBL表
DBX_flash_tag->DDR_DBL[valid_blocks] = loop;
//设置第loop块是DDR_main块
MAT_item[loop].attr = cn_DDR_main_block;
}else if(valid_blocks < 2*DDR_blocks)
{//这些块分配给DDR_bak表
if(valid_blocks == DDR_blocks)
DBX_flash_tag->DDR_bak=loop;
//填写DBL表
DBX_flash_tag->DDR_DBL[valid_blocks] = loop;
//设置第loop块是DDR_bak块
MAT_item[loop].attr = cn_DDR_bak_block;
}else if(valid_blocks == 2*DDR_blocks)
{//本块分配给目录表,刚格式化的文件柜,目录表只有1块
//填写FDS(目录表首块块号)
DBX_flash_tag->DDR_FDSB = loop;
MAT_item[loop].attr = cn_FDT_block;
}else if(valid_blocks == 2*DDR_blocks+1)
{//文件柜掉电恢复块
//设置第loop块是掉电恢复块
MAT_item[loop].attr = cn_PCRB_block;
chip->erase_block(loop + DBX_flash_tag->start_block);
DBX_flash_tag->PCRB_no = loop;
}else
{//空闲块
//设置第loop块是空闲块
MAT_item[loop].attr = cn_free_block;
}
valid_blocks++; //找到的可用块数量增量
}else
{//坏块
//设置第i块是坏块
MAT_item[loop].attr = cn_invalid_block;
}
}
//总有效块数,接下来__DFFSD_write_DDR函数如果产生坏块,会修改valid_sum。
DBX_flash_tag->valid_sum = valid_blocks - 2*DDR_blocks -2;
__DFFSD_sort_ART(DBX_flash_tag);
for(loop = 0; loop<DDR_blocks; loop++) //刚格式化,DDR表当然还没有写入
{
DBX_flash_tag->writed_DDR_main[loop] = false;
DBX_flash_tag->writed_DDR_bak[loop] = false;
}
if( ! __DFFSD_write_DDR(DBX_flash_tag)) //写DDR表到flash
{
return false; //DDR表写入flash发生错误
}
//以下4行修改flash中的MDR表,MDR表是通过严格限制磨损次数和强力校验来保证
//正确的,即使在有瑕玷的块,也能保证正确,不会报错
__DFFSD_set_MDR_DBX_formatted(chip,DBX_flash_tag->DBX_no);
__DFFSD_write_MDR_item(DBX_flash_tag,cn_MDR_DDR_main,
DBX_flash_tag->DDR_main+DBX_flash_tag->start_block);
__DFFSD_write_MDR_item(DBX_flash_tag,cn_MDR_DDR_bak,
DBX_flash_tag->DDR_bak+DBX_flash_tag->start_block);
__DFFSD_write_MDR_item(DBX_flash_tag,cn_MDR_PCRB,
DBX_flash_tag->PCRB_no+DBX_flash_tag->start_block);
if( ! __DFFSD_init_FDT(DBX_flash_tag)) //写目录表
{
return false;
}
}
return true;
}
//----定位读(写)块------------------------------------------------------------
//功能: 确定读写位置的块号,块号是基于文件柜的相对块号,如果读写位置已经超出文件
// 已分配的块数,则返回cn_limit_uint32,
//参数: DBX_flash_tag,被操作的flash文件柜的存储媒体标签
// fp,文件指针,调用方需确保是文件而不是目录
// offset,读写位置偏移量
//返回: 块号
//----------------------------------------------------------------------------
uint32_t __DFFSD_locate_block(struct st_DBX_flash_tag *DBX_flash_tag,
struct file_rsc *fp,sint64_t offset)
{
uint32_t block_no,loop,next_block,block_offset,end_block;
struct flash_chip *chip;
struct fdt_info FDT_item;
//获取文件所属芯片
chip = DBX_flash_tag->chip;
//读出文件的目录项
__DFFSD_read_FDT_item(DBX_flash_tag,(uint32_t)fp->file_medium_tag,&FDT_item);
//从目录项中取文件首块块号
next_block = __pick_little_32bit(FDT_item.fstart_dson,0);
block_offset = offset % chip->block_size;
block_no = offset/chip->block_size; //计算读写位置处于文件内第几块
//计算文件结束位置在文件内的块号,
if(fp->file_size != 0)
end_block = (fp->file_size - 1) / chip->block_size;
else
end_block = 0;
if(block_no > end_block) //读写位置超出文件已分配的块
{
return cn_limit_uint32;
}
for(loop = 0; loop < block_no; loop++) //沿MAT表找到目标位置所在块
{
next_block = DBX_flash_tag->DDR_MAT[next_block].next;
}
return next_block;
}
//----定位最后一块-------------------------------------------------------------
//功能: 确定文件最后一块的块号
//参数: DBX_flash_tag,被操作的flash文件柜的存储媒体标签
// fp,文件指针,调用方需确保是文件而不是目录
//返回: 块号
//----------------------------------------------------------------------------
uint32_t __DFFSD_last_block(struct st_DBX_flash_tag *DBX_flash_tag,
struct file_rsc *fp)
{
uint32_t loop,next_block;
struct flash_chip *chip;
struct fdt_info FDT_item;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -