录下的对应文件中。这些映射数据包括分区信息、I/O映射及特定函数的映射等。这种映射关系用映射信息结构map_info描述。 在MTD设备层中,MTD字符设备通过注册的file operation函数集来操作设备,而这些函数是通过原始设备层的操作函数来实现的,即调用了块设备的操作函数。MTD块设备实际了从块层到块设备的接口函数。所有的块设备组成一个数组*mtdblks[MAX_MTD_DEVICES],这个结构数组列出如下(在drivers/mtd/mtdblock.c中):
static struct mtdblk_dev { struct mtd_info *mtd; int count;
struct semaphore cache_sem; unsigned char *cache_data; unsigned long cache_offset; unsigned int cache_size;
enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; } *mtdblks[MAX_MTD_DEVICES];
由于flash设备种类的多样性,MTD用MTD翻译层将三大类flash设备进行的封装。每大类设备有自己的操作函数集,它们的mtdblk_dev结构实例都存在mtdblks数组中。MTD设备在内核中的层次图如下图。
图 MTD设备在内核中的层次图
MTD原始设备层中封装了三大类设备,分别是Inverse Flash、NAND Flash和MTD。它们的上体读写方法不一样。这里只分析了MTD,因为它是最常用的。
设备层和原始设备层的函数调用关系
原始设备层主要是通过mtd_info结构来管理设备,函数add_mtd_partitions()和del_mtd_partitions()将的设备分区的mtd_info结构加入mtd_table数组中,mtdpart.c中还实现了part_read、part_write等函数,这些函数注册在每个分区中,指向主分区的read、write函数,之所以这样做而不直接将主分区的read、write函数连接到每个分区中的原因是因为函数中的参数mtd_info会被调用者置为函数所属的mtd_info,即mtd->read(mtd?),而参数mtd_info其实应该指向主分区。
设备层和原始设备层的函数调用关系图如图2。MTD各种结构之间的关系图如图3。
图2 设备层和原始设备层的函数调用关系
图3 MTD各种结构之间的关系
MTD相关结构
MTD块设备的结构mtdblk_dev代表了一个闪存块设备,MTD字符设备没有相对应的结构。结构mtdblk_dev列出如下:
struct mtdblk_dev {
struct mtd_info mtd; / Locked */ 下层原始设备层的MTD设备结构 int count;
struct semaphore cache_sem; unsigned char *cache_data; //缓冲区数据地址
unsigned long cache_offset;//在缓冲区中读写位置偏移
//缓冲区中的读写数据大小(通常被设置为MTD设备的erasesize) unsigned int cache_size;
enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;//缓冲区状态 }
结构mtd_info描述了一个MTD原始设备,每个分区也被实现为一个mtd_info,如果有两个MTD原始设备,每个上有三个分区,在系统中就一共有6个mtd_info结构,这些mtd_info的指针被存放在名为mtd_table的数组里。结构mtd_info分析如下:
struct mtd_info {
u_char type;
//内存技术的类型 //标志位
u_int32_t flags;
u_int32_t size; // mtd设备的大小
//“主要的”erasesize(同一个mtd设备可能有数种不同的erasesize) u_int32_t erasesize;
u_int32_t oobblock; // oob块大小,例如:512
// Kernel-only stuff starts here. char *name; int index;
//可变擦除区域的数据,如果是0,意味着整个设备为erasesize int numeraseregions; //不同erasesize的区域的数目(通常是1) struct mtd_erase_region_info *eraseregions; u_int32_t bank_size; struct module *module;
//此routine用于将一个erase_info加入erase queue
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);
int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
u_int32_t oobsize; //每个块oob数据量,例如16 u_int32_t ecctype; u_int32_t eccsize;
//ecc类型
//自动ecc可以工作的范围
int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
int (*read_ecc) (struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf, u_char *eccbuf);
int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf); int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
/* iovec-based read/write methods. We need these especially for NAND flash,
with its limited number of write cycles per erase.
NB: The ‘count’ parameter is the number of vectors, each of which contains an (ofs, len) tuple. */
int (*readv) (struct mtd_info *mtd, struct iovec *vecs,
unsigned long count, loff_t from, size_t *retlen); int (*writev) (struct mtd_info *mtd, const struct iovec *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); void *priv; //指向map_info结构 }
设备层的mtdblcok设备的notifier声明如下: static struct mtd_notifier notifier = { mtd_notify_add, mtd_notify_remove, NULL