MTD(memory technology device):内存技术设备
是linux用于描述ROM,NAND,NOR等内存设备的子系统的抽象
MTD设备可以按块读写也可以按字节读写,也就是说MTD设备既可以是块设备也可以是字符设备
一.MTD设备基础
1.关键结构体对象
在MTD中用mtd_info来描述一个内存设备
struct mtd_info {
u_char type; //mtd设备类型
uint32_t flags; //标志
uint64_t size; //mtd设备总容量
uint32_t erasesize; //擦除数据大小
uint32_t writesize; //可写入数据最小字节数
uint32_t writebufsize; //写缓冲区大小
uint32_t oobsize; //oob区字节数
uint32_t oobavail; //可用oob区字节数
unsigned int erasesize_shift; //擦除数据偏移值
unsigned int writesize_shift; //写入数据偏移值
unsigned int erasesize_mask; //擦除数据大小掩码
unsigned int writesize_mask; //写入数据大小掩码
const char *name; //mtd设备名
int index; //索引值
struct nand_ecclayout *ecclayout; //nand ecc布局
int numeraseregions; //
struct mtd_erase_region_info *eraseregions; //
int (*erase) (struct mtd_info *mtd, struct erase_info *instr); //擦除
int (*point) (struct mtd_info *mtd, loff_t from, size_t len,size_t *retlen, void **virt, resource_size_t *phys);
void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len); //
unsigned long (*get_unmapped_area) (struct mtd_info *mtd,unsigned long len,unsigned long offset,unsigned long flags);
struct backing_dev_info *backing_dev_info; //映射性能
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 (*panic_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); //读oob区
int (*write_oob) (struct mtd_info *mtd, loff_t to,struct mtd_oob_ops *ops); //写oob区
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);
int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
void (*sync) (struct mtd_info *mtd);
int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
int (*is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
int (*suspend) (struct mtd_info *mtd);
void (*resume) (struct mtd_info *mtd);
int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
struct notifier_block reboot_notifier; //
struct mtd_ecc_stats ecc_stats; //
int subpage_sft; //
void *priv; //私有数据
struct module *owner; //模块所有者
struct device dev; //设备文件
int usecount; //使用者计数
int (*get_device) (struct mtd_info *mtd);
void (*put_device) (struct mtd_info *mtd);
};
在MTD中用mtd_part来描述一个flash分区关系
struct mtd_part {
struct mtd_info mtd; //分区信息
struct mtd_info *master; //主分区
uint64_t offset; //分区偏移值
struct list_head list; //链表
};
在MTD中用mtd_partition来描述一个flash分区名字大小等信息
struct mtd_partition {
char *name; //分区名
uint64_t size; //分区大小
uint64_t offset; //分区的偏移值
uint32_t mask_flags; //标志掩码
struct nand_ecclayout *ecclayout; //ecc布局
};
2.主设备号
#define MTD_CHAR_MAJOR 90 //MTD字符设备主设备号
#define MTD_BLOCK_MAJOR 31 //MTD块设备主设备号
3.设备类
static struct class mtd_class = {
.name = "mtd", //类名
.owner = THIS_MODULE, //模块所有者
.suspend = mtd_cls_suspend,
.resume = mtd_cls_resume,
};
设备类的注册在init_mtd函数中注册
module_init(init_mtd); //模块入口
static int __init init_mtd(void)
{
int ret;
ret = class_register(&mtd_class); //注册MTD设备类
if (ret)
goto err_reg;
ret = mtd_bdi_init(&mtd_bdi_unmappable, "mtd-unmap");
if (ret)
goto err_bdi1;
ret = mtd_bdi_init(&mtd_bdi_ro_mappable, "mtd-romap");
if (ret)
goto err_bdi2;
ret = mtd_bdi_init(&mtd_bdi_rw_mappable, "mtd-rwmap");
if (ret)
goto err_bdi3;
#ifdef CONFIG_PROC_FS
if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) //创建proc文件系统接口"/proc/mtd"
proc_mtd->read_proc = mtd_read_proc;
#endif /* CONFIG_PROC_FS */
return 0;
err_bdi3:
bdi_destroy(&mtd_bdi_ro_mappable);
err_bdi2:
bdi_destroy(&mtd_bdi_unmappable);
err_bdi1:
class_unregister(&mtd_class);
err_reg:
pr_err("Error registering mtd class or bdi: %d\n", ret);
return ret;
}
4.全局链表
mtd_partitions //mtd设备分区链表
add_mtd_partitions函数中添加
blktrans_majors //mtd_blktrans_ops结构体对象链表
添加链表:register_mtd_blktrans函数中
遍历链表:blktrans_notify_add函数中
块设备:mtdblock_tr->add_mtd(mtdblock_add_mtd) >>> add_mtd_blktrans_dev
mtd_notifiers //mtd_notifiers通知者链表
添加链表:register_mtd_blktrans >>> register_mtd_user函数中
遍历链表:add_mtd_device函数中遍历
块设备:blktrans_notifier->add(blktrans_notify_add)
5.整体流程:
5.1 字符设备部分
module_init(init_mtd) //注册mtd设备类
module_init(init_mtdchar); //mtd字符设备模块入口
__register_chrdev //注册字符设备
调用register_mtd_user
添加mtdchar_notifier到全局mtd_notifiers链表
接着调用add_mtd_partitions函数
for循环建立分区{
继承mtd_info--master属性(nand/nor...)
添加分区到全局mtd_partitions链表
接着调用add_mtd_device函数
-------------------------------------------mtd_notifiers
遍历全局mtd_notifiers链表
调用m