S3C2416裸機開發(fā)系列二一_Yaffs的移植
Nand作為市面上最主要的非易失性閃存技術(shù)之一,應(yīng)用在各種固態(tài)大容量存儲解決方案中。由于Nand flash自身的特點,Nand存儲器往往需要一款專用的Nand文件系統(tǒng)進(jìn)行管理。開源的Yaffs文件系統(tǒng)由于其優(yōu)異的性能,在Nand flash中受到廣泛的應(yīng)用,筆者此處就Yaffs的移植作一個簡單的介紹。
1. Yaffs概述Yaffs是由Aleph One公司所發(fā)展出來的Nand flash文件系統(tǒng),專門為Nand flash存儲器設(shè)計,適用于大容量的存儲設(shè)備。在GPL協(xié)議下發(fā)布,可在其官網(wǎng)上免費獲得源碼。
Yaffs是基于日志的文件系統(tǒng),提供了壞塊管理、磨損平衡和掉電恢復(fù)的健壯性,保證數(shù)據(jù)在系統(tǒng)對文件系統(tǒng)修改的過程中發(fā)生意外也不被破壞。特別針對Nand flash,在啟動時間、內(nèi)存空間占用、讀寫速度等方面做了優(yōu)化,已經(jīng)在Linux、Android、WinCE等商業(yè)產(chǎn)品中使用。
2. Yaffs移植Yaffs文件系統(tǒng)分為文件系統(tǒng)管理層接口、Yaffs內(nèi)部實現(xiàn)層和Nand接口層,這簡化了與系統(tǒng)的接口設(shè)計,便于集成到系統(tǒng)中去。移植即為實現(xiàn)Nand接口層。由于Yaffs一直在維護更新,其內(nèi)部數(shù)據(jù)結(jié)構(gòu)、函數(shù)實現(xiàn)流程等有細(xì)微的更新。因此對于時間跨度比較大的版本,再者之間的移植將會有較大的差異。對于可移植的開源項目,一般應(yīng)在源碼包相應(yīng)的makefile、readme等文檔中獲知項目的目錄架構(gòu),提取相應(yīng)的源碼。接口的移植也應(yīng)參考源碼包中的Demo接口移植,了解相應(yīng)接口應(yīng)實現(xiàn)的功能需求,便于針對特定設(shè)備重新實現(xiàn)類似的接口功能。應(yīng)用編程也可以參考源碼中的應(yīng)用測試代碼。筆者此處以2015/06版本的源碼為例說明Yaffs的移植。
2.1. 編譯器相關(guān)對于可移植開源項目,不會使用編譯器的數(shù)據(jù)類型、擴展語法等,因為不同體系的cpu、不同編譯器這部分是不同的,是不可移植的,開源項目有自己定義的數(shù)據(jù)類型,這是需要根據(jù)具體的cpu、具體的編譯器重定義的。Yaffs提供posix文件操作接口,使用了posix文件操作數(shù)據(jù)類型,而posix為unix下可移植操作系統(tǒng)應(yīng)用編程接口,并不是c標(biāo)準(zhǔn),c編譯器不必實現(xiàn)posix,因此需自定義Yaffs中使用到的posix數(shù)據(jù)類型。Yaffs應(yīng)用編程跟posix文件操作應(yīng)用編程是完全一致的。即基于posix的應(yīng)用程序在基于unix類、windows、支持posix的rtos等都是源碼級可移植的。
#ifndef __YAFFS_CONFIG_H__
#define __YAFFS_CONFIG_H__
#define CONFIG_YAFFS_DIRECT
#define CONFIG_YAFFS_YAFFS2
#define CONFIG_YAFFS_PROVIDE_DEFS
#define CONFIG_YAFFSFS_PROVIDE_VALUES
#define CONFIG_YAFFS_DEFINES_TYPES
#define inline __inline
typedef unsigned short dev_t;
typedef unsigned short mode_t;
typedef long off_t;
typedef long long loff_t;
#endif
2.2. 操作系統(tǒng)相關(guān)Yaffs需要訪問操作系統(tǒng)資源,如提供鎖、時間戳、系統(tǒng)錯誤等。對于單線程訪問、無操作系統(tǒng)并不需要操作系統(tǒng)的鎖等相關(guān)功能。在Yaffs中yaffs_osglue.h列出了所需實現(xiàn)的操作系統(tǒng)相關(guān)接口函數(shù)。
#include"stdio.h"
#include"stdlib.h"
#include"time.h"
static intyaffs_errno;
/*
* yaffs_bug_fn()
* Function to report a bug.
*/
void yaffs_bug_fn(constchar *fn, int n)
{
printf("yaffs bug at %s:%dn", fn,n);
}
/*
* yaffsfs_CurrentTime() retrns a 32-bittimestamp.
*
* Can return 0 if your system does not careabout time.
*/
unsigned intyaffsfs_CurrentTime(void)
{
return time(NULL);
}
/*
* yaffsfs_SetError() andyaffsfs_GetLastError()
* Do whatever to set the system error.
* yaffsfs_GetLastError() just fetches the lasterror.
*/
voidyaffsfs_SetError(int err)
{
yaffs_errno = err;
}
intyaffsfs_GetLastError(void)
{
return yaffs_errno;
}
/*
* yaffsfs_CheckMemRegion()
* Check that access to an address is valid.
* This can check memory is in bounds and iswritable etc.
*
* Returns 0 if ok, negative if not.
*/
intyaffsfs_CheckMemRegion(const void *addr, size_t size, int write_request)
{
if(!addr) {
return -1;
}
return 0;
}
/*
* yaffsfs_malloc()
* yaffsfs_free()
*
* Functions to allocate and free memory.
*/
void*yaffsfs_malloc(size_t size)
{
return malloc(size);
}
voidyaffsfs_free(void *ptr)
{
free(ptr);
}
/*
* yaffsfs_Lock()
* yaffsfs_Unlock()
* A single mechanism to lock and unlock yaffs.Hook up to a mutex or whatever.
*/
voidyaffsfs_Lock(void)
{
}
voidyaffsfs_Unlock(void)
{
}
voidyaffsfs_OSInitialisation(void)
{
/* No locking used */
}
#if defined(__CC_ARM)/* ARMCC compiler */
// MDK不支持strnlen函數(shù),重新實現(xiàn)
int strnlen(const char *Str, int MaxLen)
{
int i;
for (i=0;i if(Str[i] == 0) { break; } } return i; } #endif Nand驅(qū)動在前面章節(jié)有詳細(xì)的描述,一般針對Nand flash的特性,Nand底層驅(qū)動應(yīng)實現(xiàn)Nand初始化、Nand頁讀、Nand頁編程、Nand塊擦除、Nand壞塊標(biāo)記、Nand壞塊檢查。Yaffs通過函數(shù)指針的方式實現(xiàn)訪問以上的Nand底層驅(qū)動接口,需實現(xiàn)的Nand接口函數(shù)指針如下: int(*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, const u8 *data, int data_len, const u8 *oob, int oob_len); int(*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, u8 *data, int data_len, u8 *oob, int oob_len, enum yaffs_ecc_result *ecc_result); int(*drv_erase_fn) (struct yaffs_dev *dev, int block_no); int(*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no); int(*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no); int(*drv_initialise_fn) (struct yaffs_dev *dev); int(*drv_deinitialise_fn) (struct yaffs_dev *dev); drv_initialise_fn主要實現(xiàn)Nand的初始化,在文件系統(tǒng)掛載時,會最先調(diào)用該函數(shù)指針對Nand進(jìn)行初始化。 static int yaffs_nand_drv_Initialise(struct yaffs_dev*dev) { Nand_Init(); returnYAFFS_OK; } drv_erase_fn主要對某一個塊進(jìn)行擦除。 static int yaffs_nand_drv_EraseBlock(struct yaffs_dev*dev, int block_no) { if (Nand_EraseBlock(block_no)!= 0) { returnYAFFS_FAIL; } returnYAFFS_OK; } drv_mark_bad_fn需實現(xiàn)對某一塊進(jìn)行壞塊標(biāo)記。 static int yaffs_nand_drv_MarkBad(struct yaffs_dev*dev, int block_no) { if(Nand_MarkBadBlock(block_no) != 0) { returnYAFFS_FAIL; } returnYAFFS_OK; } drv_check_bad_fn需實現(xiàn)對某一塊進(jìn)行檢查,是否壞塊。 static int yaffs_nand_drv_CheckBad(struct yaffs_dev*dev, int block_no) { if(Nand_IsBadBlock(block_no) != 0) { // badblock returnYAFFS_FAIL; } returnYAFFS_OK; } drv_write_chunk_fn需實現(xiàn)對某chunk(page)在Nand data area寫入特定長度的數(shù)據(jù),通常為1 chunk(page),在Nand spare area寫入特定長度的oob數(shù)據(jù)(tags)。 static int yaffs_nand_drv_WriteChunk(struct yaffs_dev*dev, int nand_chunk, const u8 *data,int data_len, const u8 *oob, int oob_len) { if (!data ||!oob) { returnYAFFS_FAIL; } if(Nand_WriteWithOob(nand_chunk, data, data_len, oob, oob_len) != 0) { returnYAFFS_FAIL; } returnYAFFS_OK; } drv_read_chunk_fn需實現(xiàn)對某chunk(page)在Nand data area讀取特定長度的數(shù)據(jù),通常為1 chunk(page),在Nand spare area讀取特定長度的oob數(shù)據(jù)(tags)。此處采用Nand驅(qū)動硬件ecc,而未使用Yaffs自帶的軟件ecc,需處理數(shù)據(jù)是否無錯或可糾錯。 static int yaffs_nand_drv_ReadChunk(struct yaffs_dev*dev, int nand_chunk, u8*data, int data_len, u8 *oob, int oob_len, enumyaffs_ecc_result *ecc_result_out) { int ret; if (data ==NULL) { data_len= 0; } ret =Nand_ReadWithOob(nand_chunk, data, data_len, oob, oob_len); if (ret != 0){ if(ecc_result_out) {