mtd nandflash 分析

来源:互联网 发布:ubuntu 备份系统 编辑:程序博客网 时间:2024/06/05 09:56

一、MTD 的概念和层次

MTD(memory technology device 存储 技术设备 ) 是用于访问 memory 设备( ROM 、 flash )的 Linux 的子系统。 MTD 的主要目的是为了使新的 memory 设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。 MTD 的所有源代码在 /drivers/mtd 子目录下 。[1]

传统上, UNIX 只认识块设备和字符设备。字符设备是类似键盘或者鼠标的这类设备,你必须从它读取当前数据,但是不可以定位也没有大小。块设备有固定的大小并且可以定位, 它们恰好组织成许多字节的块,通常为 512字节。

闪存既不满足块设备描述也不满足字符设备的描述。它们表现的类似块设备,但又有所不同。比如,块设备不区分写和擦除操作。因此,一种符合闪存特性的特殊设备类型诞生了, 就是 MTD 设备。所以 MTD 既不是块设备,也不是字符设备 。 [2]

关于 MTD 的层次,网络上有一张流传盛广的图片,如下所示,但是最初我看了这幅图根本是一点概念都没有的,不过通过看代码和网上查阅资料,知道了详细一点的分层结构,也纠正了一些前期对这张图的误解。

( 以下这部分纯属个人理解,如果有误,请高人拍砖 !)

为了方便理解,先声明两点:

1. xxx 层 (MTD 原始设备层, MTD 块设备层 ) ,实现封装的代码。

2. xxx 设备 (MTD 原始设备, MTD 块设备 ) ,是 xxx 层向下封装后呈现给上层的表象就 是一个 xxx 设备。

Flash 硬件驱动层:该层的基于特定处理器和特定 flash 芯片,这里以 pxa935 和Hynix  NAND 512MB 1.8V 16-bit 为例。使用类型为 nand_chip,   pxa3xx_nand_info, dfc_context, pxa3xx_bbm 这几个结构体来实现硬 件驱动。代码位于 drivers/mtd/nand 目录下。

MTD 原始设备层: 用类型 mtd_info 的结构体来描述 MTD 原始设备,该结构体中有一 个域会指向 Flash 硬件驱动层中所有使用的结构体 ( 串联形式,另外  pxa3xx_bbm 结构体是只在底层驱动中使用 ) 。

  NAND flash 在嵌入式系统中通常需要划分多个分区,系统没有运 行起来的时候分区表现为 mtd_partition 类型的结构体数组,该数组由 工程师自己决定。在系统初始化,确切的说是在nand 的驱动加载时 执行相应的 prob 函数时,会将上述数组中的每一个分区用类型为 mtd_part的结构体来描述。因为 mtd_part 结构体中内嵌了一个 mtd_info 的结构体,所以每一个分区在系统看来都是一个 MTD 原始 设备,另外 mtd_part 种还有一个 master 指针,指向描述整片 flash原 始设备的 mtd_info 结构体,所以这个描述整片 nand 的 mtd_info 结构 体也被叫做主分区。

  在 MTD 原始设备层和其上层 MTD 块设备层 (FTL) 活跃着一个牛 X 的指针数组 mtd_table,定义于文件 mtdcore.c 中,该数组就是所有 MTD 原始设备的指针列表 ( 当然有数量限制,这里限制在 32 范围内 ) 。 不过上面所说的主分区没有在 mtd_table 之列。

       如果你系统中有 2 片 nand flash ,每个有 8 个分区,那么系统中总 共存在有 18 个 mtd_info 结构体对象, mtd_table 数组中有 16 个指针 已经有归属。

本层和其上层 FTL 之间就全靠 mtd_table 数组和 mtd_notifiers 链表来         

联系了 ,至于如何联系的,下文再详细解释。

MTD 块设备层: 该层也叫flash 翻译层 (FTL) 。以前为了在 MTD 设备上使用某种传统 的文件系统, linux 系统中存在一个叫做 flash 翻译层 (FTL) ,该 FTL 是在 MTD 原始设备的基础上模拟出块设备,所以 FTL 以下的所有内 容呈现给上层的就是一个块设备。这样可以使用通用的块设备的接口 了。

这里也存在一个著名的结构体指针数组,定义于 mtdblock.c 文件中, 其中的每一个指针均指向一个 struct mtdblk_dev 的类型的对象,每一 个 struct mtdblk_dev 类型的对象都是一个MTD 块设备。

网上流传着说使用该 FTL 如何不好,这种观点其实是基于使用传统 文件系统存在的问题,现在有专门针对 nand flash 的 yaffs 日志型文 件系统了。所以那种掉电丢失数据的风险降低到了很小很小。

通用磁盘层:    再上层就是通用磁盘层了,其实每个分区在最后都是向通用磁盘层注 册成了一个 disk 来使用的,后面分析代码会看到这部分。当然对于 block 层的分析不在本文之中讨论。

本来最初的目的只是为了研究内核中nand的驱动,但好奇心太强,忍不住往上层追溯,然后再往下层跟踪。这个过程中,
 特别是在block层,yaffs文件系统和用户空间中相关的分析,其实仅仅只是找到了他们之间的关键联系点而已,没有深
 入详细分析。
 
 * linux2.6.29
 * pxa935
 * Hynix NAND 512MB 1.8V 16-bit
 * 李枝果/lizgo 2010-11-8 lizhiguo0532@163.com
 * 由于本人水平有限,望读者阅读时三思,同时也指正我的错误,谢谢!
 
 话说MTD子系统向上层提供了几种在用户空间可以直接使用的接口:Raw char device、Raw block device、FTL、NFTL、
 JFFS(2)。前文中主要讨论的yaffs文件系统没有包含在其中,因为yaffs是直接建立在MTD原始设备层之上的。在nand驱动
 注册的时候,会在probe函数中将nand的每个分区通过mtd块设备层、通用磁盘层、block层向内核注册成一个block设备。
 另外挂载yaffs文件系统的时候呢,就会通过设备节点找到block层中对应的block_device结构体,最后在填充超级块的时候
 直接通过主次设备号在mtd_table[]中找到对应的mtd_info结构体。而且在yaffs文件系统层封装的读写等函数中,也是将
 找到的mtd_info结构体传入,直接利用了mtd_info结构体中的相关读写函数指针来进行底层的读写。
 
 本文主要讨论的接口是Raw block device,也就是传说中的mtdblock,本文中称为mtdblock翻译层,
 主要集中在文件drivers/mtd/mtdblock.c
