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