Linux MTD子系统学习(四)

Linux MTD子系统学习(四)

5 Linux mtd设备注册

5.1 mtd字符设备注册

5.1.1 init_mtd

源码:drivers/mtd/mtdcore.c

static int __init init_mtd(void)
{
	int ret;

	ret = class_register(&mtd_class);
	if (ret)
		goto err_reg;

	mtd_bdi = mtd_bdi_init("mtd");
	if (IS_ERR(mtd_bdi)) {
		ret = PTR_ERR(mtd_bdi);
		goto err_bdi;
	}

	proc_mtd = proc_create("mtd", 0, NULL, &mtd_proc_ops);

	ret = init_mtdchar();
	if (ret)
		goto out_procfs;

	dfs_dir_mtd = debugfs_create_dir("mtd", NULL);

	return 0;

out_procfs:
	if (proc_mtd)
		remove_proc_entry("mtd", NULL);
	bdi_put(mtd_bdi);
err_bdi:
	class_unregister(&mtd_class);
err_reg:
	pr_err("Error registering mtd class or bdi: %d\n", ret);
	return ret;
}

static void __exit cleanup_mtd(void)
{
	debugfs_remove_recursive(dfs_dir_mtd);
	cleanup_mtdchar();
	if (proc_mtd)
		remove_proc_entry("mtd", NULL);
	class_unregister(&mtd_class);
	bdi_put(mtd_bdi);
	idr_destroy(&mtd_idr);
}

module_init(init_mtd);
module_exit(cleanup_mtd);
5.1.2 init_mtdchar

源码:drivers/mtd/mtdchar.c

//mtd字符设备文件操作集
static const struct file_operations mtd_fops = {
	.owner		= THIS_MODULE,
	.llseek		= mtdchar_lseek,
	.read		= mtdchar_read,
	.write		= mtdchar_write,
	.unlocked_ioctl	= mtdchar_unlocked_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= mtdchar_compat_ioctl,
#endif
	.open		= mtdchar_open,
	.release	= mtdchar_close,
	.mmap		= mtdchar_mmap,
#ifndef CONFIG_MMU
	.get_unmapped_area = mtdchar_get_unmapped_area,
	.mmap_capabilities = mtdchar_mmap_capabilities,
#endif
};

int __init init_mtdchar(void)
{
	int ret;

//注册字符设备,major=90
//#define MTD_CHAR_MAJOR		90
	ret = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS,
				   "mtd", &mtd_fops);
	if (ret < 0) {
		pr_err("Can't allocate major number %d for MTD\n",
		       MTD_CHAR_MAJOR);
		return ret;
	}

	return ret;
}

5.2 mtdblock块设备注册

5.2.1 init_mtdblock

mtdblock块设备有两种类型,其相关的源码分别在:

ro:drivers/mtd/mtdblock_ro.c
rw:drivers/mtd/mtdblock.c

其基本方法是相同的,此处只介绍rw类型的。
源码:drivers/mtd/mtdblock.c

static struct mtd_blktrans_ops mtdblock_tr = {
	.name		= "mtdblock",
	.major		= MTD_BLOCK_MAJOR,
	.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 int __init init_mtdblock(void)
{
    //添加mtd块设备的注册方法到blktrans_notifier中
	return register_mtd_blktrans(&mtdblock_tr);
}

static void __exit cleanup_mtdblock(void)
{
	deregister_mtd_blktrans(&mtdblock_tr);
}
5.2.2 register_mtd_blktrans

源码:drivers/mtd/mtd_blkdevs.c

static void blktrans_notify_remove(struct mtd_info *mtd)
{
	struct mtd_blktrans_ops *tr;
	struct mtd_blktrans_dev *dev, *next;

	list_for_each_entry(tr, &blktrans_majors, list)
		list_for_each_entry_safe(dev, next, &tr->devs, list)
			if (dev->mtd == mtd)
				tr->remove_dev(dev);
}

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)
		tr->add_mtd(tr, mtd);
}

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

int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
	struct mtd_info *mtd;
	int ret;

	/* Register the notifier if/when the first device type is
	   registered, to prevent the link/init ordering from fucking
	   us over. */
	if (!blktrans_notifier.list.next)
		//注册mtd块设备的注册方法到mtd_notifiers链表中