一、init_mtdblock()
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)
{
 return register_mtd_blktrans(&mtdblock_tr);
}
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);
 ret = register_blkdev(tr->major, tr->name); // 块设备注册,major_names
 // 此时可以在proc/devices中看到注册的该设备
 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);  // 初始化请求队列锁
 tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
 // 初始化请求队列头和安装请求处理函数mtd_blktrans_request()
 if (!tr->blkcore_priv->rq) {
  unregister_blkdev(tr->major, tr->name);
  kfree(tr->blkcore_priv);
  mutex_unlock(&mtd_table_mutex);
  return -ENOMEM;
 }
 tr->blkcore_priv->rq->queuedata = tr; // 保存该翻译层的操作集  , &mtdblock_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;
 tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
   "%sd", tr->name);      // 创建内核线程mtd_blktrans_thread
 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);
 // 所有翻译层通过blktrans_majors链表来链接在一起
 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]);
 }
 // 对mtd_table中的所有分区调用该翻译层操作集中的add_mtd函数
 mutex_unlock(&mtd_table_mutex);
 return 0;
}
 使用函数blk_init_queue初始化一个请求队列和安装一个请求处理函数mtd_blktrans_request():
static void mtd_blktrans_request(struct request_queue *rq)
 {
  struct mtd_blktrans_ops *tr = rq->queuedata;
  wake_up_process(tr->blkcore_priv->thread);  // 唤醒内核线程
 }
 接下来就创建一个内核线程:mtd_blktrans_thread()
static int mtd_blktrans_thread(void *arg)
{
 struct mtd_blktrans_ops *tr = arg;
 struct request_queue *rq = tr->blkcore_priv->rq;
 /* we might get involved when memory gets low, so use PF_MEMALLOC */
 current->flags |= PF_MEMALLOC;
 spin_lock_irq(rq->queue_lock);
 while (!kthread_should_stop()) {
  struct request *req;
  struct mtd_blktrans_dev *dev;
  int res = 0;
  req = elv_next_request(rq);  // 获取对列中第一个未完成的请求
  if (!req) {      // 如果请求为空将线程置于睡眠状态
   set_current_state(TASK_INTERRUPTIBLE);
   spin_unlock_irq(rq->queue_lock);
   schedule();
   spin_lock_irq(rq->queue_lock);
   continue;
  }
  
  /*
  一个请求队列管理着很多请求,但是每一个请求都只能针对一个块设备gendisk。
  所以每一个请求被创建出来后都会指向它的请求对象gendisk。
  这个指向关系在函数__make_request()->init_request_from_bio()->blk_rq_bio_prep()
  -> rq->rq_disk = bio->bi_bdev->bd_disk
  中建立。
  */ 
  
  dev = req->rq_disk->private_data;
  tr = dev->tr;
  spin_unlock_irq(rq->queue_lock);
  mutex_lock(&dev->lock);
  res = do_blktrans_request(tr, dev, req);
  /*
  在函数do_blktrans_request(tr, dev, req)中
  根据请求的数据传输方向来决定调用读或写函数进行数据传输
  tr->readsect(dev, block, buf)
  tr->writesect(dev, block, buf)
  */
  //如果res是0表示不能成功完成请求,为非0表示成功完成请求
  mutex_unlock(&dev->lock);
  spin_lock_irq(rq->queue_lock);
  end_request(req, res);
 }
 spin_unlock_irq(rq->queue_lock);
 return 0;
}
 
