分类: LINUX
2010-12-02 13:28:13
关于mtd->write_oob()和chip->ecc.write_oob(),具体有何区别和联系,
自己之前无意间注意到,也迷惑过,但并没有去弄懂,这次看到别人有提问,所以,专门去看了源码,基本算是搞清楚了。
mtd->write_oob,是在nand_scan()->nand_scan_tail()中被赋值的:
mtd->write_oob = nand_write_oob;
那我们就去看看nand_write_oob(),它主要是根据输入参数,决定具体做什么事情:
1.当输入参数中的页数据缓存是空的话,那么就去只是去写oob的数据,也就是单纯的写oob,而不写整个页的数据,也就只调用
nand_do_write_oob()去真正写oob的数据。
2.当输入参数中的页数据缓存pagebuf非空的话,那么不仅写页数据,而且还写oob。
具体调用的函数nand_do_write_ops()去实现既写整页数据,又写oob信息。
而此函数的功能,其实从函数功能的注释中,也可以看得很清楚:NAND write data and/or out-of-band。
下面就分别来看看上面两个数据的具体执行过程。
1.nand_do_write_oob
此函数简单说就是,先填充oob信息,即根据ecc的layout,调用nand_fill_oob,把ecc填充到buffer中;
然后调用chip->ecc.write_oob()去真正实现将oob写入到nand flash中。
至此,mtd->write_oob和ecc.write_oob的其中一种联系,就已经很清楚了:
mtd->write_oob中,如果只是写oob,那么调用nand_do_write_oob()->ecc.write_oob(),
完成oob的实际的写操作。
2.nand_do_write_ops
简单点说就是,先根据设置决定是否去调用nand_fill_oob填充oob,然后再去调用
chip->write_page实现真正页数据的写操作。
对于chip->write_page再解释一下,如果你没有自己实现此函数的话,
也是在nand_scan()->nand_scan_tail()中被赋值成系统默认的:
chip->write_page = nand_write_page;
而nand_write_page()中,根据是否是是raw,去调用不同的函数:
(1)chip->ecc.write_page_raw
关于raw:也就是raw原始数据以及oob,没有在oob中添加ecc信息的。
nand_scan_tail()中:
chip->ecc.write_page_raw = nand_write_page_raw;
nand_write_page_raw()中直接就是先写页数据,再写oob,没有具体去计算ecc并写入,
所以,和我们此处讨论的ecc没啥关系。
(2)chip->ecc.write_page
对于nand 的ecc类型是最常见的NAND_ECC_HW的话,
nand_scan_tail()中:
chip->ecc.write_page = nand_write_page_hwecc;
nand_write_page_hwecc()中,循环ecc的step次
(常见的是,页大小是2K的nand,每512字节产生6位或8位ecc值,ecc的step值就是,
页大小/一次ecc对应数据大小=2k/512=4),
先写启用硬件ecc校验,再写页数据,
然后把得到的ecc校验值组织好,放到oob中对应位置,然后再调用底层的写函数,去写ecc。
这部分,和我们上面的ecc.write_oob,也没有直接关系。
只不过,无论是写页数据,还是写oob数据,底层具体去写的动作,都是调用chip->write_buf()去实现的。
而nand_write_page_hwecc()中后面的写oob的部分,其实和ecc.write_oob是一样的,只不过没单独再去分开而已。
【总结】
mtd->write_oob()和chip->ecc.write_oob()的联系:
1.mtd->write_oob中,如果只是写oob,那么调用nand_do_write_oob()->ecc.write_oob(),
完成oob的实际的写操作。
2.mtd->write_oob中,如果即写页数据又写oob数据,并且是带ecc的写页数据的话,调用
chip->ecc.write_page去实现具体的先写页数据,再写含ecc的oob数据,具体最后的写含ecc的oob的过程,
本质上和ecc.write_oob,底层调用函数实现机制,都是一样的。
【题外话】
1.底层如何定位到oob的起始位置?
中间有个细节,本来很简单清楚的,差点把自己搞晕了,那就是:
对于写oob,到底底层是怎么传入参数使得定位到一个页的oob的起始位置的,然后再开始写oob数据的。
结果就是,再nand_write_oob_std()中,发命令的时候,送的就是定位的起始地址:
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
其中,mtd->writesize就是页内地址,此处就是一个页大小,比如2K,页结束地址=oob开始的位置,
就可以定位到oob起始处了,后面就可以写oob数据了:
chip->write_buf(mtd, buf, length);