初步解决yaffs文件系统的nand flash烧写

1659阅读 0评论2012-06-13 hfeng1985
分类:

文件: yaffs.tar.gz
大小: 143KB
下载: 下载
 
 
初步解决yaffs文件系统的nand flash烧写
 
1)获取yaffs工具
可以到下载yaffs源码
解压后进入yaffs目录下的utils目录。
make
得到两个工具:mkyaffs mkyaffsimage
 
2)创建测试的yaffs image

mkdir yaffs

拷贝文件系统的所有文件到yaffs文件夹下

mkyaffsimage yaffs yaffs.img

 

3)写入nand flash

(注意这里使用到flash-eraseall,nandwrite等工具哦,再我的另一篇文章里有讲到mtd-utils工具,大家可以照着装上该工具)

flash-eraseall /dev/mtd2

nandwrite –a –o /dev/mtd2 yaffs.img

挂载yaffs mtd

mount –t yaffs /dev/mtdblock2 /mnt

发现mount报错,  只有一个lost+found目录, 其余空空如也. 看来只好分析yaffsnand flash代码了.

 

4)代码分析

我使用的是8bit 512bytes+16bytes oob/page 的128M nand flash, 分析yaffs与nand flash驱动代码, 发现yaffs中调用yaffs_mtdif.c中的nandmtd_WriteChunkToNAND函数将它的chunk写入FLASH,包含一个512字节的数据与yaffs_Spare结构, 512字节数据对应nand flash 一page, 所以不需要关心他的512字节数据区; 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字节, 那就是使用这16字节作为OOB. 其中ecc1与ecc2是用来计算ECC的, 只有使用yaffs自身的ECC时才用到, 我们这里使用mtd的硬件ECC, 可以忽略不计, 省下了YAFFS用来存放文件系统相关的信息(yaffs_Tags)8个bytes. 而mx27 nand flash 其 oob定义如下:

 

static struct nand_ecclayout nand_hw_eccoob_8 = {

    .eccbytes = 5,

    .eccpos = {6, 7, 8, 9, 10},

    .oobfree = {{0, 5}, {11, 5}}

};

 

Oobfree有两块, {0,5}, {11,5}总共10个字节. 需要将这8个字节保存到OOB区中, 就需要一个转换.  继续分析yaffs_mtdif.c时,发现2.6.19内核在yaffs写入oob时先使用translate_spare2oob将yaffs_Spare转换为一个8bytes数据块,然后通过mtd->write_oob使用MTD_OOB_AUTO方式写入oob数据;

#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))

    __u8 spareAsBytes[8]; /* OOB */

 

    //只有数据

    if (data && !spare)

        retval = mtd->write(mtd, addr, dev->nDataBytesPerChunk,

                &dummy, data);

else if (spare) {

        //使用nand 硬件ECC

        if (dev->useNANDECC) {

            //转换tag为8bytes数据块

            translate_spare2oob(spare, spareAsBytes);

            //使用MTD_OOB_AUTO方式将8bytes块写入到oobfree

            ops.mode = MTD_OOB_AUTO;

            ops.ooblen = 8; /* temp hack */

        } else {

            //使用yaffs自身ECC时, 直接将yaffs_Spare数据作为OOB

            ops.mode = MTD_OOB_RAW;

            ops.ooblen = YAFFS_BYTES_PER_SPARE;

        }

        ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen;

        ops.datbuf = (u8 *)data;

        ops.ooboffs = 0;

        ops.oobbuf = spareAsBytes;

        retval = mtd->write_oob(mtd, addr, &ops);

}

#endif

继续深入分析,  发现mtd-write_oob实际上是调用的是nand_do_write_ops或nand_do_write_oob(都在driver/mtd/nand/nand_base.c), 在这两个函数中在处理oob数据时都调用了同一个函数nand_fill_oob:

static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,

                  struct mtd_oob_ops *ops)

{

    size_t len = ops->ooblen;

 

