基于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
    评论
vc++全版本组件大全 VC++运行时(Visual C++ Runtime)是VC++开发环境中用于支持C和C++程序运行的基础库集合。这些库包含了执行C/C++程序所必需的基本函数和数据结构,例如内存管理、字符串操作、输入输出处理、异常处理等。VC++运行时库分为静态库和动态库两种形式,以适应不同类型的项目需求。 静态链接库 vs 动态链接库 静态链接库(Static Linking Libraries):在编译时,静态库的代码会被直接嵌入到最终生成的可执行文件中。这意味着每个使用静态库的程序都会包含库代码的一个副本,导致最终程序的体积较大,但不需要外部库文件支持即可独立运行。在VC++中,静态链接库的例子有LIBC.lib(用于单线程程序)和LIBCMT.lib(用于多线程程序)。 动态链接库(Dynamic Link Libraries):与静态链接相反,动态库的代码并不直接加入到应用程序中,而是在程序运行时被加载。这使得多个程序可以共享同一份库代码,节省了系统资源。VC++的动态运行时库主要通过msvcrt.dll(或其变体,如MSVCRTD.dll用于调试版本)实现,与之配套的导入库(Import Library)如CRTDLL.lib用于链接阶段。 运行时库的版本 VC++运行时库随着Visual Studio版本的更新而发展,每个版本都可能引入新的特性和优化,同时保持向后兼容性。例如,有VC++ 2005、2008、2010直至2019等多个版本的运行时库,每个版本都对应着特定的开发环境和Windows操作系统。 重要性 VC++运行时对于确保程序正确运行至关重要。当程序在没有安装相应运行时库的计算机上执行时,可能会遇到因缺失DLL文件(如MSVCP*.dll, VCRUNTIME*.dll等)而导致的错误。因此,开发完成后,通常需要分发相应的VC++ Redistributable Packages给最终用户安装,以确保程序能够在目标系统上顺利运行。 安装与部署 安装VC++运行时库通常是通过Microsoft提供的Redistributable Packages完成的,这是一个简单的过程,用户只需运行安装程序即可自动安装所需组件。对于开发者而言,了解和管理不同版本的运行时库对于确保应用程序的广泛兼容性和可靠性是必要的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值