内核版本号:linux-2.6.22
块设备程序: driver/mtd/mtdblock.c
1 从驱动的注册函数开始分析
a.先定义并初始化一个全局变量 mtdblock_tr
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,
};
驱动程序的init入口函数init_mtdblock中调用register_mtd_blktrans函数
register_mtd_blktrans(&mtdblock_tr)
b.在driver/mtd/mtd_blkdevs.c中定义了 register_mtd_blktrans函数和如下的结构体变量
static struct mtd_notifier blktrans_notifier = {
.add = blktrans_notify_add,
.remove = blktrans_notify_remove,
};
register_mtd_blktrans
>register_mtd_user(&blktrans_notifier)
+ >blktrans_notifier->add() =也就是调用了=>blktrans_notify_add()
+ >list_for_each(this, &blktrans_majors) //遍历某一主设备号下所用的磁盘分区
+ struct mtd_blktrans_ops *tr->add_mtd(tr, mtd); =也就是调用了=>mtdblock_add_mtd()
+ >add_mtd_blktrans_dev
+ >list_add_tail(&new->list, &tr->devs);//把设备或设备分区加入链表
+ >gd = alloc_disk(1 << tr->part_bits);//分配一个gendisk 磁盘结构体
+ >set_capacity(gd, (new->size * tr->blksize) >> 9);//设置磁盘的容量
+ >gd->queue = tr->blkcore_priv->rq;//该队列是由 blk_init_queue 函数生成的队列
+ >add_disk(gd);//添加磁盘
>register_blkdev(tr->major, tr->name);//注册块设备
>tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock)//初始化队列
>tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,"%sd", tr->name);//生成一个内核线程
>list_add(&tr->list, &blktrans_majors);//把设备分区加入链表
c.当磁盘有数据读写请求时,会调用blk_init_queue函数中指定义的处理函数: mtd_blktrans_request
mtd_blktrans_request
>wake_up_process(tr->blkcore_priv->thread)//唤醒内核线程
|| =也就是调用=> 上面kthread_run函数的第一个参数指针函数 mtd_blktrans_thread
>mtd_blktrans_thread
>do_blktrans_request(tr, dev, req);//处理请求
+ //根据req标识来选择 读/写 操作。
+ >mtd_blktrans_ops *tr->readsect(dev, block, buf) //读操作
|| =也就是调用了=> mtdblock_readsect。可参看上面定义的结构体 struct mtd_blktrans_ops mtdblock_tr
>mtdblock_readsect
>do_cached_read
>mtd->read(mtd, pos, size, &retlen, buf)//在这个地方调用了底层结构体mtd_info的 .read 函数
+ >mtd_blktrans_ops *tr->writesect(dev, block, buf) //写操作
||
>mtdblock_writesect
>do_cached_write
>erase_write(mtd, pos, size, buf)
>mtd->erase(mtd, &erase) //调用mtd_info的 .erase与.write 函数
>mtd->write(mtd, pos, len, &retlen, buf);
>end_request(req, res);//完成处理
d.由c项可知,最后驱动程序会调用mtd_info结构体的 .erase .write .read 函数,那么这些函数又是在哪里初始化的呢?
对于nand flash,可参看内核驱动driver/mtd/nand/nand_base.c
EXPORT_SYMBOL_GPL(nand_scan);
EXPORT_SYMBOL_GPL(nand_scan_ident);
EXPORT_SYMBOL_GPL(nand_scan_tail);
EXPORT_SYMBOL_GPL(nand_release);
导出了四个函数,下面分析这几个函数。
nand_scan(struct mtd_info *mtd, int maxchips) //扫描 nand
>nand_scan_ident(mtd, maxchips);
struct nand_chip *chip = mtd->priv;
>nand_set_defaults(chip, busw);//设备默认的操作函数
+ chip->chip_delay = 20;
+ chip->cmdfunc = nand_command;
+ chip->waitfunc = nand_wait;
+ chip->select_chip = nand_select_chip;
+ chip->read_byte = nand_read_byte;
+ chip->read_word = nand_read_word;
+ chip->block_bad = nand_block_bad;
+ chip->block_markbad = nand_default_block_markbad;
+ chip->write_buf = nand_write_buf;
+ chip->read_buf = nand_read_buf;
+ chip->verify_buf = nand_verify_buf;
+ chip->scan_bbt = nand_default_bbt;
+ chip->controller = &chip->hwcontrol;
>type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);//读设备ID,并比较
>nand_scan_tail(mtd);
struct nand_chip *chip = mtd->priv;
>chip->write_page = nand_write_page
>chip->ecc.read_page_raw = nand_read_page_raw;
>chip->ecc.write_page_raw = nand_write_page_raw;
case NAND_ECC_SOFT: //根据chip->ecc.mode 的值来设备软件ECC操作函数
chip->ecc.calculate = nand_calculate_ecc;
chip->ecc.correct = nand_correct_data;
chip->ecc.read_page = nand_read_page_swecc;
chip->ecc.write_page = nand_write_page_swecc;
chip->ecc.read_oob = nand_read_oob_std;
chip->ecc.write_oob = nand_write_oob_std;
chip->ecc.size = 256;
chip->ecc.bytes = 3;
/* Fill in remaining MTD driver data */
>接下来填充mtd_info结体体mtd变量对应的成员,在这里设备了上面分析过程中用到的三个重要的函数
mtd->erase 与 mtd->read 与 mtd->write,这三个函数在c项分析中提到过。当发生磁盘的读写操作时
会调用这三个函数,那么下来,再分析这三个函数是怎么读写nand flash的。
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
mtd->erase = nand_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
mtd->read = nand_read;
mtd->write = nand_write;
mtd->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
mtd->sync = nand_sync;
mtd->lock = NULL;
mtd->unlock = NULL;
mtd->suspend = nand_suspend;
mtd->resume = nand_resume;
mtd->block_isbad = nand_block_isbad;
mtd->block_markbad = nand_block_markbad;
>先从mtd->read 函数来分析
mtd->read() ==> nand_read()
>nand_get_device
>nand_do_read_ops(mtd, from, &chip->ops);
+ struct nand_chip *chip = mtd->priv;
+ >chip->select_chip(mtd, chipnr);
+ >chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+ ||
+ nand_command
+ >chip->cmd_ctrl(mtd, command, ctrl);//用来设置发命令还是发地址,需要用户来提供
+ >ret = chip->ecc.read_page(mtd, chip, bufpoi);
+ >chip->read_buf(mtd, buf, mtd->writesize);
+ >chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ >buf[i] = readb(chip->IO_ADDR_R);
+ >nand_transfer_oob(chip,buf, ops, mtd->oobsize); //实现从nand读取到的数据拷贝
+ >nand_wait_ready(mtd);
>“死循环” 调用到等待函数chip->dev_ready(mtd);
这其中不会让系统真的等待,会通过内核调度函数来调度其他线程的。
>nand_release_device
>再从mtd->write 函数来分析
mtd->write() ==> nand_write()
>nand_get_device
>nand_do_write_ops(mtd, to, &chip->ops);
+ struct nand_chip *chip = mtd->priv;
+ >chip->select_chip(mtd, chipnr);
+ >chip->write_page(mtd, chip, wbuf, page, cached,(ops->mode == MTD_OOB_RAW));
+ ||
+ >nand_write_page
+ >chip->ecc.write_page(mtd, chip, buf);
+ + >chip->ecc.write_page_raw(mtd, chip, buf);
+ + >chip->write_buf(mtd, buf, mtd->writesize);
+ + >chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ + >nand_write_buf
+ + >writeb(buf[i], chip->IO_ADDR_W);
+ >chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
+ ||
+ nand_command
+ >chip->cmd_ctrl(mtd, command, ctrl);//用来设置发命令还是发地址,需要用户来提供
+ >status = chip->waitfunc(mtd, chip);
+ ||
+ nand_wait
+ >chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
>nand_release_device
e.由前面的分析可知,nand flash 块设备的操作流程都是由mtd核心写好了,用户的驱动只需要提供相应的
mtd_info结构体,nand_chip结构体,以及他相关的硬件操作函数就可以啦。
这些完成后,调用分区相关的函数,所以还需要提供磁盘分区表数组。
先会调用driver/mtd/mtdcore.c中的
>add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)
>进行一大堆相关的数据结构体设置
>add_mtd_device(struct mtd_info *mtd)
> //遍历mtd_notifiers链表的每一个成员,
list_for_each(this, &mtd_notifiers) {
struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
not->add(mtd);
}
//调用mtd_notifier结构体的成员 .add 指针函数,这就回到了b项里的分析,这里再把这些分析
//过程贴过来。
+ >blktrans_notifier->add() =也就是调用了=>blktrans_notify_add()
+ >list_for_each(this, &blktrans_majors) //遍历某一主设备号下所用的磁盘分区
+ struct mtd_blktrans_ops *tr->add_mtd(tr, mtd); =也就是调用了=>mtdblock_add_mtd()
+ >add_mtd_blktrans_dev
+ >list_add_tail(&new->list, &tr->devs);//把设备或设备分区加入链表
+ >gd = alloc_disk(1 << tr->part_bits);//分配一个gendisk 磁盘结构体
+ >set_capacity(gd, (new->size * tr->blksize) >> 9);//设置磁盘的容量
+ >gd->queue = tr->blkcore_priv->rq;//该队列是由 blk_init_queue 函数生成的队列
+ >add_disk(gd);//添加磁盘
>至此,整个nand flash的驱动程序分析过程就结束了。
小结:
通过对nand flash驱动程序的分析,发现:用户根本就不需要编写很多代码来实现flash block驱动的程序,因为
块核心:block_core已经完美实现了gendisk、queue、请求处理函数等操作,用户代码需要实现就是根据不同的CPU
的nand 控制器、以及不同的nand flash实现flash的读、写、控制函数。也就是核心层驱动知道:发的流程,但它
不知道具体的操作方法;而用户提供的驱动程序知道:具体的操作方法;所以两都结合就可以实现了nand flash的
读写了。
用户驱动编写过程:
1 分配结构体nand_chip 、mtd_info
2 定义 nand flash 的分区表数组
3 映射 nand 控制器寄存器
4 设置 nand 控制器寄存器
5 设置 nand_chip 、 mtd_info结构体,实现相关的操作函数
6 扫描、识别nand flash: nand_scan
6 添加分区 :add_mtd_partitions
代码附上:
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/mtd/mtd.h>
#include<linux/mtd/nand.h>
#include<linux/mtd/partitions.h>
#include<linux/slab.h>
#include<asm/sizes.h>
#include<asm/arch/regs-nand.h>
#include<asm/arch/nand.h>
#include<linux/clk.h>
#include<asm/io.h>
struct nand_ctl_regs {
unsigned long nfconf;
unsigned long nfcont;
unsigned long nfcmmd;
unsigned long nfaddr;
unsigned long nfdata;
unsigned long nfmeccd0;
unsigned long nfmeccd1;
unsigned long nfseccd;
unsigned long nfstat;
unsigned long nfestat0;
unsigned long nfestat1;
unsigned long nfmecc0;
unsigned long nfmecc1;
unsigned long nfsecc;
unsigned long nfsblk;
unsigned long nfeblk;
};
static struct mtd_info *gq_nand_mtd;
static struct nand_chip *gq_nand_chip;
static struct nand_ctl_regs *nand_regs;
static struct mtd_partition nand_parts[] ={
[0] = {
.name = "u-boot",
.offset = 0,
.size = SZ_256K,
},
[1] = {
.name = "params",
.offset = MTDPART_OFS_APPEND,
.size = SZ_128K,
},
[2] = {
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = SZ_2M,
},
[3] = {
.name = "rootfs",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
static int gq_dev_ready(struct mtd_info *mtd)
{
return (nand_regs->nfstat & (1<<0) );
}
static void gq_select_chip(struct mtd_info *mtd, int chip)
{/* if chip==-1 取消片选*/
if(-1==chip){
nand_regs->nfcont |= (1<<1);
}
else{
nand_regs->nfcont &= ~(1<<1);
}
}
static void gq_cmd_ctrl(struct mtd_info *mtd, int dat,unsigned int ctrl)
{
if(ctrl & NAND_CLE)/*发命令*/
{
nand_regs->nfcmmd = dat;
}
else /*发地址*/
{
nand_regs->nfaddr = dat;
}
}
static int gq_nand_drv_init(void)
{
struct clk *n_clk;
/*打开nand控制器的时钟*/
n_clk = clk_get(NULL,"nand");
clk_enable(n_clk);
nand_regs = ioremap(0x4E000000,sizeof(struct nand_ctl_regs));
if(nand_regs == NULL){
printk("nand_chip struct alloc failed\n");
return -1;
}
/* nfconf[13-12]:发出CLE/ALE后 (Hclk*TACLS) 时间后能发出nWE,手册上说能同时发出,故可设为0
* nfconf[10- 8]:nWE的脉冲宽度 (Hclk*(TWRPH0+1)),手册上说必须 >=12ns,故可设为1
* nfconf[ 6- 4]:nWE变成高电平后,(Hclk*(TWRPH1+1))时间后,CLE/ALE才能变成低电平,由手册可知
* 必须>5ns,故可设为0
*/
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
nand_regs->nfconf = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
/* nfcont[0]:1->enable nand flash controller BUT:0->disable nand flash controller
* nfcont[1]:1->disable chip select. BUT:0->enable chip select.
*/
nand_regs->nfcont = (1<<1)|(1<<0);/*禁止片选,使能nand flash controller*/
gq_nand_chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
if(gq_nand_chip == NULL){
printk("nand_chip struct alloc failed\n");
return -1;
}
gq_nand_chip->IO_ADDR_R = &nand_regs->nfdata;
gq_nand_chip->IO_ADDR_W = &nand_regs->nfdata;
gq_nand_chip->select_chip = gq_select_chip;
gq_nand_chip->cmd_ctrl = gq_cmd_ctrl;
gq_nand_chip->dev_ready = gq_dev_ready;
gq_nand_chip->ecc.mode = NAND_ECC_SOFT;
gq_nand_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
if(gq_nand_mtd == NULL){
printk("mtd_info struct alloc failed\n");
return -1;
}
gq_nand_mtd->priv = gq_nand_chip;
gq_nand_mtd->owner = THIS_MODULE;
if(!nand_scan(gq_nand_mtd,1))
add_mtd_partitions(gq_nand_mtd, nand_parts, 4);
return 0;
}
static void gq_nand_drv_exit(void)
{
del_mtd_partitions(gq_nand_mtd);
kfree(gq_nand_mtd);
kfree(gq_nand_chip);
iounmap(nand_regs);
}
module_init(gq_nand_drv_init);
module_exit(gq_nand_drv_exit);
MODULE_LICENSE("GPL");