Nand作为市面上最基本的非易失性闪存技术之中的一个,应用在各种固态大容量存储解决方式中。因为Nand flash自身的特点,Nand存储器往往须要一款专用的Nand文件系统进行管理。开源的Yaffs文件系统因为其优异的性能,在Nand flash中受到广泛的应用,笔者此处就Yaffs的移植作一个简单的介绍。
1. Yaffs概述
Yaffs是由Aleph One公司所发展出来的Nand flash文件系统,专门为Nand flash存储器设计,适用于大容量的存储设备。在GPL协议下公布,可在其官网上免费获得源代码。
Yaffs是基于日志的文件系统,提供了坏块管理、磨损平衡和掉电恢复的健壮性,保证数据在系统对文件系统改动的过程中发生意外也不被破坏。特别针对Nand flash,在启动时间、内存空间占用、读写速度等方面做了优化,已经在Linux、Android、WinCE等商业产品中使用。
2. Yaffs移植
Yaffs文件系统分为文件系统管理层接口、Yaffs内部实现层和Nand接口层。这简化了与系统的接口设计。便于集成到系统中去。移植即为实现Nand接口层。
因为Yaffs一直在维护更新。其内部数据结构、函数实现流程等有细微的更新。
因此对于时间跨度比較大的版本号,再者之间的移植将会有较大的差异。
对于可移植的开源项目,一般应在源代码包对应的makefile、readme等文档中获知项目的文件夹架构,提取对应的源代码。接口的移植也应參考源代码包中的Demo接口移植。了解对应接口应实现的功能需求,便于针对特定设备又一次实现类似的接口功能。应用编程也能够參考源代码中的应用測试代码。笔者此处以2015/06版本号的源代码为例说明Yaffs的移植。
2.1. 编译器相关
对于可移植开源项目。不会使用编译器的数据类型、扩展语法等,由于不同体系的cpu、不同编译器这部分是不同的,是不可移植的,开源项目有自定义的数据类型。这是须要依据详细的cpu、详细的编译器重定义的。
Yaffs提供posix文件操作接口,使用了posix文件操作数据类型。而posix为unix下可移植操作系统应用编程接口。并非c标准,c编译器不必实现posix。因此需自定义Yaffs中使用到的posix数据类型。Yaffs应用编程跟posix文件操作应用编程是全然一致的。
即基于posix的应用程序在基于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
typedef unsigned short dev_t;
typedef unsigned short mode_t;
typedef long off_t;
typedef long long loff_t;
#endif
2.2. 操作系统相关
Yaffs须要訪问操作系统资源,如提供锁、时间戳、系统错误等。对于单线程訪问、无操作系统并不须要操作系统的锁等相关功能。在Yaffs中yaffs_osglue.h列出了所需实现的操作系统相关接口函数。
#include'stdio.h'
#include'stdlib.h'
#include'time.h'
static intyaffs_errno;
/*
* yaffs_bug_fn()
* Function to report a bug.
*/
voidyaffs_bug_fn(const char *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 */
}
2.3. Nand接口相关
Nand驱动在前面章节有具体的描写叙述,一般针对Nand flash的特性,Nand底层驱动应实现Nand初始化、Nand页读、Nand页编程、Nand块擦除、Nand坏块标记、Nand坏块检查。Yaffs通过函数指针的方式实现訪问以上的Nand底层驱动接口,需实现的Nand接口函数指针例如以下:
int(*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk,
const u8 *data, int data_len,
constu8 *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);
2.3.1. drv_initialise_fn函数指针
drv_initialise_fn主要实现Nand的初始化,在文件系统挂载时,会最先调用该函数指针对Nand进行初始化。
static int yaffs_nand_drv_Initialise(struct yaffs_dev*dev)
{
Nand_Init();
returnYAFFS_OK;
}
2.3.2. drv_erase_fn函数指针
drv_erase_fn主要对某一个块进行擦除。
static int yaffs_nand_drv_EraseBlock(struct yaffs_dev*dev, int block_no)
{
if(Nand_EraseBlock(block_no) != 0) {
returnYAFFS_FAIL;
}
returnYAFFS_OK;
}
2.3.3. drv_mark_bad_fn函数指针
drv_mark_bad_fn需实现对某一块进行坏块标记。
static int yaffs_nand_drv_MarkBad(struct yaffs_dev*dev, int block_no)
{
if (Nand_MarkBadBlock(block_no)!= 0) {
returnYAFFS_FAIL;
}
returnYAFFS_OK;
}
2.3.4. drv_check_bad_fn函数指针
drv_check_bad_fn需实现对某一块进行检查,是否坏块。
static int yaffs_nand_drv_CheckBad(struct yaffs_dev*dev, int block_no)
{
if(Nand_IsBadBlock(block_no) != 0) {
// badblock
returnYAFFS_FAIL;
}
returnYAFFS_OK;
}
2.3.5. drv_write_chunk_fn函数指针
drv_write_chunk_fn需实现对某chunk(page)在Nand data area写入特定长度的数据。通常为1 chunk(page),在Nand spare area写入特定长度的oob数据(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;
}
2.3.6.drv_read_chunk_fn函数指针
drv_read_chunk_fn需实现对某chunk(page)在Nand data area读取特定长度的数据,通常为1 chunk(page),在Nand spare area读取特定长度的oob数据(tags)。此处採用Nand驱动硬件ecc。而未使用Yaffs自带的软件ecc,需处理数据是否无错或可纠错。
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) {
*ecc_result_out= YAFFS_ECC_RESULT_UNKNOWN;
}
returnYAFFS_FAIL;
} else {
if(ecc_result_out) {
*ecc_result_out= YAFFS_ECC_RESULT_NO_ERROR;
}
}
returnYAFFS_OK;
}
2.3.7.drv_deinitialise_fn函数指针
drv_deinitialise_fn为取消选中Nand flash,do nothing。
static int yaffs_nand_drv_Deinitialise(structyaffs_dev *dev)
{
returnYAFFS_OK;
}
2.3.8.yaffs_start_up函数
Yaffs在挂载使用前,必须先安装Nand驱动。通过yaffs_start_up函数把对应的Nand底层訪问接口载入进Yaffs的接口层。
struct yaffs_dev *yaffs_nand_install_drv(const char*dev_name)
{
struct yaffs_driver*drv;
structyaffs_dev *dev;
structyaffs_param *param;
dev =malloc(sizeof(struct yaffs_dev));
if (!dev) {
returnNULL;
}
memset(dev,0, sizeof(*dev));
param =&dev->param;
param->name= strdup(dev_name);
if(!param->name){
free(dev);
returnNULL;
}
param->total_bytes_per_chunk= 2048;
param->chunks_per_block= 64;
param->n_reserved_blocks= 5;
param->start_block= 32; // First block, reserve 4M for boot
param->end_block= 4096 - 1;
param->is_yaffs2= 1;
param->use_nand_ecc= 1; // use driver's ecc
param->n_caches= 10;
drv =&dev->drv;
drv->drv_write_chunk_fn= yaffs_nand_drv_WriteChunk;
drv->drv_read_chunk_fn= yaffs_nand_drv_ReadChunk;
drv->drv_erase_fn= yaffs_nand_drv_EraseBlock;
drv->drv_mark_bad_fn= yaffs_nand_drv_MarkBad;
drv->drv_check_bad_fn= yaffs_nand_drv_CheckBad;
drv->drv_initialise_fn= yaffs_nand_drv_Initialise;
drv->drv_deinitialise_fn= yaffs_nand_drv_Deinitialise;
/* The yaffsdevice has been configured, install it into yaffs */
yaffs_add_device(dev);
return dev;
}
int yaffs_start_up(void)
{
static u8start_up_called = 0;
上一篇:第七章之S5PV210移植到Nandflash
下一篇:S5PV210开发系列四_uCGUI的移植
- 热门资源推荐
- 热门放大器推荐
- 使用 Analog Devices 的 LT3088HST 的参考设计
- 用于 VIN 和 VDD、5A 同步降压转换器的 NCP3135 单电压轨的典型应用
- 使用 ROHM Semiconductor 的 BU94607KV 的参考设计
- LT8570IDD-1 1.5MHz、5V 至 12V 升压转换器的典型应用
- LTC1096、0 至 2A 电池电流监视器从 3V 至 6V 电池仅消耗 70uA
- 使用 Analog Devices 的 LTC1728HS5-5 的参考设计
- Woof Meter:测量低音炮的 TS 参数
- AD5341 并行接口、单电压输出、12 位 DAC 的典型应用
- 具有 PowerPath 的 LTC4162IUFD-FFSM 1 节 USB 电力传输充电器的典型应用
- ADR03 的典型应用电路,用于可编程 0 mA 至 5 mA 电流源的 2.5V 电压基准



非常经典的关于LLC的杨波博士论文
AD744BH/+

1-292148-8






京公网安备 11010802033920号