    switch(ops->mode) {

 

    case MTD_OOB_PLACE:

    case MTD_OOB_RAW:

        memcpy(chip->oob_poi + ops->ooboffs, oob, len);

        return oob + len;

 

    case MTD_OOB_AUTO: {

        struct nand_oobfree *free = chip->ecc.layout->oobfree;

        uint32_t boffs = 0, woffs = ops->ooboffs;

        size_t bytes = 0;

 

        for(; free->length && len; free++, len -= bytes) {

            /* Write request not from offset 0 ? */

            if (unlikely(woffs)) {

                if (woffs >= free->length) {

                    woffs -= free->length;

                    continue;

                }

                boffs = free->offset + woffs;

                bytes = min_t(size_t, len,

                          (free->length - woffs));

                woffs = 0;

            } else {

                bytes = min_t(size_t, len, free->length);

                boffs = free->offset;

            }

            memcpy(chip->oob_poi + boffs, oob, bytes);

            oob += bytes;

        }

        return oob;

    }

    default:

        BUG();

    }

    return NULL;

}

可以看出nand_fill­_oob使用了2种方式来组织oob的处理方式: MTD_OOB_PLACE与MTD_OOB_RAW为一种, 直接将OOB数据复制到要写入oob的数据缓存chip->oob_poi; MTD_OOB_AUTO讲oob数据复制到要写入oob的数据缓存oobfree位置上. 这就是MTD_OOB_RAW与MTD_OOB_AUTO的最终解释了.

 

再来看mkyaffsimage的代码:

static int write_chunk(__u8 *data, __u32 objId, __u32 chunkId, __u32 nBytes)

{

    yaffs_Tags t;

    yaffs_Spare s;

   

    error = write(outFile,data,512);

    if(error < 0) return error;

 

    memset(&t,0xff,sizeof (yaffs_Tags));

    memset(&s,0xff,sizeof (yaffs_Spare));

 

    t.chunkId = chunkId;

    t.serialNumber = 0;

    t.byteCount = nBytes;

    t.objectId = objId;

 

    if (convert_endian)

    {

        little_to_big_endian(&t);

    }

 

   yaffs_CalcTagsECC(&t);

    yaffs_LoadTagsIntoSpare(&s,&t);

    yaffs_CalcECC(data,&s);

 

    nPages++;

    return write(outFile,&s,sizeof(yaffs_Spare));

}

他在512字节之后是包含了16字节yaffs_Spare的,这个16字节的yaffs_Spare就是他的oob结构. 但是这个16字节并没有通过translate_spare2oob转换, 而是直接写入image中了.

再看通过nandwrite -a -o 写入mtd时的代码

if (!noecc) {

                int i, start, len;

                /*

                 *  We use autoplacement and have the oobinfo with the autoplacement

                 * information from the kernel available

                 *

                 * Modified to support out of order oobfree segments,

                 * such as the layout used by diskonchip.c

                 */

                if (!oobinfochanged && (old_oobinfo.useecc ==   MTD_NANDECC_AUTOPLACE)) {

                    for (i = 0;old_oobinfo.oobfree[i][1]; i++) {

                        /* Set the reserved bytes to 0xff */

                        start = old_oobinfo.oobfree[i][0];

                        len = old_oobinfo.oobfree[i][1];

                        printf( "oob:[%d:%d]\n", start, len );

                        memcpy(oobbuf + start,

                            oobreadbuf + start,

                            len);

                    }

                } else {

                    /* jffs2 or yaffs */

                    /* Set at least the ecc byte positions to 0xff */

                    start = old_oobinfo.eccbytes;

                    len = meminfo.oobsize - start;

 

                    memcpy(oobbuf + start,

                        oobreadbuf + start,

                        len);

                }

            }

可见nandwrite在写入oob时是也是通过MTD_NANDECC_AUTOPLACE(等同MTD_OOB_AUTO)方式写入的.

 

比较一下yaffs流程与mkyaffsimage流程:

yaffs流程是通过translate_spare2oob将8bytes的yaffs_tags转为8bytes数据块,然后通过write_oob将这8bytes写入到OOB的oobfree块区; 读出来的时候反过来translate_oob2spare, 就可以还原成yaffs_tags; 而mkyaffsimage创建yaffs image时却是直接将yaffs_Spare写入文件, 通过nandwrite -a -o 写入mtd时, 直接使用这块yaffs_Spare作为oob数据写入, 虽然使用方式也是MTD_OOB_AUTO; 这就造成yaffs读取chunk时无法读取正确的yaffs_Spare数据了;

 

