前面几篇文章介绍了MTD 硬件驱动层的一些东西。逐渐往上面看就会看到MTD Block层。什么是MTD Block层,就是为Flash设备在内核中建立块设备结构,建立一种和磁盘等块设备一样的身份告诉内核块层和文件系统层。在这层往上面,Flash就和磁盘是一样一样的地位了。或许有人会说,在MTD Block和MTD Char之上还有MTD核心层吗?是的确实有MTD核心层,但是逻辑上MTD Block层上面就是内核的块层和文件系统,MTD核心层只是提供相应的服务,逻辑结构上并没有在这种抽象中。
掉书袋的东西扯了一通,我们快速进入正题中来。所谓的MTD Block层,肯定需要一个初始化吧,这是linux的套路,搞一个init就出来一套东东。
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,
};
|
Mtdblock_tr这个结构体是mtd block层中最关键的一个机构体,MTD Block之后的一系列重要操作都和这个结构体相关。
而下面register_mtd_blktrans函数就是mtd block层的初始化操作,这个函数没有太多复杂的操作简单总结就是:
1. 在block的工商局注册了一个31的major号商标
2. 初始化了一个块设备IO请求的处理队列,这个队列之后用来处理所有的MTD Block设备的IO请求。
3. 初始化了一个处理MTD Block层设备IO请求的内核进程
4. 初始化mtd_table数组,之后mtd设备的mtd_info结构体会被依照需要加入到这个数组中来管理。
int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
int ret, i;
/* 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)
register_mtd_user(&blktrans_notifier);
tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
if (!tr->blkcore_priv)
return -ENOMEM;
mutex_lock(&mtd_table_mutex);
/*抢先道block的工商局注册major号商标*/
ret = register_blkdev(tr->major, tr->name);
if (ret)
{
printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
tr->name, tr->major, ret);
kfree(tr->blkcore_priv);
mutex_unlock(&mtd_table_mutex);
return ret;
}
spin_lock_init(&tr->blkcore_priv->queue_lock);
/*初始化一个块设备的请求的调度队列,这个很重要,所有的mtd块设备
都会公用这个队列来完成IO操作,但是这里暂时不详细展开描述*/
tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
if (!tr->blkcore_priv->rq)
{
unregister_blkdev(tr->major, tr->name);
kfree(tr->blkcore_priv);
mutex_unlock(&mtd_table_mutex);
return -ENOMEM;
}
/*你才blksize是多少,512呵呵*/
tr->blkcore_priv->rq->queuedata = tr;
blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize);
if (tr->discard)
blk_queue_set_discard(tr->blkcore_priv->rq,
blktrans_discard_request);
tr->blkshift = ffs(tr->blksize) - 1;
/*初始化一个mtd block的内核进程,这个进程也是和处理mtd block
设备的IO请求有关系,这里不展开讨论,只有会在block层讨论
你在嵌入式linux下面使用ps命令,可以看到这个内核进程*/
tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
"%sd", tr->name);
if (IS_ERR(tr->blkcore_priv->thread))
{
blk_cleanup_queue(tr->blkcore_priv->rq);
unregister_blkdev(tr->major, tr->name);
kfree(tr->blkcore_priv);
mutex_unlock(&mtd_table_mutex);
return PTR_ERR(tr->blkcore_priv->thread);
}
INIT_LIST_HEAD(&tr->devs);
list_add(&tr->list, &blktrans_majors);
/*初始化mtd_table数组,mtd 设备的mtd_info结构体都会放到这个
上面去*/
for (i = 0; i < MAX_MTD_DEVICES; i++)
{
if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)
tr->add_mtd(tr, mtd_table[i]);
}
mutex_unlock(&mtd_table_mutex);
return 0;
}
|
上面说了MTD Block层的初始化,它的主要作用是将MTD 顶层设备包装成block设备。这里要插入一点就是,一般而言mtd底层将一个Flash设备就分成了不同的分区。每个分区抽象成一个mtd设备。每个分区的mtd设备注册到MTD Block层之后,该层将这个mtd设备认为是一个不能再分区的block设备了。
嵌入式设备中一般都定义了类似下面的Flash设备分区表
static struct mtd_partition qt2410_nand_part[] =
{
[0] = {
.name = "U-Boot",
.size = 0x30000,
.offset = 0,
},
[1] = {
.name = "U-Boot environment",
.offset = 0x30000,
.size = 0x4000,
},
[2] = {
.name = "kernel",
.offset = 0x34000,
.size = SZ_2M,
},
[3] = {
.name = "initrd",
.offset = 0x234000,
.size = SZ_4M,
},
[4] = {
.name = "jffs2",
.offset = 0x634000,
.size = 0x39cc000,
},
};
|
上面的这个分区表就是将一个底层的Flash设备分为两若干个分区设备。
add_one_partition函数会将上面的每一个分区建立一个mtd_part的结构体,并初始化mtd_part->mtd中的数据成员,每个分区在mtd底层驱动中抽象成了独立的mtd设备
对于这样的分区mtd设备,最后调用add_mtd_device函数加入到mtd内核层
而后,add_mtd_device函数调用mtdblock_add_mtd函数将mtd硬件设备抽象成mtd block设备。
在mtd block层内核使用blktrans_dev数据结构来描述一个mtd block设备
struct mtd_blktrans_dev
{
struct mtd_blktrans_ops *tr;
struct list_head list;
struct mtd_info *mtd;
struct mutex lock;
int devnum;
unsigned long size;
int readonly;
void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */
};
|
mtdblock_add_mtd函数就是简单的分配了一个mtd_blktrans_dev结构体,并做了相应的初始化,之后就调用了add_mtd_blktrans_dev函数将。好戏在这个add_mtd_blktrans_dev函数里面。
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;
dev->devnum = mtd->index;
dev->size = mtd->size >> 9;
dev->tr = tr;
if (!(mtd->flags & MTD_WRITEABLE))
dev->readonly = 1;
add_mtd_blktrans_dev(dev);
}
|
关于add_mtd_blktrans_dev的处理,下面代码中标注了详细的注释,这个函数基本上起到了承上启下的作用,将mtd block继续抽象成block设备,像block层进行了添加注册。
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;
if (mutex_trylock(&mtd_table_mutex))
{
mutex_unlock(&mtd_table_mutex);
BUG();
}
/*这段代码只是检查一下mtd block层中次设备号有没有被分区出去
从前面的分析知道mtd block设备的主设备号为31,而mtd设备将加入
mtd_talbe时的index作为mtd block设备的次设备号,这里需要首先
检查一下这个次设备号是否被占用*/
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 */
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;
}
if (new->devnum == -1)
new->devnum = last_devnum + 1;
if ((new->devnum << tr->part_bits) > 256)
{
return -EBUSY;
}
/*分配次设备号成功,将mtd_blktrans_dev链接到
mtdblock_tr->devs上面*/
list_add_tail(&new->list, &tr->devs);
added:
mutex_init(&new->lock);
if (!tr->writesect)
new->readonly = 1;
/*下面就是承上启下的作用了,在分配了mtd block设备后,这里又
要将mtd block设备抽象成一个block设备,将它注册到block层*/
gd = alloc_disk(1 << tr->part_bits);
if (!gd)
{
list_del(&new->list);
return -ENOMEM;
}
/*下面的处理就是分配一个gendisk数据结构,
初始化这个数据结构,
将gendisk 添加注册到block层中*/
/*下面的初始化需要注意,first_minor就是mtd block的次设备号
part_bits位 0,不允许在block设备上再有分区*/
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);
/* 2.5 has capacity in units of 512 bytes while still
having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */
set_capacity(gd, (new->size * tr->blksize) >> 9);
gd->private_data = new;
new->blkcore_priv = gd;
gd->queue = tr->blkcore_priv->rq;
if (new->readonly)
set_disk_ro(gd, 1);
add_disk(gd);
return 0;
}
|
阅读(1982) | 评论(0) | 转发(0) |