基于mx27ads 的yaffs 文件系统释疑

转载地址:http://blog.csdn.net/force_eagle/article/details/2128407

Mx27ads bsp内核采用2.6.19, 选择文件系统中的yaffs2

File systems  ---> Miscellaneous filesystems  ---> <*> YAFFS2 file system support

创建测试的yaffs image

mkdir userfs

echo test > userfs/test

mkyaffsimage userfs userfs.yaffs

写入nand flash

nandwrite –a –o /dev/mtd/8 userfs.yaffs

挂载yaffs mtd

mount –t yaffs /dev/mtdblock/8 /mnt/rwfs

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

mx27的使用的是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) {

//转换tag8bytes数据块

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之前一致的数据即可.

以下是修改过的 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

}

引申扩展:

在uboot下 nand_util.c文件里有函数

int nand_write_skip_bad_yaffs(nand_info_t *nand, loff_t offset, size_t *length,u_char *buffer)
{
  ........
   int page, pages;
   size_t pagesize = nand->writesize;
   size_t pagesize_oob = pagesize + nand->oobsize;
   struct mtd_oob_ops ops;

   ops.len = pagesize;
   ops.ooblen = nand->oobsize;
   ops.mode = MTD_OOB_AUTO; ---------------------此处说明是没有使用yaffs自己的ecc数据,仅仅在oob free存放yaffs的文件数据
   ops.ooboffs = 0;

   pages = write_size / pagesize_oob;
   for (page = 0; page < pages; page++) {

    WATCHDOG_RESET();

    ops.datbuf = p_buffer;
    ops.oobbuf = ops.datbuf + pagesize;    
    rval = nand->write_oob(nand, offset, &ops);
    offset += pagesize;
    off += pagesize;
    p_buffer += pagesize_oob;
   }

   unsigned long long n = (unsigned long long)
     (tLength-left_to_write) * 100;

   do_div(n, tLength);
   percent = (int)n;

   /* output progress message only at whole percent
    * steps to reduce the number of messages printed
    * on (slow) serial consoles
    */
   if (percent != percent_complete) {
    printf("\rWriting data at 0x%lx "
           "-- %3d%% complete.",
           off, percent);
    percent_complete = percent;
    }
  if (rval != 0) {
   printf ("NAND write to offset %llx failed %d\n",
    offset, rval);
   *length -= left_to_write;
   return rval;
  }

  left_to_write -= write_size;

 }
 printf("\rWriting data at 0x%lx "
    "-- %3d%% complete.\n",
           off, 100);
 return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值