在如今嵌入式盛行的时代,智能机泛滥的时期,闪存的使用必不可少,nor flash 、nand flash等. 在嵌入式开发中当然也少不了flash的身影,常用的有启动的时候把启动代码烧录到nor flash,而其他放到nand flash中,等系统启动后加载到内存中运行. 关于flash的一些硬件特性这里并不会详细介绍,自己可以通过网络查询或者参考相关硬件手册. 这里主要说下基于mtd架构的flash框架是如何工作的,以及有了这个mtd,我们驱动减少了多少工作量.
参考:嵌入式linux开发完全手册 linux驱动开发详解 nand flash硬件手册 linux相关代码.
参考内核版本:2.6.32.60
下面就先上个图 ,关于mtd框架的层次结构:
画的有点拙略^^,从图中我们可以看出mtd大致分四个层次:
1.mtd设备(节点)层
2.mtd原始设备层
3.mtd 通用驱动层
4. flash 特定硬件驱动层
有了这个层次的概念,那么我们就从上到下来分析下mtd到底是如何和真正的驱动结合起来而其作用的呢?
我们来说明下挂着文件系统:
mount -t yaffs2 /dev/mtdblock6 /mnt/nandfl
一般情况下我们在/dev/目录下会看到类似 /dev/mtd0 /dev/mtd1 /dev/mtdX ,/dev/mtdblock0 /dev/mtdblock1 /dev/mtdblockX等
这些设备文件就是供我们用户空间程序调用而操作真正的flash.
<1> mtd 块设备层 (drivers/mtd/mtdblock.c )
-
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 int __init init_mtdblock(void)
-
{
-
mutex_init(&mtdblks_lock);
-
-
return register_mtd_blktrans(&mtdblock_tr);
-
}
-
-
static void __exit cleanup_mtdblock(void)
-
{
-
deregister_mtd_blktrans(&mtdblock_tr);
-
}
-
-
module_init(init_mtdblock);
-
module_exit(cleanup_mtdblock);
看代码的结构很简单,就是调用register_mtd_blktrans注册了struct mtd_blktrans_ops这样一个结构体.
这里我们不在说明.open的初始化.而是看注册函数:
-
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)
-
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: %dn",
-
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;
-
}
register_blkdev注册一个块设备到设备模型中.
list_add(&tr->list, &blktrans_majors);与通知链有关:
-
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);
-
}
而关于承上启下的关键部分是:
for (i=0; i
if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)
tr->add_mtd(tr, mtd_table[i]);
}
mtd_table在mtd_core.c定义 :
-
/* These are exported solely for the purpose of mtd_blkdevs.c. You
-
should not use them for _anything_ else */
-
DEFINE_MUTEX(mtd_table_mutex);
-
struct mtd_info *mtd_table[MAX_MTD_DEVICES];
而关于tr->add_mtd做了什么工作呢?
mtdblock_add_mtd函数用于为每一个分区建立mtdblock接口.
而在mtdcore.c中的函数add_mtd_device中它是向mtd_table添加我们初始化的分区和设备.
-
/**
-
* add_mtd_device - register an MTD device
-
* @mtd: pointer to new MTD device info structure
-
*
-
* Add a device to the list of MTD devices present in the system, and
-
* notify each currently active MTD 'user' of its arrival. Returns
-
* zero on success or 1 on failure, which currently will only happen
-
* if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16)
-
* or there's a sysfs error.
-
*/
-
-
int add_mtd_device(struct mtd_info *mtd)
-
{
-
int i;
-
-
if (!mtd->backing_dev_info) {
-
switch (mtd->type) {
-
case MTD_RAM:
-
mtd->backing_dev_info = &mtd_bdi_rw_mappable;
-
break;
-
case MTD_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);
-
-
for (i=0; i < MAX_MTD_DEVICES; i++)
-
if (!mtd_table[i]) {
-
struct mtd_notifier *not;
-
-
mtd_table[i] = mtd;
-
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;
-
-
/* Some chips always power up locked. Unlock them now */
-
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 workn",
-
mtd->name);
-
}
-
-
/* Caller should have set dev.parent to match the
-
* physical device.
-
*/
-
mtd->dev.type = &mtd_devtype;
-
mtd->dev.class = &mtd_class;
-
mtd->dev.devt = MTD_DEVT(i);
-
dev_set_name(&mtd->dev, "mtd%d", i);
-
dev_set_drvdata(&mtd->dev, mtd);
-
if (device_register(&mtd->dev) != 0) {
-
mtd_table[i] = NULL;
-
break;
-
}
-
-
if (MTD_DEVT(i))
-
device_create(&mtd_class, mtd->dev.parent,
-
MTD_DEVT(i) + 1,
-
NULL, "mtd%dro", i);
-
-
DEBUG(0, "mtd: Giving out device %d to %sn",i, mtd->name);
-
/* No need to get a refcount on the module containing
-
the notifier, since we hold the mtd_table_mutex */
-
list_for_each_entry(not, &mtd_notifiers, list)
-
not->add(mtd);
-
-
mutex_unlock(&mtd_table_mutex);
-
/* We _know_ we aren't being removed, because
-
our caller is still holding us here. So none
-
of this try_ nonsense, and no bitching about it
-
either. :) */
-
__module_get(THIS_MODULE);
-
return 0;
-
}
-
-
mutex_unlock(&mtd_table_mutex);
-
return 1;
-
}
我们下面看一个简单的查看mtd_table的例子:
#cat /proc/mtd
dev: size erasesize name
mtd0: 00200000 00020000 "a"
mtd1: 01000000 00020000 "b"
mtd2: 04000000 00020000 "c"
mtd3: 04000000 00020000 "d"
mtd4: 06e00000 00020000 "f"
这个proc在哪里注册呢? 我们看mtdcore.c中 init_mtd:
-
/*====================================================================*/
-
/* Init code */
-
-
static int __init init_mtd(void)
-
{
-
int ret;
-
ret = class_register(&mtd_class);
-
-
if (ret) {
-
pr_err("Error registering mtd class: %dn", ret);
-
return ret;
-
}
-
#ifdef CONFIG_PROC_FS
-
if ((proc_mtd = create_proc_entry( "mtd", 0, NULL )))
-
proc_mtd->read_proc = mtd_read_proc;
-
#endif /* CONFIG_PROC_FS */
-
return 0;
-
}
其实我们应该明白mtd_table在实际应用中扮演的角色了.
那么关于mtd字符设备层我们这里不在多说,它也是最终会操作mtd_table.
我们可以看mtdchar.c 我们看mtd_open中的 mtd = get_mtd_device(NULL, devnum); 它和mtdblock操作相同的mtd_table[*].
下面我们分析mtd原始设备层:
<2>mtd原始设备层
mtd设备原始层 mtdcore.c mtdpart.c 等
向驱动提供的接口:
add_mtd_device
add_mtd_partitions
...
我们只需要分析它们在驱动中哪里调用了就行.
第三部分是mtd通用驱动层
<3>mtd通用驱动层
这里我觉得直接看驱动代码来分析这个两个层的调用会更清晰一些.当然网上有很多拿2410 nand驱动例子说事的,也是不错的. 具体代码实例分析见下篇.
阅读(11501) | 评论(1) | 转发(5) |