二、用户通知器
结构体定义:
struct mtd_notifier {
 void (*add)(struct mtd_info *mtd);
 void (*remove)(struct mtd_info *mtd);
 struct list_head list;
};
有两个方法和一个链表挂钩,参数均为mtd_info指针。
在drivers/mtd/mtd_blkdevs.c中定义了下面的块翻译层通知器
static struct mtd_notifier blktrans_notifier = {
 .add = blktrans_notify_add,
 .remove = blktrans_notify_remove,
};
该通知器被FTL、NFTL、mtdblock翻译层使用。
int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
...
/* 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); // 只有第一个翻译层注册的时候该函数才会调用
...
}
void register_mtd_user (struct mtd_notifier *new)
{
 int i;
 mutex_lock(&mtd_table_mutex);
 list_add(&new->list, &mtd_notifiers);
 // 将这个新的用户通知器添加到全局链表mtd_notifiers中(该链表中还可能存在其他用户通知器)
  __module_get(THIS_MODULE);// 增加模块引用计数
 for (i=0; i< MAX_MTD_DEVICES; i++)
  if (mtd_table[i])
   new->add(mtd_table[i]);
   // 对mtd_table中的所有分区调用该翻译层操作集中的add_mtd函数,第一次注册该工作在后面会重复再做一次
   // blktrans_notify_add()
 mutex_unlock(&mtd_table_mutex);
}
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);
  // blktrans_majors链表管理着所有的翻译层操作集结构体
  // 该处的意思是对于传入的同一个mtd_info结构体,所有的翻译层都会调用自己的
  // add_mtd函数(这些函数都不一样,对于mtdblock层该函数是mtdblock_add_mtd())
}
在系统启动的时候,register_mtd_blktrans(&mtdblock_tr)执行的时候,mtd_table数组中是空的,所以就不会执行到翻译层
的add_mtd函数上来,那么在又在什么时候调用了翻译层的add_mtd()函数了呢?请看下面
三、将分区向上层注册成block device。
 // pxa3xx_nand.c
 在注册nand驱动的时候:
 pxa3xx_nand_init()
 --> platform_driver_register()
   --> ...经过注册和设备匹配后调用probe()函数
     --> pxa3xx_nand_probe()
       --> ...
       --> add_mtd_partitions(monahans_mtd, pdata->parts, pdata->nr_parts)
         --> add_one_partition()
           --> add_mtd_device()
             --> list_for_each_entry(not, &mtd_notifiers, list)
                 not->add(mtd);
               // 这里就是调用mtd_notifiers链表中所有用户通知器的add函数,以mtdblock的用户通知
               // 器为例,那么就是调用函数 mtdblock_add_mtd()。这就是证实了当添加一个分区的时候
               // 用户通知器的add函数被调用,那么当移除分区的时候,remove函数就会被调用,只是
               // 我们这里没有移除的分区动作。
代码如下:
static int pxa3xx_nand_probe(struct platform_device *pdev)
{
 ...
 return add_mtd_partitions(monahans_mtd, pdata->parts, pdata->nr_parts);
 ...
}
int add_mtd_partitions(struct mtd_info *master,
         const struct mtd_partition *parts,
         int nbparts)
{
 struct mtd_part *slave;
 uint64_t cur_offset = 0;
 int i;
 printk(KERN_NOTICE "Creating %d MTD partitions on /"%s/":/n", nbparts, master->name);
 for (i = 0; i < nbparts; i++) {
  slave = add_one_partition(master, parts + i, i, cur_offset);
  if (!slave)
   return -ENOMEM;
  cur_offset = slave->offset + slave->mtd.size;
 }
 return 0;
}
static struct mtd_part *add_one_partition(struct mtd_info *master,
  const struct mtd_partition *part, int partno,
  uint64_t cur_offset)
{
 struct mtd_part *slave;
 /* allocate the partition structure */
 slave = kzalloc(sizeof(*slave), GFP_KERNEL);
 if (!slave) {
  printk(KERN_ERR"memory allocation error while creating partitions for /"%s/"/n",
   master->name);
  del_mtd_partitions(master);
  return NULL;
 }
 list_add(&slave->list, &mtd_partitions);
 // mtd_partitions 用于在MTD原始设备层统一管理所有分区信息
 // static LIST_HEAD(mtd_partitions) 本文件中定义
 /* set up the MTD object for this partition */
 slave->mtd.type = master->type;
 slave->mtd.flags = master->flags & ~part->mask_flags;
 slave->mtd.size = part->size;  // 分区大小
 slave->mtd.writesize = master->writesize;
 slave->mtd.oobsize = master->oobsize;
 slave->mtd.oobavail = master->oobavail;
 slave->mtd.subpage_sft = master->subpage_sft;
 slave->mtd.name = part->name;  // 分区名字
 slave->mtd.owner = master->owner;
 slave->mtd.read = part_read;  // 分区读写函数
 slave->mtd.write = part_write;
 if (master->panic_write)
  slave->mtd.panic_write = part_panic_write;
 if (master->point && master->unpoint) {
  slave->mtd.point = part_point;
  slave->mtd.unpoint = part_unpoint;
 }
 if (master->read_oob)
  slave->mtd.read_oob = part_read_oob;
 if (master->write_oob)
  slave->mtd.write_oob = part_write_oob;
 if (master->read_user_prot_reg)
  slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
 if (master->read_fact_prot_reg)
  slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
 if (master->write_user_prot_reg)
  slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
 if (master->lock_user_prot_reg)
  slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
 if (master->get_user_prot_info)
  slave->mtd.get_user_prot_info = part_get_user_prot_info;
 if (master->get_fact_prot_info)
  slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
 if (master->sync)
  slave->mtd.sync = part_sync;
 if (!partno && master->suspend && master->resume) {
   slave->mtd.suspend = part_suspend;
   slave->mtd.resume = part_resume;
 }
 if (master->writev)
  slave->mtd.writev = part_writev;
 if (master->lock)
  slave->mtd.lock = part_lock;
 if (master->unlock)
  slave->mtd.unlock = part_unlock;
 if (master->block_isbad)
  slave->mtd.block_isbad = part_block_isbad;
 if (master->block_markbad)
  slave->mtd.block_markbad = part_block_markbad;
 slave->mtd.erase = part_erase;
 slave->master = master;   // 该分区的主分区
 slave->offset = part->offset; // 该分区偏移
 slave->index = partno;   // 分区索引
 if (slave->offset == MTDPART_OFS_APPEND)
  slave->offset = cur_offset;
 if (slave->offset == MTDPART_OFS_NXTBLK) {
  slave->offset = cur_offset;
  if (mtd_mod_by_eb(cur_offset, master) != 0) {
   /* Round up to next erasesize */
   slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
   printk(KERN_NOTICE "Moving partition %d: "
          "0x%012llx -> 0x%012llx/n", partno,
          (unsigned long long)cur_offset, (unsigned long long)slave->offset);
  }
 }
 if (slave->mtd.size == MTDPART_SIZ_FULL)
  slave->mtd.size = master->size - slave->offset;
 printk(KERN_NOTICE "0x%012llx-0x%012llx : /"%s/"/n", (unsigned long long)slave->offset,
  (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
 // 打印分区表信息
 /* let's do some sanity checks */
 if (slave->offset >= master->size) {
  /* let's register it anyway to preserve ordering */
  slave->offset = 0;
  slave->mtd.size = 0;
  printk(KERN_ERR"mtd: partition /"%s/" is out of reach -- disabled/n",
   part->name);
  goto out_register;
 }
 if (slave->offset + slave->mtd.size > master->size) {
  slave->mtd.size = master->size - slave->offset;
  printk(KERN_WARNING"mtd: partition /"%s/" extends beyond the end of device /"%s/" -- size truncated to %#llx/n",
   part->name, master->name, (unsigned long long)slave->mtd.size);
 }
 if (master->numeraseregions > 1) {
  /* Deal with variable erase size stuff */
  int i, max = master->numeraseregions;
  u64 end = slave->offset + slave->mtd.size;
  struct mtd_erase_region_info *regions = master->eraseregions;
  /* Find the first erase regions which is part of this
   * partition. */
  for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
   ;
  /* The loop searched for the region _behind_ the first one */
  i--;
  /* Pick biggest erasesize */
  for (; i < max && regions[i].offset < end; i++) {
   if (slave->mtd.erasesize < regions[i].erasesize) {
    slave->mtd.erasesize = regions[i].erasesize;
   }
  }
  BUG_ON(slave->mtd.erasesize == 0);
 } else {
  /* Single erase size */
  slave->mtd.erasesize = master->erasesize;  // 分区擦除大小赋值
 }
 if ((slave->mtd.flags & MTD_WRITEABLE) &&
     mtd_mod_by_eb(slave->offset, &slave->mtd)) {
  /* Doesn't start on a boundary of major erase size */
  /* FIXME: Let it be writable if it is on a boundary of
   * _minor_ erase size though */
  slave->mtd.flags &= ~MTD_WRITEABLE;
  printk(KERN_WARNING"mtd: partition /"%s/" doesn't start on an erase block boundary -- force read-only/n",
   part->name);
 }
 if ((slave->mtd.flags & MTD_WRITEABLE) &&
     mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
  slave->mtd.flags &= ~MTD_WRITEABLE;
  printk(KERN_WARNING"mtd: partition /"%s/" doesn't end on an erase block -- force read-only/n",
   part->name);
 }
 slave->mtd.ecclayout = master->ecclayout;
 if (master->block_isbad) {
  uint64_t offs = 0;
  while (offs < slave->mtd.size) {
   if (master->block_isbad(master,
      offs + slave->offset))
    slave->mtd.ecc_stats.badblocks++; // 分区内坏块检查统计
   offs += slave->mtd.erasesize;
  }
 }
out_register:
 if (part->mtdp) {
  /* store the object pointer (caller may or may not register it*/
  *part->mtdp = &slave->mtd;
  slave->registered = 0;
 } else {
  /* register our partition */
  add_mtd_device(&slave->mtd);      // importment
  // 将该从分区作为MTD原始设备加入到mtd_table中,成功返回0
  // MTD原始设备层和MTD设备层就是依靠mtd_table来联系的
  slave->registered = 1;
 }
 return slave;
}
int add_mtd_device(struct mtd_info *mtd)
{
 int i;
 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_table[]数组,mtd原始设备层和mtd块设备层通过mtd_table[]联系在了一起
   mtd->index = i;     // mtd_table[]数组的下标赋给mtd->index
   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 work/n",
            mtd->name);
   }
   DEBUG(0, "mtd: Giving out device %d to %s/n",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);        // 只要底层向上层添加一个mtd原始设备的话,那么就会遍历所有用户通知器
                    // 然后调用其add函数,再向mtd块设备层的上层通用磁盘层和block层注册。
                    // 对于mtdblock翻译层,其add_mtd函数指针指向mtdblock_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;
}
//////////////////////////////////////////////////////////////////////////////////////////////
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 */
};
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;
 // 这里将分区作为的MTD设备联系在了一起,mtd_table
 dev->mtd = mtd;
 dev->devnum = mtd->index; // 分区表的索引值
 dev->size = mtd->size >> 9; // 该分区的大小以512为单位来计算
 dev->tr = tr;    // 操作集
 if (!(mtd->flags & MTD_WRITEABLE))
  dev->readonly = 1;
 add_mtd_blktrans_dev(dev);
 // 后面详解
}
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();
 }
 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;
   // 这里返回上层没有错误判断,因此也就没有释放掉上层函数开辟的struct mtd_blktrans_dev
   // 的内存空间,存在内存泄露,这里应该算是一个内核bug吧,不过呢,这个bug基本上是不会出现的
   // 如果你的分区不会动态被增加或者删除的话,这里就不会返回这个错误的 
  } 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;
 }
 list_add_tail(&new->list, &tr->devs);
 // 翻译层操作集管理者所有属于该层的设备
 added:
 mutex_init(&new->lock);
 if (!tr->writesect)
  new->readonly = 1;
 gd = alloc_disk(1 << tr->part_bits);        // note 1
 // 分配gendisk结构体空间,并做一些初始的设置
 if (!gd) {
  list_del(&new->list);
  return -ENOMEM;
 }
 gd->major = tr->major;               // mtdblock  , 31
 gd->first_minor = (new->devnum) << tr->part_bits;  // mtd_table[]下标
 gd->fops = &mtd_blktrans_ops;            // 操作函数集
 if (tr->part_bits)  // 0
  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);
                           // gd->disk_name = mtdblock{0 - 31}
 /* 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; // 该mtd设备的请求队列
 if (new->readonly)
  set_disk_ro(gd, 1);
 add_disk(gd);  // 向上层添加一个gendisk note2
 return 0;
}
/** note1 gd = alloc_disk(1) **/
struct gendisk *alloc_disk(int minors)
{
 return alloc_disk_node(minors, -1);  // note1-1
}
/**** note1-1  alloc_disk_node(1, -1) ****/
struct gendisk *alloc_disk_node(int minors, int node_id)
{
 struct gendisk *disk;
 disk = kmalloc_node(sizeof(struct gendisk),
    GFP_KERNEL | __GFP_ZERO, node_id);   // node_id就是NUMA系统中的节点号
 if (disk) {
  if (!init_part_stats(&disk->part0)) {
   kfree(disk);
   return NULL;
  }
  disk->node_id = node_id;          // -1
  if (disk_expand_part_tbl(disk, 0)) {
   free_part_stats(&disk->part0);
   kfree(disk);
   return NULL;
  }
  disk->part_tbl->part[0] = &disk->part0;
  disk->minors = minors;           // 1
  rand_initialize_disk(disk);
  disk_to_dev(disk)->class = &block_class;  // #define disk_to_dev(disk) (&(disk)->part0.__dev)
  disk_to_dev(disk)->type = &disk_type;
  device_initialize(disk_to_dev(disk));
  INIT_WORK(&disk->async_notify,
   media_change_notify_thread);
 }
 return disk;
}
/**** note1-1  alloc_disk_node(1, -1) ****/
/** note1 gd = alloc_disk(1) **/
/** note2 add_disk(gd) **/
void add_disk(struct gendisk *disk)
{
 struct backing_dev_info *bdi;
 dev_t devt;
 int retval;
 /* minors == 0 indicates to use ext devt from part0 and should
  * be accompanied with EXT_DEVT flag.  Make sure all
  * parameters make sense.
  */
 WARN_ON(disk->minors && !(disk->major || disk->first_minor));
 WARN_ON(!disk->minors && !(disk->flags & GENHD_FL_EXT_DEVT));
 disk->flags |= GENHD_FL_UP;
 retval = blk_alloc_devt(&disk->part0, &devt);   // note2-1
 if (retval) {
  WARN_ON(1);
  return;
 }
 disk_to_dev(disk)->devt = devt;  // disk->part0.__dev->devt = devt
 // 主次设备号
 /* ->major and ->first_minor aren't supposed to be
  * dereferenced from here on, but set them just in case.
  */
 disk->major = MAJOR(devt);    // 31 
 disk->first_minor = MINOR(devt); // {0 - 31}
 blk_register_region(disk_devt(disk), disk->minors, NULL,
       exact_match, exact_lock, disk);
 register_disk(disk);       // note2-2
 blk_register_queue(disk);
 bdi = &disk->queue->backing_dev_info;
 bdi_register_dev(bdi, disk_devt(disk));
 retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj,
       "bdi");
 WARN_ON(retval);
}
/**** note2-1  blk_alloc_devt() ****/
int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
{
 struct gendisk *disk = part_to_disk(part);
 int idx, rc;
 
// part->partno = 0, disk->minors = 1, disk->major = 31, disk->first_minor = {0 - 31}
 /* in consecutive minor range? */
 if (part->partno < disk->minors) {
  *devt = MKDEV(disk->major, disk->first_minor + part->partno);  // 最后组织的主次设备号为31<<20 | {0 ~ 31}
  return 0;
 }
 /* allocate ext devt */      // 另外的方式来分配主次设备号
 do {
  if (!idr_pre_get(&ext_devt_idr, GFP_KERNEL))
   return -ENOMEM;
  rc = idr_get_new(&ext_devt_idr, part, &idx);
 } while (rc == -EAGAIN);
 if (rc)
  return rc;
 if (idx > MAX_EXT_DEVT) {
  idr_remove(&ext_devt_idr, idx);
  return -EBUSY;
 }
 *devt = MKDEV(BLOCK_EXT_MAJOR, blk_mangle_minor(idx));
 return 0;
}
/**** note2-1  blk_alloc_devt() ****/
/**** note2-2  register_disk(disk) ****/
void register_disk(struct gendisk *disk)
{
 struct device *ddev = disk_to_dev(disk);
 struct block_device *bdev;
 struct disk_part_iter piter;
 struct hd_struct *part;
 int err;
 ddev->parent = disk->driverfs_dev;
 dev_set_name(ddev, disk->disk_name);    // mtdblock{0 - 31}
 /* delay uevents, until we scanned partition table */
 ddev->uevent_suppress = 1;
 if (device_add(ddev))            // 将该设备添加到系统设备树中
  return;
#ifndef CONFIG_SYSFS_DEPRECATED
 err = sysfs_create_link(block_depr, &ddev->kobj,
    kobject_name(&ddev->kobj));
 if (err) {
  device_del(ddev);
  return;
 }
#endif
 disk->part0.holder_dir = kobject_create_and_add("holders", &ddev->kobj);
 disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj);
 /* No minors to use for partitions */
 if (!disk_partitionable(disk))
  goto exit;
 /* No such device (e.g., media were just removed) */
 if (!get_capacity(disk))
  goto exit;
 bdev = bdget_disk(disk, 0);   // note2-2-1
 if (!bdev)
  goto exit;
 bdev->bd_invalidated = 1;
 err = blkdev_get(bdev, FMODE_READ); // 获取一次以验证
 if (err < 0)
  goto exit;
 blkdev_put(bdev, FMODE_READ);
