Linux MTD子系统学习(四)

本文详细介绍了Linux中Memory Technology Device (MTD)的字符设备和块设备的注册过程,包括`init_mtd`、`init_mtdchar`、`init_mtdblock`等关键函数,以及`mtd_fops`和`mtd_blktrans_ops`操作集。通过这些函数,MTD设备被注册到系统中,使得用户可以进行读写操作。
摘要由CSDN通过智能技术生成

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;
}
————————————————
版权声明:本文为CSDN博主「楓潇潇」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013836909/article/details/93301692

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值