register_mtd_user(&blktrans_notifier); 

	mutex_lock(&mtd_table_mutex);

	ret = register_blkdev(tr->major, tr->name);//注册块设备,主设备号为31
	if (ret < 0) {
		printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
		       tr->name, tr->major, ret);
		mutex_unlock(&mtd_table_mutex);
		return ret;
	}

	if (ret)
		tr->major = ret;

	tr->blkshift = ffs(tr->blksize) - 1;

	INIT_LIST_HEAD(&tr->devs);
	list_add(&tr->list, &blktrans_majors);

	mtd_for_each_device(mtd)
		if (mtd->type != MTD_ABSENT)
			tr->add_mtd(tr, mtd);

	mutex_unlock(&mtd_table_mutex);
	return 0;
}
5.2.3 mtdblock_add_mtd

该函数最终将会被add_mtd_device()调用,实现mtd块设备的注册,详细如下:
源码:drivers/mtd/mtdblock.c

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;

	if (!(mtd->flags & MTD_WRITEABLE))//读写标志
		dev->mbd.readonly = 1;

	if (add_mtd_blktrans_dev(&dev->mbd))//注册块设备
		kfree(dev);
}
5.2.4 add_mtd_blktrans_dev

源码:drivers/mtd/mtd_blkdevs.c

int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
{
	struct mtd_blktrans_ops *tr = new->tr;
	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();
	}

  /*这段代码只是检查一下mtd block层中次设备号有没有被分区出去 
    从前面的分析知道mtd block设备的主设备号为31,而mtd设备将加入 
    mtd_talbe时的index作为mtd block设备的次设备号,这里需要首先 
    检查一下这个次设备号是否被占用*/
	mutex_lock(&blktrans_ref_mutex);
	list_for_each_entry(d, &tr->devs, list) {
		if (new->devnum == -1) {
			/* Use first free number */
			if (d->devnum != last_devnum+1) {
				/* Found a free devnum. Plug it in here */
				new->devnum = last_devnum+1;
				list_add_tail(&new->list, &d->list);
				goto added;
			}
		} else if (d->devnum == new->devnum) {
			/* Required number taken */
			mutex_unlock(&blktrans_ref_mutex);
			return -EBUSY;
		} else if (d->devnum > new->devnum) {
			/* Required number was free */
			list_add_tail(&new->list, &d->list);
			goto added;
		}
		last_devnum = d->devnum;
	}

	ret = -EBUSY;
	if (new->devnum == -1)
		new->devnum = last_devnum+1;

	/* Check that the device and any partitions will get valid
	 * minor numbers and that the disk naming code below can cope
	 * with this number. */
	if (new->devnum > (MINORMASK >> tr->part_bits) ||
	    (tr->part_bits && new->devnum >= 27 * 26)) {
		mutex_unlock(&blktrans_ref_mutex);
		goto error1;
	}

/*分配次设备号成功,将mtd_blktrans_dev链接到 mtdblock_tr->devs上面*/
	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;

/*下面就是承上启下的作用了,在分配了mtd block设备后,这里又 
      要将mtd block设备抽象成一个block设备,将它注册到block层*/
	/* Create gendisk */
	ret = -ENOMEM;
	gd = alloc_disk(1 << tr->part_bits);//分配gendisk结构体空间
	if (!gd)
		goto error2;

    /*下面的处理就是分配一个gendisk数据结构,初始化这个数据结构, 
      将gendisk 添加注册到block层中*/ 
    /*下面的初始化需要注意,first_minor就是mtd block的次设备号 
      part_bits位 0,不允许在block设备上再有分区*/
	new->disk = gd;
	gd->private_data = new;
	gd->major = tr->major;//mtd块设备主设备号---31
	gd->first_minor = (new->devnum) << tr->part_bits;//mtd_table[]下标
	gd->fops = &mtd_block_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, ((u64)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;

	if (tr->flush)
		blk_queue_write_cache(new->rq, true, false);

	new->rq->queuedata = new;
	blk_queue_logical_block_size(new->rq, tr->blksize);

	blk_queue_bounce_limit(new->rq, BLK_BOUNCE_HIGH);
	queue_flag_set_unlocked(QUEUE_FLAG_NONROT, new->rq);
	queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, new->rq);

	if (tr->discard) {
		queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, new->rq);
		blk_queue_max_discard_sectors(new->rq, UINT_MAX);
	}

	gd->queue = new->rq;//该mtd设备的请求队列

	/* Create processing workqueue */
	new->wq = alloc_workqueue("%s%d", 0, 0,
				  tr->name, new->mtd->index);
	if (!new->wq)
		goto error4;
	INIT_WORK(&new->work, mtd_blktrans_work);

	if (new->readonly)
		set_disk_ro(gd, 1);

	device_add_disk(&new->mtd->dev, gd);

	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:
	return ret;
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值