zoukankan      html  css  js  c++  java
  • 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,
    };










     


















     

  • 相关阅读:
    人人都有数字替身的时代马上到来
    教你如何在linux下查看服务是否已经启动或者关闭
    提前了解2019年物联网发展的六大趋势
    本科理工男如何学习Linux
    linux常见命令ps的应用
    useradd 命令的常见用法
    简单聊聊Linux学习经历
    什么是公网IP、内网IP和NAT转换?
    远程获得的有趣的linux命令
    js练习题之查找数组中的位子
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3177879.html
Copyright © 2011-2022 走看看