exit:
 /* announce disk after possible partitions are created */
 ddev->uevent_suppress = 0;
 kobject_uevent(&ddev->kobj, KOBJ_ADD);
 /* announce possible partitions */
 disk_part_iter_init(&piter, disk, 0);
 while ((part = disk_part_iter_next(&piter)))
  kobject_uevent(&part_to_dev(part)->kobj, KOBJ_ADD);
 disk_part_iter_exit(&piter);
}
/****** note2-2-1  bdev = bdget_disk(disk, 0) ******/
struct block_device *bdget_disk(struct gendisk *disk, int partno)
{
 struct hd_struct *part;
 struct block_device *bdev = NULL;
 part = disk_get_part(disk, partno);
 if (part)
  bdev = bdget(part_devt(part));  // note 2-2-1-1
  // 根据主次设备号得到block_device结构体
 disk_put_part(part);
 return bdev;
}
/******** note2-2-1-1  bdev = bdget(part_devt(part)) ********/
// fs/block_dev.c
struct bdev_inode {
 struct block_device bdev;
 struct inode vfs_inode;
};
static inline struct bdev_inode *BDEV_I(struct inode *inode)
{
 return container_of(inode, struct bdev_inode, vfs_inode);
}
inline struct block_device *I_BDEV(struct inode *inode)
{
 return &BDEV_I(inode)->bdev;
}
static inline unsigned long hash(dev_t dev)
{
 return MAJOR(dev)+MINOR(dev);
}
static int bdev_test(struct inode *inode, void *data)
{
 return BDEV_I(inode)->bdev.bd_dev == *(dev_t *)data;
}
static int bdev_set(struct inode *inode, void *data)
{
 BDEV_I(inode)->bdev.bd_dev = *(dev_t *)data;
 return 0;
}
static LIST_HEAD(all_bdevs);
struct block_device *bdget(dev_t dev)
{
 struct block_device *bdev;
 struct inode *inode;
 inode = iget5_locked(blockdev_superblock, hash(dev),
   bdev_test, bdev_set, &dev);
 // 该函数最终会调用函数bdev_set()将dev的值赋值给BDEV_I(inode)->bdev.bd_dev
 if (!inode)
  return NULL;
 bdev = &BDEV_I(inode)->bdev;
 if (inode->i_state & I_NEW) {
  bdev->bd_contains = NULL;
  bdev->bd_inode = inode;  // inode关联到block_device结构体中
  bdev->bd_block_size = (1 << inode->i_blkbits);
  bdev->bd_part_count = 0;
  bdev->bd_invalidated = 0;
  inode->i_mode = S_IFBLK; // 对应的是块设备节点
  inode->i_rdev = dev;   // 主次设备号关联
  inode->i_bdev = bdev;   // block_device结构体关联
  
  /************************
  在使用mount挂载该设备上的文件系统时,例如上一篇文章中为了挂载nand分区中的yaffs2文件系统,那么系统是在哪个
  地方使用了注册时设置的这些信息呢?
  ...
  get_sb_bdev()
  --> open_bdev_exclusive()
    --> lookup_bdev()
      --> kern_path()
      --> bdev = bd_acquire(inode)
        这里只是取出了block_device结构体的指针
  而对于上面函数iget5_locked()-->bdev_set()中设置在对应block_device结构体中的主次设备号在文件系统挂载的时候
  ,在下面这个函数中获取到后存放在超级块中。
  ...
  get_sb_bdev()
  --> open_bdev_exclusive()
  --> sget()
    --> set_bdev_super()
      --> s->s_bdev = data;        // 就是open_bdev_exclusive()函数获得的block_device结构体
      --> s->s_dev = s->s_bdev->bd_dev;  // 主次设备号
  
  最后在yaffs2文件系统超级块填充函数yaffs_internal_read_super()中是这么使用的:
  ...
  if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR)
   return NULL; /* This isn't an mtd device */
  ...
  mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); // 取得对应的mtd_info结构体
  ...
  ************************/
  
