7.NandFlash块设备驱动-- 二:NAND FLASH驱动框架

接下来我们来参考自带的nand flash驱动,位于drivers/mtd/nand/s3c2410.c中
为什么nand flash设备驱动在mtd目录下?
因为mtd(memory technology device 存储 技术设备 ) 是用于访问 memory 设备( ROM 、 flash )的Linux 的子系统。 MTD 的主要目的是为了使新的 memory 设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口

首先来看s3c2410.c的入口函数:


static int __init s3c2410_nand_init(void)
{
       printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
       platform_driver_register(&s3c2412_nand_driver);     
       platform_driver_register(&s3c2440_nand_driver);     

       return platform_driver_register(&s3c2410_nand_driver);
}

在入口函数中,注册了一个platform平台设备驱动,也是说当与nandflash设备匹配时,就会调用s3c2440_nand_driver ->probe来初始化

我们进入probe函数中,看看是如何初始化

static int s3c24xx_nand_probe(struct platform_device *pdev, enum s3c_cpu_type cpu_type)
{
... ...

err = s3c2410_nand_inithw(info, pdev);       //初始化硬件hardware,设置TACLS 、TWRPH0、TWRPH1通信时序等

s3c2410_nand_init_chip(info, nmtd, sets);    //初始化芯片

nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1); //3.扫描nandflash
... ...
s3c2410_nand_add_partition(info, nmtd, sets);         //4.调用add_mtd_partitions()来添加mtd分区
... ...
}

通过上面代码和注释,得出:nand flash设备驱动主要调用
内核的nand_scan()函数和s3c2410_nand_add_partition()函数,来完成注册nandflash。

1.nand_scan()函数:扫描nand Flash

它会调用
a.nand_scan()->nand_scan_ident()->nand_get_flash_type()来获取flash存储器的类型
b.以及nand_scan()->nand_scan_tail()来构造mtd设备的成员(实现对nandflash的读,写,擦除等)

1.1 nand_get_flash_type()函数:获取flash存储器的类型

函数如下所示:

static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,struct nand_chip *chip,int busw, int *maf_id)
{
 struct nand_flash_dev *type = NULL;
 int i, dev_id, maf_idx;
 chip->select_chip(mtd, 0);     //调用nand_chip结构体的成员select_chip使能flash片选

 chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); //3.2调用nand_chip结构体的成员cmdfunc,发送读id命令,最后数据保存在mtd结构体里

 *maf_id = chip->read_byte(mtd); // 获取厂家ID,

  dev_id = chip->read_byte(mtd);   //获取设备ID

   /* 3.3for循环匹配nand_flash_ids[]数组,找到对应的nandflash信息*/
     for (i = 0; nand_flash_ids[i].name != NULL; i++) 
   {  
         if (dev_id == nand_flash_ids[i].id)     //匹配设备ID
         {type =  &nand_flash_ids[i];
           break;}
  }
       ... ...

/* 3.4 匹配成功,便打印nandflash参数   */
printk(KERN_INFO "NAND device: Manufacturer ID:"
                     " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
                     dev_id, nand_manuf_ids[maf_idx].name, mtd->name);  
       ... ...
}

从上面代码和注释得出, nand_chip结构体就是保存与硬件相关的函数(后面会讲这个结构体)
其中NAND_CMD_READID定义为0x90,也就是发送0X90命令,和0x00地址来读id,最后放到mtd中
nand_flash_ids[]数组是个全局变量,这里通过匹配设备ID,来确定我们的nand flash是个多大的存储器

1.2 nand_scan_tail函数:构造mtd设备的成员(实现对nandflash的读,写,擦除等)

2.s3c2410_nand_add_partition()函数:注册mtd设备,创建nand flash分区

最终它调用了s3c2410_nand_add_partition()->add_mtd_partitions() -> add_mtd_device()其中**
a.add_mtd_partitions()函数主要实现多个分区创建,也就是多次调用add_mtd_device()**

b.当只设置nand_flash为一个分区时,就直接调用add_mtd_device()即可.

2.1: add_mtd_partitions()函数原型如下:

int add_mtd_partitions(struct mtd_info *master, const struct mtd_partition *parts,int nbparts);  //创建多个分区mtd设备
//函 数 成 员 介 绍 : 
//master:就是要创建的mtd设备
//parts:分区信息的数组,它的结构体是mtd_partition,该结构体如下所示:
/*
struct mtd_partition {
       char *name;                  //分区名,比如bootloader、params、kernel、root
       u_int32_t size;               //分区大小
       u_int32_t offset;            //分区所在的偏移值
       u_int32_t mask_flags;            //掩码标志
       struct nand_ecclayout *ecclayout; //OOB布局
       struct mtd_info **mtdp;              //MTD的指针,不常用
};
*/
//nbparts:等于分区信息的数组个数,表示要创建分区的个数

比如我们启动内核时,也能找到内核自带的nandflash的分区信息:
1
2.2其中add_mtd_device()函数如下所示:

int add_mtd_device(struct mtd_info *mtd)    //创建一个mtd设备
{
 struct list_head *this;
 ... ...
    list_for_each(this, &mtd_notifiers)     //4.3找mtd_notifiers链表里的list_head结构体
  {
   struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); //通过list_head找到struct mtd_notifier *not
   not->add(mtd);            //最后调用mtd_notifier 的add()函数
  }
 ... ...
}

从上面代码和注释可知,add_mtd_device创建一个mtd设备,就是将list_head结构体放入mtd_notifiers链表中。
通过搜索mtd_notifiers链表发现,list_head在register_mtd_user()函数里放到mtd_notifiers链表中。
在这里插入图片描述
继续搜索register_mtd_user(),被哪个调用
在这里插入图片描述
如上图,找到被drivers/mtd/mtdchar.c、drivers/mtd/mtd_blkdevs.c调用。
是因为 mtd层既提供了字符设备的操作接口(mtdchar.c), 也实现了块设备的操作接口(mtd_blkdevs.c)

我们在控制台输入ls -l /dev/mtd*,也能找到块MTD设备节点和字符MTD设备节点,如下图所示:
在这里插入图片描述
图中,可以看到共创了4个分区的设备,每个分区都包含了两个字符设备(mtd%d,mtd%dro)、一个块设备(mtdblock0).
其中MTD的块设备的主设备号为31,MTD的字符设备的主设备号为90 (后面会讲到在哪被创建)。

3.mtdchar.c: 字符设备的操作接口:
mtdchar.c的入口函数 将notifie添加到mtd_notifiers链表中,
然后在add_mtd_device()函数中当查找到mtd字符设备的list_head时,就调用mtd_notifiers->add()来创建两个字符设备(mtd%d,mtd%dro)

4.mtd_blkdevs.c 块设备的操作接口:
mtd_blkdevs.c块设备的入口函数 将blktrans_notifier添加到mtd_notifiers链表中,并创建块设备,请求队列.
然后在add_mtd_device()函数中,当查找到有blktrans_notifier时,就调用blktrans_notifier->add()来分配设置注册gendisk结构体

总结:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值