Linux Flash Driver
在Linux中,采用MTD(memory technology device)系统来建立各种flash对linux的统一接口。结构图如下。
硬件驱动层:负责驱动硬件工作,包括读写擦除等,NOR Flash驱动在drivers\mtd\chips目录下,NAND Flash驱动在drivers\mtd\nand目录下。
MTD原始设备层:一部分是通用代码,一部分是各Flash的数据(如分区)。
MTD设备层:分为字符设备(mtdchar.c)和块设备(mtdblock.c),建立在mtd原始设备层之上,为应用程序提供访问flash的接口。
在MTD原始设备层,每个flash分区都对应一个mtd_info的结构体,这些mtd_info存放在mtd_table数组里。mtd_info主要包括此分区的类型,标志,大小,名字,索引号,以及读写擦除函数等。下面是mtd_info的具体成员:
struct mtd_info {
u_char type; //类型,如MTD_NANDFlash
uint32_t flags; //标志
uint64_t size; // Total size of the MTD
uint32_t erasesize;//擦除大小
uint32_t writesize;//写入大小,NORFLASH为1,NANDFLASH为页/半页/1/4页大小
uint32_t oobsize; // Amount of OOB data per block (e.g. 16)
uint32_t oobavail; // Available OOB bytes per block
......
// Kernel-only stuff starts here.
const char *name; //分区名字
int index; //分区索引号
......
//擦除函数
int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
......
//读写函数
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);
......
//oob读写函数
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);
......
/* If the driver is something smart, like UBI, it may need to maintain
* its own reference counting. The below functions are only for driver.
* The driver may register its callbacks. These callbacks are not
* supposed to be called by MTD users */
int (*get_device) (struct mtd_info *mtd);
void (*put_device) (struct mtd_info *mtd);
};
这些read,write,erase函数在MTD原始层有通用的函数。
MTD原始设备的增加和删除:
int add_mtd_device(struct mtd_info *mtd);会将mtd加入mtd_table
int del_mtd_device (struct mtd_info *mtd);把mtd从mtd_table中删除
MTD原始设备层中的分区:有两种分区,一是master分区(大小涵盖整个FLASH),二是普通分区。分区描述结构:
/* Our partition node structure */
struct mtd_part {
struct mtd_info mtd;
struct mtd_info *master;
uint64_t offset;
struct list_head list;
};
在MTD原始设备层中维护着一个mtd_part链表mtd_partitions。
mtd_info成员用来描述本分区,大部分的成员由master分区决定,各函数也指向主分区中相同的函数,普通分区会被加入到mtd_table中,而master分区不会。
FLASH驱动向MTD原始设备层注册分区时会采用一个结构体mtd_patition。这些分区信息来源:首先查找命令行中是否提供分区信息,如果有就使用它,如果没有就使用platform中提供的默认分区信息。(一般flash驱动采用platform平台管理)。
struct mtd_partition {
char *name; /* identifier string */
uint64_t size; /* partition size */
uint64_t offset; /* offset within the master MTD space */
uint32_t mask_flags; /* master MTD flags to mask out for this partition */
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/
};
分区注册函数:
int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)
来增加分区信息到MTD原始层,然后调用add_mtd_device将master分区加入MTD原始层。
add_mtd_partitions对每个分区信息调用了add_one_partition,在add_one_partition中,申请了一个mtd_part结构体变量,将它加入到mtd_partitions,然后根据master分区填充此结构体,最后调用add_mtd_device将此分区加入到MTD原始层中。
在驱动退出时,应该从MTD原始设备层中删除分区:
int del_mtd_partitions(struct mtd_info *master);
在MTD设备层,当驱动设备文件被打开时,会跟据所打开的设备文件的次设备号调用get_mtd_device,得到mtd_info结构体,从而使用其中的read,write,erase等函数向应用程序提供服务。
普通分区mtd_info中的read,write,erase函数都是指向master分区中的同名函数,master分区中的read,write,erase函数来自于flash硬件驱动提供的函数,这样就形成了整个调用链。
NAND Flash 驱动的开发:
MTD层已经实现了通用的NAND驱动(nand_base.c),所以一些通用函数不再需要理会,要理会的是一些参数和特别的函数,填充一个nand_chip结构体。此结构体中包含了NAND FLASH的地址信息,读写方法,ECC校验方法,硬件控制等等。
(1)申请mtd_info结构体并初始化,申请nand_chip结构体并初始化,将mdt_info的priv指针指向nand_chip。
(2)映射好FLASH内存,设置好nand_chip中的成员。
(3)调用nand_scan扫描FLASH。
(4)注册分区信息。
NOR Flash驱动开发:
(1)定义map_info结构体的实例,填充好接口。
(2)调用do_map_probe()得到mtd_info。
(3)注册分区信息。