  inode->i_data.a_ops = &def_blk_aops;
  mapping_set_gfp_mask(&inode->i_data, GFP_USER);
  inode->i_data.backing_dev_info = &default_backing_dev_info;
  spin_lock(&bdev_lock);
  list_add(&bdev->bd_list, &all_bdevs);
  spin_unlock(&bdev_lock);
  unlock_new_inode(inode);
 }
 return bdev;        // 返回这个block_device指针
}
/******** note2-2-1-1  bdev = bdget(part_devt(part)) ********/
/****** note2-2-1  bdev = bdget_disk(disk, 0) ******/
/**** note2-2  register_disk(disk) ****/
/** note2 add_disk(gd) **/
四、一点补充
static struct mtdblk_dev {
 struct mtd_info *mtd;
 int count;
 struct mutex cache_mutex;
 unsigned char *cache_data;
 unsigned long cache_offset;
 unsigned int cache_size;
 enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
} *mtdblks[MAX_MTD_DEVICES];  // #define MAX_MTD_DEVICES 32
/*
** 这里是定义了一个指针数组,其中的每一个指针均指向一个struct mtdblk_dev
** 的类型的对象,每一个struct mtdblk_dev类型的对象都是一个MTD块设备,
** 这里的每一个指针指向的MTD块设备和mtd_table[]中元素指向的每一个
** struct mtd_info一一对应。
** 另外,可以看出linux最多支持32个MTD块设备
 
 关于mtdblks[]指针数组,我这里是以yaffs文件系统为例,所以是不会使用到这个指针数组的,应为yaffs它是建立在mtd原始设备层上,在其封装的函数内直接使用了mtd_info结构体内的函数,而没有经过mtdblock翻译层。如果我们的系统中使用的是mtd的其他接口,比如block device,那么就会使用到这个指针数组,在哪里使用呢?
 mtdblock.c文件中定义了mtdblock翻译层的操作集:
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,
};
这些mtdblks[]的指针在哪里赋值的呢?请看函数mtdblock_open()中:
static int mtdblock_open(struct mtd_blktrans_dev *mbd)
{
 // mtd_blktrans_dev这个设备就是我们在初始化经过mtdblock翻译层向上层注册时的产物,表示了本层环境中的mtd设备
 // mbd->mtd在mtdblock_add_mtd()函数中被赋值,就是对应的mtd原始设备
 struct mtdblk_dev *mtdblk;
 struct mtd_info *mtd = mbd->mtd;
 int dev = mbd->devnum;
 DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open/n");
 if (mtdblks[dev]) {
  mtdblks[dev]->count++;
  return 0;
 } // 如果已经打开了,那么只需要增加引用计数
 /* OK, it's not open. Create cache info for it */
 mtdblk = kzalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); // 否则,分配空间
 if (!mtdblk)
  return -ENOMEM;
 mtdblk->count = 1;  // 引用计数初始化成1
 mtdblk->mtd = mtd;  // 重要的联系
 mutex_init(&mtdblk->cache_mutex);
 mtdblk->cache_state = STATE_EMPTY;
 if ( !(mtdblk->mtd->flags & MTD_NO_ERASE) && mtdblk->mtd->erasesize) {
  mtdblk->cache_size = mtdblk->mtd->erasesize;
  mtdblk->cache_data = NULL;
 }
 mtdblks[dev] = mtdblk; // mtdblks指针数组中相应位置设置
 DEBUG(MTD_DEBUG_LEVEL1, "ok/n");
 return 0;
}

