yaffs2应用到较老版本linux上时的问题 (移植日记)

来源:互联网 发布:什么是风险软件 编辑:程序博客网 时间:2024/05/29 03:21
 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
原创粉丝点击