Chinaunix首页 | 论坛 | 博客
  • 博客访问: 165169
  • 博文数量: 56
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 14
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-24 17:36
文章分类

全部博文(56)

文章存档

2016年(43)

2015年(9)

2014年(2)

2013年(2)

我的朋友

分类: LINUX

2016-06-02 14:08:52

     在如今嵌入式盛行的时代,智能机泛滥的时期,闪存的使用必不可少,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  )  

点击(此处)折叠或打开

  1. static struct mtd_blktrans_ops mtdblock_tr = {
  2.     .name        = "mtdblock",
  3.     .major        = 31,
  4.     .part_bits    = 0,
  5.     .blksize     = 512,
  6.     .open        = mtdblock_open,
  7.     .flush        = mtdblock_flush,
  8.     .release    = mtdblock_release,
  9.     .readsect    = mtdblock_readsect,
  10.     .writesect    = mtdblock_writesect,
  11.     .add_mtd    = mtdblock_add_mtd,
  12.     .remove_dev    = mtdblock_remove_dev,
  13.     .owner        = THIS_MODULE,
  14. };

  15. static int __init init_mtdblock(void)
  16. {
  17.     mutex_init(&mtdblks_lock);

  18.     return register_mtd_blktrans(&mtdblock_tr);
  19. }

  20. static void __exit cleanup_mtdblock(void)
  21. {
  22.     deregister_mtd_blktrans(&mtdblock_tr);
  23. }

  24. module_init(init_mtdblock);
  25. module_exit(cleanup_mtdblock);
看代码的结构很简单,就是调用register_mtd_blktrans注册了struct mtd_blktrans_ops这样一个结构体.
这里我们不在说明.open的初始化.而是看注册函数:

点击(此处)折叠或打开

  1. int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
  2. {
  3.     struct mtd_info *mtd;
  4.     int ret;

  5.     /* Register the notifier if/when the first device type is
  6.      registered, to prevent the link/init ordering from fucking
  7.      us over. */
  8.     if (!blktrans_notifier.list.next)
  9.         register_mtd_user(&blktrans_notifier);


  10.     mutex_lock(&mtd_table_mutex);

  11.     ret = register_blkdev(tr->major, tr->name);
  12.     if (ret < 0) {
  13.         printk(KERN_WARNING "Unable to register %s block device on major %d: %dn",
  14.          tr->name, tr->major, ret);
  15.         mutex_unlock(&mtd_table_mutex);
  16.         return ret;
  17.     }

  18.     if (ret)
  19.         tr->major = ret;

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

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

  23.     mtd_for_each_device(mtd)
  24.         if (mtd->type != MTD_ABSENT)
  25.             tr->add_mtd(tr, mtd);

  26.     mutex_unlock(&mtd_table_mutex);
  27.     return 0;
  28. }
register_blkdev注册一个块设备到设备模型中.
list_add(&tr->list, &blktrans_majors);与通知链有关:


点击(此处)折叠或打开

  1. static void blktrans_notify_add(struct mtd_info *mtd)
  2. {
  3.     struct mtd_blktrans_ops *tr;

  4.     if (mtd->type == MTD_ABSENT)
  5.         return;

  6.     list_for_each_entry(tr, &blktrans_majors, list)
  7.         tr->add_mtd(tr, mtd);
  8. }
而关于承上启下的关键部分是:
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定义 :

点击(此处)折叠或打开

  1. /* These are exported solely for the purpose of mtd_blkdevs.c. You
  2.    should not use them for _anything_ else */
  3. DEFINE_MUTEX(mtd_table_mutex);
  4. struct mtd_info *mtd_table[MAX_MTD_DEVICES];
而关于tr->add_mtd做了什么工作呢?
mtdblock_add_mtd函数用于为每一个分区建立mtdblock接口.
而在mtdcore.c中的函数add_mtd_device中它是向mtd_table添加我们初始化的分区和设备.