其实mtd块设备层和mtd原始设备层的分界线很明显,只是通过
 mtd_table[]和mtd_notifiers链表来联系,具体怎么联系的请参考上一篇文章。
 本文中将详细分析linux内核中nand设备注册和驱动注册,同时文中会穿插关于nand坏块管理的部分,另外在适当的地方会
 讲解DMA原理和其在nand驱动程序中的应用。
 
 * OS   : linux2.6.29
 * SOC  : pxa935
 * NAND : Hynix(512MB 1.8V 16-bit) - H8BCS0UN0MCR
 * Author: 李枝果/lizgo  2010-11-9  lizhiguo0532@163.com
 * note  : 本文中涉及的内容基本都是平台相关的,读者可只关注共性的东西


 2.6版本的linux内核驱动模型中流传着一个时髦的词:“platform”,其中也存在platform device和platform driver,
内核中使用platform bus统一管理这些设备和驱动,所以注册包括device和driver的注册。
一、platform device注册
 MACHINE_START和MACHINE_END定义的结构体是平台相关的,这里定义如下:
 MACHINE_START(BENZGLB, "Benzglb")
  .phys_io        = 0x40000000,
  .boot_params    = 0xa0000100,
  .io_pg_offst    = (io_p2v(0x40000000) >> 18) & 0xfffc,
  
  .map_io         = pxa_map_io,
  // start_kernel()-->setup_arch()-->paging_init()-->devicemaps_init()-->pxa_map_io
  .init_irq       = pxa3xx_init_irq,
  // 在setup_arch()中被赋值给全局函数指针init_arch_irq
  // start_kernel()-->init_IRQ()-->"init_arch_irq()"间接调用pxa3xx_init_irq
  .timer          = &pxa_timer,
  // 在setup_arch()中被赋值给全局struct sys_timer对象指针system_timer
  // start_kernel()-->time_init-->"system_timer->init()"间接调用pxa_timer.init = pxa_timer_init()
  .init_machine   = benzglb_init,
  // 在setup_arch()中被赋值给全局函数指针init_machine
  // 由于arch_initcall(customize_machine),所以在start_kernel()-->rest_init()
  // -->kernel_init()-->do_basic_setup()-->do_initcalls()的第3个等级上被调用
  // init.h
 MACHINE_END
 /*******************************
 其中的宏定义于文件:arch/arm/include/asm/mach/arch.h
 #define MACHINE_START(_type,_name)   /
 static const struct machine_desc __mach_desc_##_type /
  __used       /
  __attribute__((__section__(".arch.info.init"))) = { /
  .nr  = MACH_TYPE_##_type,  /
  .name  = _name,
 
 #define MACHINE_END    /
 };
 将上面的展开,实际上就是定义了一个结构体:
 static const struct machine_desc __mach_desc_BENZGLB   __used /
                  __attribute__((__section__(".arch.info.init"))) = {
                  .nr  = MACH_TYPE_BENZGLB,
                  .name  = "Benzglb",
                  .phys_io        = 0x40000000,
                    .boot_params    = 0xa0000100,
                    .io_pg_offst    = (io_p2v(0x40000000) >> 18) & 0xfffc,
                    .map_io         = pxa_map_io,
                    .init_irq       = pxa3xx_init_irq,
                    .timer          = &pxa_timer,
                    .init_machine   = benzglb_init,
                    };
 *******************************/
 benzglb_init()函数可谓是重量级的了,初始化了很多东西。但是是在哪里调用该函数的呢?或许从上面的注释你也可以
 注意到了,下面就再来wlak一下:
 start_kernel()
 --> setup_arch()
   --> ...
   --> init_machine = mdesc->init_machine;
   --> ...
   init_machine是文件arch/arm/kernel/setup.c中的静态全局变量,定义和调用如下:
   static void (*init_machine)(void) __initdata;
   static int __init customize_machine(void)
   {
    /* customizes platform devices, or adds new ones */
    if (init_machine)
     init_machine();
    return 0;
   }
   arch_initcall(customize_machine);    // init.h  initcall3
   可以看到customize_machine()函数将会在do_initcalls()的第3个等级上被调用,接着就会调用函数init_machine(),
   也就是函数benzglb_init():
 do_initcalls()
 --> customize_machine()
   --> init_machine() == benzglb_init()
     --> benzina_init_nand()       // 该函数中我们只关注nand初始化
       看来有必要将benzina_init_nand()全部列出来看一看了:
       static struct pxa3xx_nand_platform_data benzina_nand_info;
       /******
       struct pxa3xx_nand_platform_data {
        struct mtd_partition *parts;
        unsigned int    nr_parts;
       };
       ******/
       static void __init benzina_init_nand(void)
       {
        benzina_nand_info.parts = android_256m_v75_partitions;        // nand分区数组
        benzina_nand_info.nr_parts = ARRAY_SIZE(android_256m_v75_partitions); // nand分区数目
        
        pxa3xx_device_nand.dev.platform_data = &benzina_nand_info;
        platform_device_register(&pxa3xx_device_nand);
       }
       android_256m_v75_partitions定义于arch/arm/mach-pxa/include/mach/part_table.h中,这是一个
       struct mtd_partition类型结构体的数组,描述了系统上nand分区情况:name、offset、size等。
       pxa3xx_device_nand结构体对象是struct platform_device类型:
       static u64 pxa3xx_nand_dma_mask = DMA_BIT_MASK(32);
       static struct resource pxa3xx_resource_nand[] = {
        [0] = {
         .start = 0x43100000,
         .end = 0x431000ff,
         .flags = IORESOURCE_MEM,
        },
        [1] = {
         .start = IRQ_NAND,
         .end = IRQ_NAND,
         .flags = IORESOURCE_IRQ,
        },
       };
       
       struct platform_device pxa3xx_device_nand = {
        .name  = "pxa3xx-nand",
        .id  = -1,
        .dev  = {
         .dma_mask = &pxa3xx_nand_dma_mask,
         .coherent_dma_mask = DMA_BIT_MASK(32),
        },
        .resource = pxa3xx_resource_nand,   // see up
        .num_resources = ARRAY_SIZE(pxa3xx_resource_nand),
       };
       struct platform_device结构体的定义位于文件include/linux/platform_device.h中:
       struct platform_device {
        const char * name;
        int  id;
        struct device dev;
        u32  num_resources;
        struct resource * resource;
       };
       benzina_init_nand()函数中将描述nand分区的结构体benzina_nand_info与描述nand device的结构体
       联系起来:
       pxa3xx_device_nand.dev.platform_data = &benzina_nand_info;
       最后调用函数platform_device_register(&pxa3xx_device_nand)将nand的平台设备注册进系统的设备树内。
       对于注册的过程这里就不跟踪了,如果有兴趣,可参考我的另篇文章或自行分析。
