Flash 在嵌入式系统中必不可少,Flash是 BootLoader、Linux 内核和文件系统的最佳载体。在 Linux 内核中,引入 MTD(内存技术设备) 层为 NOR Flash 和NAND Flash 设备提供统一的接口,使得 Flash 驱动的设计工作大为简化。
1、 Linux Flash 驱动的结构,主要讲解MTD 系统的层次结构和接口。
2、讲解NOR Flash 和 NAND Flash 驱动的设计方法,给出设计模板。
3、以 S3C6410 外围 NOR Flash 和 NAND Flash 为实例讲解NOR Flash 和 NAND Flash 驱动的设计。
4、如何在 Flash 上建立 cramfs、jffs/jffs2、yaffs/yaffs2 及ubifs 文件系统。
19.1 Linux Flash驱动结构
19.1.1 Linux MTD 系统层次
在 Linux 系统中,提供 MTD(MemoryTechnology Device,内存技术设备)系统来建立Flash 针对 Linux 的统一、抽象的接口。MTD 将文件系统与底层的 Flash 存储器进行隔离,使Flash 驱动工程师无须关心 Flash 作为字符设备和块设备与 Linux 内核的接口。
如图 19.1 所示,在引入 MTD 后,Linux系统中的 Flash 设备驱动及接口可分为 4 层,从上到下依次是:设备节点、MTD 设备层、MTD 原始设备层和硬件驱动层,这 4 层的作用如下。
图19.1 Linux MTD 系统
1、硬件驱动层:Flash 硬件驱动层负责 Flash 硬件设备的读、写、擦除,Linux MTD 设备的NOR Flash 芯片驱动位于 drivers/mtd/chips 子目录下,NAND 型 Flash 的驱动程序则位于drivers/mtd/nand 子目录下。
2、MTD 原始设备层:MTD 原始设备层由两部分组成,一部分是 MTD 原始设备的通用代码,另一部分是各个特定的 Flash 的数据,例如分区。
3、MTD 设备层:基于 MTD 原始设备,Linux 系统可以定义出 MTD 的块设备(主设备号31)和字符设备(设备号 90),构成 MTD 设备层。MTD 字符设备的定义在 mtdchar.c中实现,通过注册一系列 file_operation 函数(lseek、open、close、read、write、ioctl)可实现对 MTD 设备的读写和控制。MTD 块设备定义了一个描述 MTD 块设备的结构 mtdblk_dev,并声明一个名为 mtdblks 的指针数组,这数组中的每一个 mtdblk_dev
和 mtd_table 中的每一个 mtd_info 一一对应。
4、设备节点:通过 mknod 在/dev 子目录下建立 MTD 字符设备节点(主设备号为 90)和 MTD块设备节点(主设备号为31),用户通过访问此设备节点即可访问MTD 字符设备和块设备。
19.1.2 Linux MTD 系统接口
如图 19.2 所示,在引入 MTD 后,底层 Flash 驱动直接与 MTD 原始设备层交互,利用其提供的接口注册设备和分区。
图19.2 底层 Flash 驱动
描述 MTD 原始设备的数据结构是 mtd_info,定义了大量关于 MTD 的数据和操作函数,如代码清单 19.1 所示。mtd_info 是表示 MTD 原始设备的结构体,每个分区也被认为是一个 mtd_info,例如,如果有两个MTD 原始设备,每个上有 3 个分区,在系统中就有 6 个 mtd_info 结构体,这些 mtd_info 的指针被存放在名为 mtd_table 的数组里。
代码清单 19.1 mtd_info 结构体
include/linux/mtd/mtd.h
struct mtd_info {
u_char type;/* 内存技术的类型 */
u_int32_t flags;/*标志位*/
u_int32_t size; /*mtd 设备的大小*/
/* "Major" erase size for the device. Naïve users may take this
* to be the only erase size available, or may use the more detailed
* information below if they desire
*/
u_int32_t erasesize;/*主要的擦除块大小*/
/* Minimal writable flash unit size. In case of NOR flash it is 1 (even
* though individual bits can be cleared), in case of NAND flash it is
* one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
* it is of ECC block size, etc. It is illegal to have writesize = 0.
* Any driver registering a struct mtd_info must ensure a writesize of
* 1 or larger.
*/
u_int32_t writesize;/*最小的可写单元的字节数*/
u_int32_t oobsize; /* OOB 字节数*/
u_int32_t oobavail; /*可用的 OOB 字节数*/
// Kernel-only stuff starts here.
char *name;
int index;
/* ecc layout structure pointer - read only ! */
struct nand_ecclayout *ecclayout;/*ECC 布局结构体指针*/
/* Data for variable erase regions. If numeraseregions is zero,
* it means that the whole device has erasesize as given above.
*/
int numeraseregions;
struct mtd_erase_region_info *eraseregions;
int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
/* This stuff for eXecute-In-Place */
int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf);
/* We probably shouldn't allow XIP if the unpoint isn't a NULL */
void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len);
int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
int (*read_oob) (struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops);
int (*write_oob) (struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops);
/*
* Methods to access the protection register area, present in some
* flash devices. The user data is one time programmable but the
* factory data is read only.
*/
int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
/* kvec-based read/write methods.
NB: The 'count' parameter is the number of _vectors_, each of
which contains an (ofs, len) tuple.
*/
int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
/* Sync */
void (*sync) (struct mtd_info *mtd);
/* Chip-supported device locking */
int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
/* Power Management functions */
int (*suspend) (struct mtd_info *mtd);
void (*resume) (struct mtd_info *mtd);