【OK210试用体验】Yaffs的移植 - 在线问答 - 电子技术论坛 - 最好最受欢迎电子论坛!

【OK210试用体验】Yaffs的移植

刘天 ( 楼主 ) 2015-8-3 21:20:28  只看该作者 倒序浏览
Yaffs的移植
象棋小子    1048272975
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= 4096;
    param->chunks_per_block= 128;
    param->n_reserved_blocks= 5;
    param->start_block= 8; // First block, reserve 4M for boot
    param->end_block= 2048 - 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;
    if(start_up_called){
        return 0;
    }
    start_up_called= 1;
    // Stuff toinitialise anything special (eg lock semaphore).
    yaffsfs_OSInitialisation();
    yaffs_nand_install_drv("/");
    return 0;
}
3. 应用测试
Yaffs提供了posix文件操作应用接口,因此Yaffs应用编程实际与posix文件操作应用编程完全一致。此处测试在”/”目录下可通过选择创建test.txt测试文件,每次可对该文件累计写入测试字符串,通过相应选项读出该文件的内容,可选择列举所有”/”目录下的所有文件,在第一次使用Yaffs时,必须先对Nand flash进行格式化,不然不同的Nand驱动标记的脏数据会造成Nand信息的出错。
static const char test[] = "This is yaffs testfilern";
static const char *yaffs_file_type_str(structyaffs_stat *stat)
{
    switch(stat->st_mode & S_IFMT) {
    case S_IFREG:return "regular file";
    case S_IFDIR:return "directory";
    case S_IFLNK:return "symlink";
    default:return "unknown";
    }
}
int yaffs_ls(const char *mountpt, int longlist)
{
    int i;
    yaffs_DIR *d;
    structyaffs_dirent *de;
    structyaffs_stat stat;
    chartempstr[255];
    d =yaffs_opendir(mountpt);
    if (!d) {
        printf("opendirfailed, %sn", yaffs_error_to_str(yaffsfs_GetLastError()));
        return-1;
    }
    for (i = 0;(de = yaffs_readdir(d)) != NULL; i++) {
    if (longlist){
        sprintf(tempstr,"%s/%s", mountpt, de->d_name);
        yaffs_lstat(tempstr,&stat);
        printf("%-25st%7ld",
                de->d_name,
                (long)stat.st_size);
        printf("%5d %sn",
                stat.st_ino,
                yaffs_file_type_str(&stat));
    } else {
        printf("%sn",de->d_name);
    }
    }
    yaffs_closedir(d);
    return 0;
}
// 重新根据新的驱动规则扫描标注坏块
void nand_scan_bad_block(void)
{
    int i;
    for (i = 8; i<= (2048-1); i++) {
        if(Nand_IsBadBlock(i)) {
        if(Nand_EraseBlock(i)) {
            Nand_MarkBadBlock(i);// 确实为坏块
        }
        }
    }
}
void main(void)
{   
char Text[1024];
int handle;
int BytesRead;
uint8_t Command;   
RTC_Time Time = {
    2015, 7, 1,22, 00, 0, 3
};  
Uart_Init();
RTC_Init(&Time);
   
yaffs_start_up();
yaffs_mount("/");
while (1) {
printf("1: Write test.txtn");
printf("2: View test.txtn");
printf("3: List files in nandn");
printf("4: Format nand flashn");
Command = Uart_WaitChar();
switch (Command) {
case '1':
    handle =yaffs_open("/test.txt", O_CREAT|O_WRONLY|O_APPEND, S_IREAD|S_IWRITE);
    if (handle ==-1) {
        printf("Createtest.txt failedn");
        break;
    }
    yaffs_write(handle,test, strlen(test));
    yaffs_close(handle);
    break;
case '2':
    handle =yaffs_open("/test.txt", O_RDONLY, S_IREAD|S_IWRITE);
    if (handle ==-1) {
        printf("Opentest.txt failedn");
        break;
    }
    while (1) {
    BytesRead =yaffs_read(handle, Text, sizeof(Text)-1);
    if (BytesRead== -1) {
        printf("Read test.txt errorn");
        yaffs_close(handle);
        break;
    }
    Text[BytesRead]= 0;
    printf("%s",Text);
    if (BytesRead< (sizeof(Text)-1)) {
        yaffs_close(handle);
        break;
    }
    }
    break;
case '3':
    yaffs_ls("/",1);
    break;
case '4':
    yaffs_unmount("/");
    nand_scan_bad_block();
    yaffs_format("/",0, 0, 0);
    yaffs_mount("/");
    break;
default:
    break;
}
printf("n");
}
}
4. 附录
OK210_IAR_Yaffs2,Yaffs在IAR下的移植工程,包括S5PV210 Bootloader、Yaffs源码、以及相应的Nand flash驱动。
OK210_IAR_Yaffs2.rar (159.55 KB, 下载次数: 3)

0个回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则


关闭

站长推荐上一条 /6 下一条

小黑屋|手机版|Archiver|电子发烧友 ( 湘ICP备2023018690号 )

GMT+8, 2024-12-4 01:40 , Processed in 0.459306 second(s), Total 39, Slave 25 queries .

Powered by 电子发烧友网

© 2015 bbs.elecfans.com

微信扫描
快速回复 返回顶部 返回列表