二、platform driver注册
 该部分的内容位于文件drivers/mtd/nand/pxa3xx_nand.c
 static struct platform_driver pxa3xx_nand_driver = {
  .driver = {
   .name = "pxa3xx-nand",
  },
  .probe  = pxa3xx_nand_probe,
  .remove  = pxa3xx_nand_remove,
 #ifdef CONFIG_PM                // 电源管理的部分
  .suspend = pxa3xx_nand_suspend,
  .resume  = pxa3xx_nand_resume,
 #endif
 };
 static int __init pxa3xx_nand_init(void)
 {
  ...
  return platform_driver_register(&pxa3xx_nand_driver);
 }
 module_init(pxa3xx_nand_init); 
 这里说明一点:device和driver的注册其实是没有先后之分的,device注册的时候除了将自己挂在platform bus上外,另外
 会去遍历该bus上的所有drivers,直到匹配到(device和driver的名字相同)一个driver为止。而driver注册的时候,也是除
 了将自己挂在platfrom bus上之外,另外也会去遍历该bus上的所有设备区匹配,这里和前面不同的是,它会去找到多有该
 driver可以管理到的设备为止。下面就跟踪一下driver注册时,如何调用到probe函数的,又如何传递了
 struct platform_device的参数:
 platform_driver_register(&pxa3xx_nand_driver)
 --> drv->driver.bus = &platform_bus_type
 --> drv->driver.probe = platform_drv_probe
 --> ...
 --> driver_register(&drv->driver)
   --> driver_find(drv->name, drv->bus)    // 线检查是否已经注册过了
   --> bus_add_driver(drv)
     --> driver_attach(drv)
        --> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
          --> fn(dev, data) = __driver_attach(dev, data) // dev - each device on platfrom bus
                                  // data - driver
            --> driver_probe_device(drv, dev)
              --> drv->bus->match(dev, drv) = platform_match(dev, drv) // 名字匹配
              --> really_probe(dev, drv)
                --> drv->probe(dev) = platform_drv_probe(dev)
                  --> static int platform_drv_probe(struct device *_dev)
                   {
                    struct platform_driver *drv = to_platform_driver(_dev->driver);
                    struct platform_device *dev = to_platform_device(_dev);
             
                    return drv->probe(dev);
                   }
                   -->pxa3xx_nand_probe(&pxa3xx_device_nand);
                   ...
                   到这个过程中,我们就还可以看到,driver注册的时候,会优先使用platform bus
                   的probe函数,如果它的probe函数为NULL,那么就使用注册driver的probe函数
                   (前提是要存在probe函数)
