MTD设备驱动

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
", 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链表
                    调用mtdchar_notifier->add方法
                        --dchar_notify_add无作为
                device_register	//注册字符设备文件 --"/dev/mtd%d"
                device_create	//创建字符设备文件 --"/dev/mtd%dro"
   }	

5.2 块设备部分

module_init(init_mtdblock); 	//mtd块设备模块入口
module_init(mtdblock_init);		//mtd只读块设备模块入口
入口函数调用:register_mtd_blktrans
    register_blkdev【注册块设备】
    先调用register_mtd_user
        添加blktrans_notifier到全局mtd_notifiers链表
    后
        添加mtdblock_tr到全局blktrans_majors链表
接着调用add_mtd_partitions函数
    for循环建立分区{
    继承mtd_info--master属性(nand/nor...)
    添加分区到全局mtd_partitions链表
        接着调用add_mtd_device函数
            -------------------------------------------mtd_notifiers
            遍历全局mtd_notifiers链表
                调用blktrans_notifier->add方法
                    --blktrans_notify_add
                        -------------------------------------------blktrans_majors
                        遍历全局blktrans_majors链表
                            调用mtdblock_tr->add_mtd方法
                                --mtdblock_add_mtd
                                    调用add_mtd_blktrans_dev
                                        alloc_disk	【分配gendisk】
                                        blk_init_queue	【初始化块设备请求队列】
                                        mtd_blktrans_thread%s%d	【守护线程】
                                        add_disk	【添加gendisk】
    }

5.3方法调用的流程

file_operations //字符设备操作方法
	mtd_info slave //分区的操作方法
		mtd_info master	//主分区的操作方法

block_device_operations	//块设备操作方法
	mtd_blktrans_ops	//mtd块操作方法
		mtd_info slave //分区的操作方法
			mtd_info master	//主分区的操作方法


二.添加mtd

1.添加mtd分区

