2009.7.21
yaffs2 在 2.6.22的 kernel 上表现很正常,但在老的 2.6.12 上,则出现问题。
无奈之下只好阅读 yaffs2 的代码
fs/yaffs2/yaffs_mtdif2.c 中的 nandmtd2_ReadChunkWithTagsFromNAND 函数从nand flash 中
读取 oob 信息, 获取 yaffs_ExtendedTags tags,
if (tags){
memcpy(&pt, dev->spareBuffer, sizeof(pt));
yaffs_UnpackTags2(tags, &pt);
if(pt.t.chunkId != -1)
{
printk("yaffs_UnpackTags2 chunkId = %x addr = %x/n", pt.t.chunkId, addr);
int i = 0;
for(; i<28; i++)
{
printk(" %02x " , dev->spareBuffer[i]);
}
printk("/n");
}
}
通过和 2.6.22 的kernel 对比,发现数据偏移了两位, 我就太阳。。
后来找到下面这篇文章:
新版本内核中的MTD驱动考虑到了与yaffs2的接口问题,与yaffs2的整合一般都很顺利。但是老版本就容易出现写进去的文件umount/mount后丢失的问题。基本上应该属于oob中数据布局的问题。MTD中的oob(2k-page)布局:
====================
字节0: 坏块标记
字节1: 保留
2-0x27: 给上层使用(yaffs)
0x28-0x3F: ECC
====================
YAFFS中与MTD最纠缠不清的就是oob中的数据存储。yaffs需要将tag存入MTD的oob区域,写入时通过write_ecc, 读取时会用到read_ecc和read_oob,后者在扫描文件系统的时候会用到,问题出在这里:
1. NAND驱动中read_ecc和write_ecc都会小心得将yaffs2传递过来的oob数据写到2-0x27的位置,
2. read_oob时,则是将整个oob从0开始都读取出来了。导致yaffs2取到的tag数据错误,这是造成文件丢失或者mout失败的罪魁祸首。
解决的方法很简单(只针对pagesize=2K的nand,其他的要参考driver/mtd/nand/nand_base.c中的nand_oobinfo,修改yaffs_mtdif2.c中的ReadChunkWithTagsFromNand,
1. 添加一个变量oobOffset,默认值为0, 当版本<=2.6.17并且调用read_oob时,oobOffset = 2.
2. 最后copy数据到tag结构体时,加上oobOffset即可。
参考文档:
http://www.linux-mtd.infradead.org/tech/mtdnand/x255.html
http://www.yaffs.net/yaffs-2-specification-and-development-notes
读的问题解决了,能正确读到数据了,但写数据的时候还是不正常。。
写入数据以后, 会导致文件丢失, 并产生:Partially written block 15 chunk 973 detected 警告。
其实根本原因还是写入的时候,写oob 信息有问题。明天继续查吧。
2009.7.22
问题终于解决了,现在我的 2.6.12 也能顺畅的使用 yaffs2 了。
问题的根源还是在读flash的 oob的时候出问题了,
2.6.12 的kernel 中 mtd->read_ecc 函数既读取 data 也读取 tags 但是读tags 的时候ecc错误,或者说这个函数在处理 512byte/page 的 nand flash 时工作正常,
但是在读 2k/page 的 flash 时,工作就不正常了。再看mtd->read 只读数据,工作OK, mtd->read_oob 只读oob 或者说只读 tags ,也正常,但是在memcpy数据的时候
需要加 2 的偏移就正常了。问题找到了,不使用 mtd->read_ecc 函数 就什么问题都没有了,如果既需要读数据,又需要读oob, 那么就分别调用 另外两个函数。代码:
int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
__u8 * data, yaffs_ExtendedTags * tags)
{
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
#if (MTD_VERSION_CODE > MTD_VERSION(2,6,17))
struct mtd_oob_ops ops;
#endif
size_t dummy;
int retval = 0;
int localData = 0;
int tag_off = 0;
int tag_mod = 0;
loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
yaffs_PackedTags2 pt;
memset((char*)&pt, 0, sizeof(pt) );
T(YAFFS_TRACE_MTD,
(TSTR
("nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p"
TENDSTR), chunkInNAND, data, tags));
if(dev->inbandTags){
if(!data) {
localData = 1;
data = yaffs_GetTempBuffer(dev,__LINE__);
}
}
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
if (dev->inbandTags || (data && !tags))
retval = mtd->read(mtd, addr, dev->totalBytesPerChunk,
&dummy, data);
else if (tags) {
ops.mode = MTD_OOB_AUTO;
ops.ooblen = sizeof(pt);
ops.len = data ? dev->nDataBytesPerChunk : sizeof(pt);
ops.ooboffs = 0;
ops.datbuf = data;
ops.oobbuf = dev->spareBuffer;
retval = mtd->read_oob(mtd, addr, &ops);
}
#else
tag_off = 2;
#if 0
if (!dev->inbandTags && data && tags) {
retval = mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
&dummy, data, dev->spareBuffer,
NULL);
tag_mod = 1;
}
else
{
#endif
if (data)
retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy,
data);
if (!dev->inbandTags && tags)
{
retval =
mtd->read_oob(mtd, addr, mtd->oobsize, &dummy,
dev->spareBuffer);
tag_mod = 2;
}
#endif
if (!dev->inbandTags && data && tags)
{
tag_mod = 1;
}
if(dev->inbandTags){
if(tags){
yaffs_PackedTags2TagsPart * pt2tp;
pt2tp = (yaffs_PackedTags2TagsPart *)&data[dev->nDataBytesPerChunk+tag_off];
yaffs_UnpackTags2TagsPart(tags,pt2tp);
}
}
else {
if (tags){
memcpy(&pt, dev->spareBuffer + tag_off, sizeof(pt));
yaffs_UnpackTags2(tags, &pt);
}
}
if(localData)
yaffs_ReleaseTempBuffer(dev,data,__LINE__);
if(tags && retval == -EBADMSG && tags->eccResult == YAFFS_ECC_RESULT_NO_ERROR)
{
tags->eccResult = YAFFS_ECC_RESULT_UNFIXED;
}
if (retval == 0)
return YAFFS_OK;
else
return YAFFS_FAIL;
}
参考文档:
如何编写linux下nand flash驱动
http://blog.ednchina.com/edaworld/140765/Message.aspx
Yaffs文件系统结构
http://www.360doc.com/content/070110/14/198_325124.html