开始阅读YAFFS源码 2.6.18以上需要打补丁 yaffs2 内核 kernel的ecc布局

开始阅读YAFFS2源码,有不少收获。

初步明白为什么在新内核上原来的yaffs会有问题,让我突然觉得那些

拿了别人可以烧写yaffs的uboot 又问linux 2.6.22怎么出问题的那些人挺可笑的,其实我自己一开始也这样,算是自嘲了。感觉纯粹的就是拿来,不去自己分析其中的问题,这样学不到东西。

这两天我对原来uboot烧写yaffs的功能作了深入分析,因为补丁其实很短小,反复看看就明白了。再加上对新的yaffs2代码做了初步阅读, 不能说完全明白,但已经找到了问题所在。事实上,yaffs2的代码目前用在linux 2.6.23.9上已经和原来我用在2.6.20.3上的不一样了!

先来说说YAFFS1的NAND数据组织。

K9F1208 这块FLASH是 512字节+16字节 1个page。512字节用来存放数据,16字节oob。

MTD中,用了8字节做ECC,这8个字节是16字节中的低8字节,详细阅读linux源码中drivers/mtd/nand/nand_base.c中

static struct nand_ecclayout nand_oob_16 = {
 .eccbytes = 6,
 .eccpos = {0, 1, 2, 3, 6, 7},
 .oobfree = {
  {.offset = 8,
   . length = 8}}
};

 

//这个oob layout是在xxx_nand_probe->nand_scan->nand_scan_tail()调用的.

 

可见,可用的freeoob是高8字节。

原来的yaffs中使用nandmtd_WriteChunkToNAND函数将它的chunk写入FLASH,我开始不关心他的512字节数据区,关键还是分析它的oob。原本的yaffs中,他有个yaffs_Spare结构,在yaffs_guts.h中定义的

typedef struct {
 __u8 tagByte0;
 __u8 tagByte1;
 __u8 tagByte2;
 __u8 tagByte3;
 __u8 pageStatus; /* set to 0 to delete the chunk */
 __u8 blockStatus;
 __u8 tagByte4;
 __u8 tagByte5;
 __u8 ecc1[3];
 __u8 tagByte6;
 __u8 tagByte7;
 __u8 ecc2[3];
} yaffs_Spare;

 

正好是16字节。 所以如果在用MTD_ECC的,必然需要一个转化,因为你需要把低8字节的oob让出来给MTD的ECC,只用高8字节的oob, 这个功能是由translate_spare2oob函数完成的。读出来时的时候正好反过来translate_oob2spare。实际YAFFS在调 用 mtd->write_oob的时候只写了8字节,由上很容易推测这是高8字节的oob,低8字节由mtd去计算ECC了。

再来看mkyaffsimage的代码,他在512字节之后是包含了16字节oob的,但是从代码中发现,这个16字节的oob就是他的 yaffs_Spare结构,也就是说你用MTD_ECC的话,这个16字节是不适合NAND 的oob结构的,所以需要translate_spare2oob的做法来转换。到这里,就明白为什么uboot补丁中

#ifdef CFG_NAND_YAFFS1_NEW_OOB_LAYOUT
    /* translate OOB for yaffs1 on Linux 2.6.18+ */
    oob_buf[15] = oob_buf[12];
    oob_buf[14] = oob_buf[11];
    oob_buf[13] = (oob_buf[7] & 0x3f)
    | (oob_buf[5] == 'Y' ? 0 : 0x80)
    | (oob_buf[4] == 0 ? 0 : 0x40);
    oob_buf[12] = oob_buf[6];
    oob_buf[11] = oob_buf[3];
    oob_buf[10] = oob_buf[2];
    oob_buf[9]  = oob_buf[1];
    oob_buf[8]  = oob_buf[0];
    memset(oob_buf, 0xff, 8);

这段代码就是在调整oob,实际上就是translate_spare2oob功能,将低8字节填充0xFF这样MTD会自己计算ECC,高8字节转化成有用的信息。

到这里就对上了,uboot中烧写的yaffs就能和linux中的yaffs对上。

现在出了什么问题呢?!现在的yaffs2代码中,用高版本内核的话,已经不是调用nandmtd_WriteChunkToNAND这个函数了!!参看yaffs_fs.c。

#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
  /* use the MTD interface in yaffs_mtdif1.c */
  dev->writeChunkWithTagsToNAND =
   nandmtd1_WriteChunkWithTagsToNAND;
  dev->readChunkWithTagsFromNAND =
   nandmtd1_ReadChunkWithTagsFromNAND;
  dev->markNANDBlockBad = nandmtd1_MarkNANDBlockBad;
  dev->queryNANDBlock = nandmtd1_QueryNANDBlock;
#else
  dev->writeChunkToNAND = nandmtd_WriteChunkToNAND;
  dev->readChunkFromNAND = nandmtd_ReadChunkFromNAND;
#endif

再看yaffs_nand.c

if (dev->readChunkWithTagsFromNAND)
  result = dev->readChunkWithTagsFromNAND(dev, realignedChunkInNAND, buffer,
            tags);
 else
  result = yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev,
         realignedChunkInNAND,
         buffer,
         tags); 

 

可见linux.2.6.17以上就调用的是 nandmtd1_WriteChunkWithTagsToNAND这个函数,而低版本的通过 yaffs_TagsCompatabilityReadChunkWithTagsFromNAND来调用 nandmtd_WriteChunkToNAND。

现在的这个函数用的不是yaffs_spare结构,而是

typedef struct {
 unsigned chunkId:20;
 unsigned serialNumber:2;
 unsigned byteCount:10;
 unsigned objectId:18;
 unsigned ecc:12;
 unsigned deleted:1;
 unsigned unusedStuff:1;
 unsigned shouldBeFF;

} yaffs_PackedTags1;

这个结构。

都是靠yaffs_ExtendedTags转过去的。相应的mkyaffsimage也没有对应上!!

今天就分析到这里,相信能给广大的开发人员指明一个方向。还是建议阅读阅读代码,自己分析,实际上并不是很复杂。

 只需要修改mkyaffsimage代码和uboot相应代码就能解决问题了。

(转载自:http://www.armchina.cn 作者hiboy)

你可能感兴趣的:(linux,struct,layout,Flash,buffer,interface)