三、pxa3xx_nand_probe()函数分析
  由于内容加多,参见文档:pxa3xx_nand_probe.c
四、底层几个关键结构体的联系
static struct mtd_info *monahans_mtd = NULL;
struct nand_chip *this; 
struct pxa3xx_nand_info *info;
struct dfc_context dfc_context =
{
 .dfc_mode = &dfc_mode,
};
static struct dfc_flash_info hynix4GbX16 =
{
 .timing = {
  .tCH = 10,      /* tCH, Enable signal hold time */
  .tCS = 35,      /* tCS, Enable signal setup time */
  .tWH = 15,      /* tWH, ND_nWE high duration */
  .tWP = 25,      /* tWP, ND_nWE pulse time */
  .tRH = 15,      /* tRH, ND_nRE high duration */
  .tRP = 25,      /* tRP, ND_nRE pulse width */
  /* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
  .tR = 25000,
  /* tWHR, ND_nWE high to ND_nRE low delay for status read */
  .tWHR = 60,
  .tAR = 10,      /* tAR, ND_ALE low to ND_nRE low delay */
 },
 .enable_arbiter = 1,    /* Data flash bus arbiter enable */
 .page_per_block = 64,   /* Pages per block */
 .row_addr_start = 1, /* third cycle start, Row address start position */
 .read_id_bytes = 4,     /* Returned ID bytes */
 .dfc_mode = 0,          /* NAND mode */
 .ncsx = 0,
 .page_size = 2048,      /* Page size in bytes */
 .oob_size = 64,         /* OOB size in bytes */
 .flash_width = 16,      /* Width of Flash memory */
 .dfc_width = 16,        /* Width of flash controller */
 .num_blocks = 4096,     /* Number of physical blocks in Flash */   //modified sunqidong
 .chip_id =  0xbcad,                         //modified sunqidong
 .read_prog_cycles = 5, /* Read, Program Cycles */
 /* command codes */
 .read1 = 0x3000,        /* Read */
 .read2 = 0x0050,        /* Read1 unused, current DFC don't support */
 .program = 0x1080,      /* Write, two cycle command */       //modified sunqidong
 .read_status = 0x0070,  /* Read status */
 .read_id = 0x0090,      /* Read ID */
 .erase =  0xD060,       /* Erase, two cycle command */
 .reset = 0x00FF,        /* Reset */
 .lock = 0x002A,         /* Lock whole flash */
 .unlock = 0x2423, /* Unlock, two cycle command, supporting partial unlock */
 .lock_status = 0x007A,  /* Read block lock status */
 .addr2ndcb1 = HYNIX4GbX16Addr2NDCB1,
 .ndbbr2addr = HYNIX4GbX16NDBBR2Addr,
};
///////////////////////// 这几个底层关键结构体的联系 ////////////////////////////
context->flash_info = &hynix4GbX16
// 分配mtd_info、nand_chip、pxa3xx_nand_info的空间
monahans_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip) +
   sizeof(struct pxa3xx_nand_info) , GFP_KERNEL);
...
this = (struct nand_chip *)((void *)monahans_mtd + sizeof(struct mtd_info));
info = (struct pxa3xx_nand_info *)((void *)this + sizeof(struct nand_chip));
...
monahans_mtd->priv = this;
this->priv = info;
...
info->context = &dfc_context
...


0 0
原创粉丝点击