由此可见只要在mkyaffsimage写入yaffs_Spare时, 只要将写入的数据转换为yaffs中写入flash之前一致的数据即可.

 

5)修改代码

以下是修改过的 write_chunk

static int write_chunk(__u8 *data, __u32 objId, __u32 chunkId, __u32 nBytes)

{

    yaffs_Tags t;

    yaffs_Spare s;

    __u8 oobdata[16];

 

    error = write(outFile,data,512);

    if(error < 0) return error;

 

    memset(&t,0xff,sizeof (yaffs_Tags));

    memset(&s,0xff,sizeof (yaffs_Spare));

 

    t.chunkId = chunkId;

    t.serialNumber = 0;

    t.byteCount = nBytes;

    t.objectId = objId;

 

    if (convert_endian)

    {

        little_to_big_endian(&t);

    }

 

    yaffs_CalcTagsECC(&t);

    yaffs_LoadTagsIntoSpare(&s,&t);

    yaffs_CalcECC(data,&s);

 

    nPages++;

#if 0

    return write(outFile,&s,sizeof(yaffs_Spare));

#else

    memset(oobdata,0xff,16);

    translate_spare2oob( &s, oobdata )

 

//因为采用的是硬件ECC, 这里忽略了yaffs自身的ECC

return write(outFile, oobdata, 16);

#endif

}

 

6)

再次重新编译mkyaffsimage工具,并制作yaffs.img

我使用了U盘,将yaffs.img文件等都拷贝到U盘里面了。

 

7)

flash-eraseall /dev/mtd2

nandwrite –a –o /dev/mtd2 /mnt/yaffs.img

挂载yaffs mtd

mount –t yaffs /dev/mtdblock2 /mnt

这回可以正确mount到mtdblock2,但还是只有一个lost+found目录, 其余空空如也.

umount /mnt/

 

8)

使用mkyaffs工具对mtd2格式化

mkyaffs /dev/mtd2

 

9)

mount –t yaffs /dev/mtdblock2 /tmp/

再mount,发现还是只有lost+found目录,但是mtd3已经可以读写了。拷贝所有文件系统要使用的目录及文件到mnt目录,重启,再mount,原来的文件还在,说明yaffs盘(即mtd2)可以使用了。

 

10)

在PC上打包文件系统

tar -zcvf yaffs.tgz yaffs/

拷贝yaffs.tgz文件到USB中,插到开发板上,挂载U盘。

cd /tmp/

tar zxvf ../mnt/yaffs.tgz

看到一系列的解压写入过程后,看到tmp目录下多了个yaffs目录,接下来

mv -f ./yaffs/* ./

rm -rf lost+found/

rm -rf ./yaffs/

ls

可以看到,tmp目录下已经包含我们要的根文件系统所要的所有文件了。

 

11)

烧写不带ramdisk的内核进mtd0并指定root启动参数:

"root=/dev/mtdblock2"

重启。

可以进入文件系统啦。

 

12)

有待解决问题:

为什么我nandwrite进mtd2的yaffs.img里面的内容不能被直接mount出来?

我的本意是直接烧写一个img文件就可以使用mtd盘了。

 

至今还没找到好的解决方法,使用烧写yaffs.img的工具用过flashcp flash-dd nandwrite都不能将整个文件系统拷进mtd里面并正确读取。只看到一个lost+found目录。

 

还有,我看到重启后的文件系统是只读的,如何修改成可读写的属性呢?

 

有什么高手可以指教并帮我解决下吗?

 

使用硬件参数:

CPU:海山HS3210

NAND FLASH:Samsung K9F2G08

 

 

 

本文参考CSDN博客:

http://blog.csdn.net/force_eagle/archive/2008/02/28/2128407.aspx

 

上一篇:搭建linux RIS服务器批量布署windows 2003系统
下一篇:初步解决yaffs文件系统的nand flash烧写