点击(此处)折叠或打开

  1. /**
  2.  *    add_mtd_device - register an MTD device
  3.  *    @mtd: pointer to new MTD device info structure
  4.  *
  5.  *    Add a device to the list of MTD devices present in the system, and
  6.  *    notify each currently active MTD 'user' of its arrival. Returns
  7.  *    zero on success or 1 on failure, which currently will only happen
  8.  *    if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16)
  9.  *    or there's a sysfs error.
  10.  */

  11. int add_mtd_device(struct mtd_info *mtd)
  12. {
  13.     int i;

  14.     if (!mtd->backing_dev_info) {
  15.         switch (mtd->type) {
  16.         case MTD_RAM:
  17.             mtd->backing_dev_info = &mtd_bdi_rw_mappable;
  18.             break;
  19.         case MTD_ROM:
  20.             mtd->backing_dev_info = &mtd_bdi_ro_mappable;
  21.             break;
  22.         default:
  23.             mtd->backing_dev_info = &mtd_bdi_unmappable;
  24.             break;
  25.         }
  26.     }

  27.     BUG_ON(mtd->writesize == 0);
  28.     mutex_lock(&mtd_table_mutex);

  29.     for (i=0; i < MAX_MTD_DEVICES; i++)
  30.         if (!mtd_table[i]) {
  31.             struct mtd_notifier *not;

  32.             mtd_table[i] = mtd;
  33.             mtd->index = i;
  34.             mtd->usecount = 0;

  35.             if (is_power_of_2(mtd->erasesize))
  36.                 mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
  37.             else
  38.                 mtd->erasesize_shift = 0;

  39.             if (is_power_of_2(mtd->writesize))
  40.                 mtd->writesize_shift = ffs(mtd->writesize) - 1;
  41.             else
  42.                 mtd->writesize_shift = 0;

  43.             mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
  44.             mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;

  45.             /* Some chips always power up locked. Unlock them now */
  46.             if ((mtd->flags & MTD_WRITEABLE)
  47.              && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) {
  48.                 if (mtd->unlock(mtd, 0, mtd->size))
  49.                     printk(KERN_WARNING
  50.                      "%s: unlock failed, "
  51.                      "writes may not workn",
  52.                      mtd->name);
  53.             }

  54.             /* Caller should have set dev.parent to match the
  55.              * physical device.
  56.              */
  57.             mtd->dev.type = &mtd_devtype;
  58.             mtd->dev.class = &mtd_class;
  59.             mtd->dev.devt = MTD_DEVT(i);
  60.             dev_set_name(&mtd->dev, "mtd%d", i);
  61.             dev_set_drvdata(&mtd->dev, mtd);
  62.             if (device_register(&mtd->dev) != 0) {
  63.                 mtd_table[i] = NULL;
  64.                 break;
  65.             }

  66.             if (MTD_DEVT(i))
  67.                 device_create(&mtd_class, mtd->dev.parent,
  68.                         MTD_DEVT(i) + 1,
  69.                         NULL, "mtd%dro", i);

  70.             DEBUG(0, "mtd: Giving out device %d to %sn",i, mtd->name);
  71.             /* No need to get a refcount on the module containing
  72.              the notifier, since we hold the mtd_table_mutex */
  73.             list_for_each_entry(not, &mtd_notifiers, list)
  74.                 not->add(mtd);

  75.             mutex_unlock(&mtd_table_mutex);
  76.             /* We _know_ we aren't being removed, because
  77.              our caller is still holding us here. So none
  78.              of this try_ nonsense, and no bitching about it
  79.              either. :) */
  80.             __module_get(THIS_MODULE);
  81.             return 0;
  82.         }

  83.     mutex_unlock(&mtd_table_mutex);
  84.     return 1;
  85. }
我们下面看一个简单的查看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:

点击(此处)折叠或打开

  1. /*====================================================================*/
  2. /* Init code */

  3. static int __init init_mtd(void)
  4. {
  5.     int ret;
  6.     ret = class_register(&mtd_class);

  7.     if (ret) {
  8.         pr_err("Error registering mtd class: %dn", ret);
  9.         return ret;
  10.     }
  11. #ifdef CONFIG_PROC_FS
  12.     if ((proc_mtd = create_proc_entry( "mtd", 0, NULL )))
  13.         proc_mtd->read_proc = mtd_read_proc;
  14. #endif /* CONFIG_PROC_FS */
  15.     return 0;
  16. }
其实我们应该明白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驱动例子说事的,也是不错的. 具体代码实例分析见下篇.



阅读(1320) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~