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