int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)
{
	struct mtd_part *slave;
	uint64_t cur_offset = 0;
	int i;
	printk(KERN_NOTICE "Creating %d MTD partitions on "%s":
", nbparts, master->name);
	for (i = 0; i < nbparts; i++) {	//添加若干个mtd设备,nbparts为分区个数
		slave = allocate_partition(master, parts + i, i, cur_offset);	//分配mtd_part对象内存等
		if (IS_ERR(slave))
			return PTR_ERR(slave);
		mutex_lock(&mtd_partitions_mutex);
		list_add(&slave->list, &mtd_partitions);	//添加到全局mtd_partitions链表(mtd分区链表)
		mutex_unlock(&mtd_partitions_mutex);
		add_mtd_device(&slave->mtd);	//添加mtd设备
		cur_offset = slave->offset + slave->mtd.size;	//调整偏移值
	}
	return 0;
}
EXPORT_SYMBOL(add_mtd_partitions);

for循环分配mtd_part内存并添加mtd_part分区到全局mtd分区链表,添加mtd设备,最后调整偏移量,继续处理下一个mtd分区,如此循环nbparts次

2.分配mtd分区内存

static struct mtd_part *allocate_partition(struct mtd_info *master,const struct mtd_partition *part, int partno,uint64_t cur_offset)
{
	struct mtd_part *slave;	//声明mtd_part结构体对象
	char *name;

	/* allocate the partition structure */
	slave = kzalloc(sizeof(*slave), GFP_KERNEL);	//分配mtd_part对象内存
	name = kstrdup(part->name, GFP_KERNEL);	//分区名
	if (!name || !slave) {
		//printk(KERN_ERR"memory allocation error while creating partitions for "%s"
",master->name);
		kfree(name);
		kfree(slave);
		return ERR_PTR(-ENOMEM);
	}
	slave->mtd.type = master->type;	//mtd设备类型
	slave->mtd.flags = master->flags & ~part->mask_flags;	//mtd设备标志
	slave->mtd.size = part->size;	//mtd分区尺寸
	slave->mtd.writesize = master->writesize;	//最小可写尺寸
	slave->mtd.writebufsize = master->writebufsize;	//写缓冲区尺寸
	slave->mtd.oobsize = master->oobsize;	//oob区大小
	slave->mtd.oobavail = master->oobavail;	//oob区可用大小
	slave->mtd.subpage_sft = master->subpage_sft;
	slave->mtd.name = name;	//名字
	slave->mtd.owner = master->owner;	//模块所有者
	slave->mtd.backing_dev_info = master->backing_dev_info;
	slave->mtd.dev.parent = master->dev.parent;	//mtd设备父设备
	slave->mtd.read = part_read;	//读方法
	slave->mtd.write = part_write;	//写方法
	if (master->panic_write)
		slave->mtd.panic_write = part_panic_write;
	if (master->point && master->unpoint) {
		slave->mtd.point = part_point;
		slave->mtd.unpoint = part_unpoint;
	}
	if (master->get_unmapped_area)
		slave->mtd.get_unmapped_area = part_get_unmapped_area;
	if (master->read_oob)	//读oob区
		slave->mtd.read_oob = part_read_oob;
	if (master->write_oob)	//写oob区
		slave->mtd.write_oob = part_write_oob;
	if (master->read_user_prot_reg)
		slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
	if (master->read_fact_prot_reg)
		slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
	if (master->write_user_prot_reg)
		slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
	if (master->lock_user_prot_reg)
		slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
	if (master->get_user_prot_info)
		slave->mtd.get_user_prot_info = part_get_user_prot_info;
	if (master->get_fact_prot_info)
		slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
	if (master->sync)
		slave->mtd.sync = part_sync;
	if (!partno && !master->dev.class && master->suspend && master->resume) {
			slave->mtd.suspend = part_suspend;
			slave->mtd.resume = part_resume;
	}
	if (master->writev)
		slave->mtd.writev = part_writev;
	if (master->lock)
		slave->mtd.lock = part_lock;
	if (master->unlock)
		slave->mtd.unlock = part_unlock;
	if (master->is_locked)
		slave->mtd.is_locked = part_is_locked;
	if (master->block_isbad)
		slave->mtd.block_isbad = part_block_isbad;
	if (master->block_markbad)
		slave->mtd.block_markbad = part_block_markbad;
	slave->mtd.erase = part_erase;	//擦除方法
	slave->master = master;
	slave->offset = part->offset;
	if (slave->offset == MTDPART_OFS_APPEND)
		slave->offset = cur_offset;
	if (slave->offset == MTDPART_OFS_NXTBLK) {
		slave->offset = cur_offset;
		if (mtd_mod_by_eb(cur_offset, master) != 0) {
			slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
			printk(KERN_NOTICE "Moving partition %d:0x%012llx -> 0x%012llx
", 
				partno,(unsigned long long)cur_offset, (unsigned long long)slave->offset);
		}
	}
	if (slave->mtd.size == MTDPART_SIZ_FULL)
		slave->mtd.size = master->size - slave->offset;
	printk(KERN_NOTICE "0x%012llx-0x%012llx : "%s"
", 
		(unsigned long long)slave->offset,(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
	if (slave->offset >= master->size) {
		slave->offset = 0;
		slave->mtd.size = 0;
		//printk(KERN_ERR"mtd: partition "%s" is out of reach -- disabled
",part->name);
		goto out_register;
	}
	if (slave->offset + slave->mtd.size > master->size) {
		slave->mtd.size = master->size - slave->offset;
		printk(KERN_WARNING"mtd: partition "%s" extends beyond the end of device "%s" -- size truncated to %#llx
",
			part->name, master->name, (unsigned long long)slave->mtd.size);
	}
	if (master->numeraseregions > 1) {
		int i, max = master->numeraseregions;
		u64 end = slave->offset + slave->mtd.size;
		struct mtd_erase_region_info *regions = master->eraseregions;
		for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
			;
		if (i > 0)
			i--;
		for (; i < max && regions[i].offset < end; i++) {
			if (slave->mtd.erasesize < regions[i].erasesize) {
				slave->mtd.erasesize = regions[i].erasesize;
			}
		}
		BUG_ON(slave->mtd.erasesize == 0);
	} 
	else {
		slave->mtd.erasesize = master->erasesize;
	}
	if ((slave->mtd.flags & MTD_WRITEABLE) && mtd_mod_by_eb(slave->offset, &slave->mtd)) {
		slave->mtd.flags &= ~MTD_WRITEABLE;
		printk(KERN_WARNING"mtd: partition "%s" doesn't start on an erase block boundary -- force read-only
",part->name);
	}
	if ((slave->mtd.flags & MTD_WRITEABLE) && mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
		slave->mtd.flags &= ~MTD_WRITEABLE;
		printk(KERN_WARNING"mtd: partition "%s" doesn't end on an erase block -- force read-only
",part->name);
	}
	slave->mtd.ecclayout = master->ecclayout;	//ecc布局
	if (master->block_isbad) {
		uint64_t offs = 0;
		while (offs < slave->mtd.size) {
			if (master->block_isbad(master,offs + slave->offset))
				slave->mtd.ecc_stats.badblocks++;
			offs += slave->mtd.erasesize;
		}
	}
out_register:
	return slave;
}

分配mtd_part(slave)内存,并继承mtd_info(master)的一些属性
3.添加mtd设备

int add_mtd_device(struct mtd_info *mtd)
{
	struct mtd_notifier *not;
	int i, error;
	if (!mtd->backing_dev_info) {	//映射性能
		switch (mtd->type) {	//根据设备类型设置映射性能
		case MTD_RAM:	//RAM设备
			mtd->backing_dev_info = &mtd_bdi_rw_mappable;
			break;
		case MTD_ROM:	//ROM设备
			mtd->backing_dev_info = &mtd_bdi_ro_mappable;
			break;
		default:		//默认设备
			mtd->backing_dev_info = &mtd_bdi_unmappable;
			break;
		}
	}
	BUG_ON(mtd->writesize == 0);
	mutex_lock(&mtd_table_mutex);
	do {
		if (!idr_pre_get(&mtd_idr, GFP_KERNEL))
			goto fail_locked;
		error = idr_get_new(&mtd_idr, mtd, &i);	//idr机制添加新叶子到mtd_idr二叉树下
	} while (error == -EAGAIN);
	if (error)
		goto fail_locked;
	mtd->index = i;	//索引值(次设备号)
	mtd->usecount = 0;	//使用计数
	if (is_power_of_2(mtd->erasesize))	//擦除数据大小
		mtd->erasesize_shift = ffs(mtd->erasesize) - 1;	//擦除数据偏移量
	else
		mtd->erasesize_shift = 0;
	if (is_power_of_2(mtd->writesize))	//写入数据大小
		mtd->writesize_shift = ffs(mtd->writesize) - 1;	//写入数据偏移量
	else
		mtd->writesize_shift = 0;
	mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;	//擦除数据大小掩码
	mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;	//写入数据大小掩码
	if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) {
		if (mtd->unlock(mtd, 0, mtd->size))
			printk(KERN_WARNING"%s: unlock failed, writes may not work
",mtd->name);
	}
	mtd->dev.type = &mtd_devtype;	//mtd设备设备文件的设备类型
	mtd->dev.class = &mtd_class;	//mtd设备设备文件的设备类
	mtd->dev.devt = MTD_DEVT(i);	//获取mtd设备设备文件设备号
	dev_set_name(&mtd->dev, "mtd%d", i);	//设置mtd设备设备文件名
	dev_set_drvdata(&mtd->dev, mtd);	//设置mtd设备文件的驱动数据
	if (device_register(&mtd->dev) != 0)	//注册mtd设备设备文件
		goto fail_added;
	if (MTD_DEVT(i))
		device_create(&mtd_class, mtd->dev.parent,MTD_DEVT(i) + 1,NULL, "mtd%dro", i);	//创建mtd设备设备文件(只读)
	DEBUG(0, "mtd: Giving out device %d to %s
", i, mtd->name);
	list_for_each_entry(not, &mtd_notifiers, list)	//遍历全局mtd_notifiers链表查找对应的mtd_notifier对象
		not->add(mtd);	//调用mtd_notifier对象的add方法
	mutex_unlock(&mtd_table_mutex);
	__module_get(THIS_MODULE);
	return 0;
fail_added:
	idr_remove(&mtd_idr, i);
fail_locked:
	mutex_unlock(&mtd_table_mutex);
	return 1;
}

利用idr机制将添加的mtd设备指针挂在mtd_idr 32叉树下
指定设备类,并创建对应的字符设备,可读写"/dev/mtd%d",只读"/dev/mtd%dro"
遍历全局mtd_notifiers通知者链表,调用其add方法blktrans_notify_add/mtdchar_notify_add

三.MTD字符设备
1.初始化
模块入口module_init(init_mtdchar);

static int __init init_mtdchar(void)
{
	int ret;
	ret = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS,"mtd", &mtd_fops);	//注册字符设备,捆绑mtd_fops
	if (ret < 0) {
		pr_notice("Can't allocate major number %d for Memory Technology Devices.
", MTD_CHAR_MAJOR);
		return ret;
	}
	ret = register_filesystem(&mtd_inodefs_type);	//注册mtd_inodefs_type文件系统
	if (ret) {
		pr_notice("Can't register mtd_inodefs filesystem: %d
", ret);
		goto err_unregister_chdev;
	}
	mtd_inode_mnt = kern_mount(&mtd_inodefs_type);	//挂载mtd_inodefs_type文件系统
	if (IS_ERR(mtd_inode_mnt)) {
		ret = PTR_ERR(mtd_inode_mnt);
		pr_notice("Error mounting mtd_inodefs filesystem: %d
", ret);
		goto err_unregister_filesystem;
	}
	register_mtd_user(&mtdchar_notifier);	//-->五.注册通知者
	return ret;
err_unregister_filesystem:
	unregister_filesystem(&mtd_inodefs_type);
err_unregister_chdev:
	__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
	return ret;
}

2.字符设备操作函数集

static const struct file_operations mtd_fops = {
	.owner		= THIS_MODULE,
	.llseek		= mtd_lseek,
	.read		= mtd_read,
	.write		= mtd_write,
	.unlocked_ioctl	= mtd_unlocked_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= mtd_compat_ioctl,
#endif
	.open		= mtd_open,
	.release	= mtd_close,
	.mmap		= mtd_mmap,
#ifndef CONFIG_MMU
	.get_unmapped_area = mtd_get_unmapped_area,
#endif
};

四.MTD块设备
1.初始化
模块入口
module_init(init_mtdblock);
module_init(mtdblock_init); //只读

static int __init init_mtdblock(void)
{
	mutex_init(&mtdblks_lock);
	return register_mtd_blktrans(&mtdblock_tr);	//注册mtd操作结构体
}

static int __init mtdblock_init(void)	//只读
{
	return register_mtd_blktrans(&mtdblock_tr);	//注册mtd操作结构体
}

2.MTD操作结构体

struct mtd_blktrans_ops {
	char *name;	//名字
	int major;	//主设备号
	int part_bits;
	int blksize;
	int blkshift;
	int (*readsect)(struct mtd_blktrans_dev *dev,unsigned long block, char *buffer);	//读section
	int (*writesect)(struct mtd_blktrans_dev *dev,unsigned long block, char *buffer);	//写section
	int (*discard)(struct mtd_blktrans_dev *dev,unsigned long block, unsigned nr_blocks);
	int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo);
	int (*flush)(struct mtd_blktrans_dev *dev);
	int (*open)(struct mtd_blktrans_dev *dev);	//打开方法
	int (*release)(struct mtd_blktrans_dev *dev);	//释放方法
	void (*add_mtd)(struct mtd_blktrans_ops *tr, struct mtd_info *mtd);	//添加mtd分区
	void (*remove_dev)(struct mtd_blktrans_dev *dev);	//移除mtd分区
	struct list_head devs;
	struct list_head list;	//链表 添加到blktrans_majors
	struct module *owner;	//模块所有者
};

MTD操作结构体对象

static struct mtd_blktrans_ops mtdblock_tr = {
	.name		= "mtdblock",
	.major		= 31,
	.part_bits	= 0,
	.blksize 	= 512,
	.open		= mtdblock_open,
	.flush		= mtdblock_flush,
	.release	= mtdblock_release,
	.readsect	= mtdblock_readsect,
	.writesect	= mtdblock_writesect,
	.add_mtd	= mtdblock_add_mtd,
	.remove_dev	= mtdblock_remove_dev,
	.owner		= THIS_MODULE,
};
static struct mtd_blktrans_ops mtdblock_tr = {	//只读
	.name		= "mtdblock",
	.major		= 31,
	.part_bits	= 0,
	.blksize 	= 512,
	.readsect	= mtdblock_readsect,
	.writesect	= mtdblock_writesect,
	.add_mtd	= mtdblock_add_mtd,
	.remove_dev	= mtdblock_remove_dev,
	.owner		= THIS_MODULE,
};

3.注册mtd操作结构体

int register_mtd_blktrans(struct mtd_blktrans_ops *tr)	//注册mtd操作结构体
{
	struct mtd_info *mtd;
	int ret;
	if (!blktrans_notifier.list.next)
		register_mtd_user(&blktrans_notifier);	//-->五.注册通知者
	mutex_lock(&mtd_table_mutex);
	ret = register_blkdev(tr->major, tr->name);	//注册块设备驱动
	if (ret < 0) {
		printk(KERN_WARNING "Unable to register %s block device on major %d: %d
",tr->name, tr->major, ret);
		mutex_unlock(&mtd_table_mutex);
		return ret;
	}
	if (ret)
		tr->major = ret;	//设置主设备号
	tr->blkshift = ffs(tr->blksize) - 1;	//获取mtd设备偏移值
	INIT_LIST_HEAD(&tr->devs);	//设备mtd设备链表头
	list_add(&tr->list, &blktrans_majors);	//添加到全局blktrans_majors链表(MTD设备主分区)
	mtd_for_each_device(mtd)	//遍历mtd_idr idr机制32叉树 查找添加到该树下的节点对应的mtd_info(add_mtd_device函数会添加节点)
		if (mtd->type != MTD_ABSENT)	//一般都会为真 nand--MTD_NANDFLASH nor--NORFLASH...
			tr->add_mtd(tr, mtd);	//mtdblock_add_mtd
	mutex_unlock(&mtd_table_mutex);
	return 0;
}

五.注册通知者
1.注册通知者

void register_mtd_user (struct mtd_notifier *new)
{
	struct mtd_info *mtd;
	mutex_lock(&mtd_table_mutex);
	list_add(&new->list, &mtd_notifiers);	//添加到全局mtd_notifier通知者链表
 	__module_get(THIS_MODULE);
	mtd_for_each_device(mtd)
		new->add(mtd);	//-->4.通知者add方法
	mutex_unlock(&mtd_table_mutex);
}

2.字符设备通知者

static struct mtd_notifier mtdchar_notifier = {
	.add = mtdchar_notify_add,
	.remove = mtdchar_notify_remove,
};

3.块设备通知者

static struct mtd_notifier blktrans_notifier = {	//mtd_notifiers
	.add = blktrans_notify_add,
	.remove = blktrans_notify_remove,
};

4.通知者add方法
对于字符设备add方法为空操作
对于块设备add方法为blktrans_notify_add

static void blktrans_notify_add(struct mtd_info *mtd)
{
	struct mtd_blktrans_ops *tr;
	if (mtd->type == MTD_ABSENT)
		return;
	list_for_each_entry(tr, &blktrans_majors, list)	//遍历全局blktrans_majors链表
		tr->add_mtd(tr, mtd);	//调用MTD操作结构体对象的add_mtd方法
}

5.add_mtd方法
5.1对于可读写块设备
1.核心结构体

struct mtdblk_dev {
	struct mtd_blktrans_dev mbd;	//mtd_blktrans_dev设备
	int count;
	struct mutex cache_mutex;
	unsigned char *cache_data;
	unsigned long cache_offset;
	unsigned int cache_size;
	enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
};

2.add_mtd方法--mtdblock_add_mtd

static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
{
	struct mtdblk_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return;
	dev->mbd.mtd = mtd;	//mtd分区对象
	dev->mbd.devnum = mtd->index;	//mtd分区编号
	dev->mbd.size = mtd->size >> 9;	//
	dev->mbd.tr = tr;	//mtd_blktrans_ops
	if (!(mtd->flags & MTD_WRITEABLE))
		dev->mbd.readonly = 1;
	if (add_mtd_blktrans_dev(&dev->mbd))	//-->5.3 添加mtd_blktrans_dev设备
		kfree(dev);
}

5.2对于只读块设备
1.核心结构体

struct mtd_blktrans_dev {
	struct mtd_blktrans_ops *tr;	//
	struct list_head list;
	struct mtd_info *mtd;	//mtd设备
	struct mutex lock;
	int devnum;
	unsigned long size;
	int readonly;	//只读标志
	int open;    //打开标记
	struct kref ref;
	struct gendisk *disk;	//磁盘结构体
	struct attribute_group *disk_attributes;
	struct task_struct *thread;	//任务结构体
	struct request_queue *rq;	//请求队列
	spinlock_t queue_lock;
	void *priv;
};

2.add_mtd方法--mtdblock_add_mtd

static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
{
	struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return;
	dev->mtd = mtd;	//mtd_info对象
	dev->devnum = mtd->index;	//设备编号
	dev->size = mtd->size >> 9;
	dev->tr = tr;	//mtd_blktrans_ops
	dev->readonly = 1;	//只读标志
	if (add_mtd_blktrans_dev(dev))	//-->5.3 添加mtd_blktrans_dev设备
		kfree(dev);
}

5.3 添加mtd_blktrans_dev设备

int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
{
	struct mtd_blktrans_ops *tr = new->tr;	//获取mtd_blktrans_ops对象
	struct mtd_blktrans_dev *d;
	int last_devnum = -1;
	struct gendisk *gd;
	int ret;
	if (mutex_trylock(&mtd_table_mutex)) {
		mutex_unlock(&mtd_table_mutex);
		BUG();
	}
	mutex_lock(&blktrans_ref_mutex);
	list_for_each_entry(d, &tr->devs, list) {	//遍历mtd_blktrans_ops对象的devs链表
		if (new->devnum == -1) {
			if (d->devnum != last_devnum+1) {
				new->devnum = last_devnum+1;
				list_add_tail(&new->list, &d->list);
				goto added;
			}
		} 
		else if (d->devnum == new->devnum) {
			mutex_unlock(&blktrans_ref_mutex);
			return -EBUSY;
		} 
		else if (d->devnum > new->devnum) {
			list_add_tail(&new->list, &d->list);
			goto added;
		}
		last_devnum = d->devnum;
	}
	ret = -EBUSY;
	if (new->devnum == -1)
		new->devnum = last_devnum+1;
	if (new->devnum > (MINORMASK >> tr->part_bits) || (tr->part_bits && new->devnum >= 27 * 26)) {
		mutex_unlock(&blktrans_ref_mutex);
		goto error1;
	}
	list_add_tail(&new->list, &tr->devs);
 added:
	mutex_unlock(&blktrans_ref_mutex);
	mutex_init(&new->lock);
	kref_init(&new->ref);
	if (!tr->writesect)
		new->readonly = 1;
	ret = -ENOMEM;
	gd = alloc_disk(1 << tr->part_bits);	//分配gendisk
	if (!gd)
		goto error2;
	new->disk = gd;	//指定gendisk
	gd->private_data = new;
	gd->major = tr->major;	//设置主设备号
	gd->first_minor = (new->devnum) << tr->part_bits;
	gd->fops = &mtd_blktrans_ops;	//设置操作函数集
	if (tr->part_bits)
		if (new->devnum < 26)
			snprintf(gd->disk_name, sizeof(gd->disk_name),"%s%c", tr->name, 'a' + new->devnum);
		else
			snprintf(gd->disk_name, sizeof(gd->disk_name),"%s%c%c", tr->name,'a' - 1 + new->devnum / 26,'a' + new->devnum % 26);
	else
		snprintf(gd->disk_name, sizeof(gd->disk_name),"%s%d", tr->name, new->devnum);
	set_capacity(gd, (new->size * tr->blksize) >> 9);
	/* Create the request queue */
	spin_lock_init(&new->queue_lock);
	new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock);	//初始化请求队列
	if (!new->rq)
		goto error3;
	new->rq->queuedata = new;
	blk_queue_logical_block_size(new->rq, tr->blksize);
	if (tr->discard)
		queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,new->rq);
	gd->queue = new->rq;
	new->thread = kthread_run(mtd_blktrans_thread, new,"%s%d", tr->name, new->mtd->index);	//运行mtd_blktrans_thread线程
	if (IS_ERR(new->thread)) {
		ret = PTR_ERR(new->thread);
		goto error4;
	}
	gd->driverfs_dev = &new->mtd->dev;
	if (new->readonly)
		set_disk_ro(gd, 1);
	add_disk(gd);	//添加gendisk
	if (new->disk_attributes) {
		ret = sysfs_create_group(&disk_to_dev(gd)->kobj,new->disk_attributes);
		WARN_ON(ret);
	}
	return 0;
error4:
	blk_cleanup_queue(new->rq);
error3:
	put_disk(new->disk);
error2:
	list_del(&new->list);
error1:
	kfree(new);
	return ret;
}

5.4 mtd块设备操作函数集

static const struct block_device_operations mtd_blktrans_ops = {
	.owner		= THIS_MODULE,
	.open		= blktrans_open,
	.release	= blktrans_release,
	.ioctl		= blktrans_ioctl,
	.getgeo		= blktrans_getgeo,
};










 


















 

原文地址:https://www.cnblogs.com/jiangu66/p/3177879.html