塊設(shè)備驅(qū)動程序之nandflash——基本框架
掃描二維碼
隨時(shí)隨地手機(jī)看文章
我們先查看內(nèi)核的啟動信息,以搞清楚從哪個(gè)文件著手來分析:
S3C24XX NAND Driver, (c) 2004 Simtec Electronics
s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
Scanning device for bad blocks
Bad eraseblock 408 at 0x03300000
Bad eraseblock 441 at 0x03720000
Bad eraseblock 804 at 0x06480000
Bad eraseblock 1155 at 0x09060000
Bad eraseblock 1236 at 0x09a80000
Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":
0x00000000-0x00040000 : "bootloader"
0x00040000-0x00060000 : "params"
0x00060000-0x00260000 : "kernel"
0x00260000-0x10000000 : "root"
我們來搜索:S3C24XX NAND Driver
結(jié)果我們在driversmtdnands3c2410.c文件里發(fā)現(xiàn)了打印這句話的函數(shù):
static struct platform_driver s3c2440_nand_driver = {
.probe = s3c2440_nand_probe,
.remove = s3c2410_nand_remove,
.suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume,
.driver = {
.name = "s3c2440-nand",
.owner = THIS_MODULE,
},
};
static int __init s3c2410_nand_init(void)
{
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronicsn");
platform_driver_register(&s3c2412_nand_driver);
platform_driver_register(&s3c2440_nand_driver);
return platform_driver_register(&s3c2410_nand_driver);
}
我們看到了,nandflash采用的是平臺總線設(shè)備機(jī)制,當(dāng)發(fā)現(xiàn)名字是s3c2440-nand的設(shè)備的時(shí)候,就會調(diào)用probe函數(shù),那么我們就從probe函數(shù)入手了:
s3c24xx_nand_probe
s3c2410_nand_inithw(info, pdev);//初始化硬件
s3c2410_nand_init_chip(info, nmtd, sets);//初始化芯片,這里設(shè)置了chip的一些信息
nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);//掃描芯片
nand_scan_ident(struct mtd_info *mtd, int maxchips)
nand_set_defaults(chip, busw);//設(shè)置默認(rèn)函數(shù)
nand_get_flash_type(mtd, chip, busw, &nand_maf_id);//獲取nandflash類型
chip->select_chip(mtd, 0);//選中芯片
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//發(fā)出讀ID的命令,NAND_CMD_READID為90h
*maf_id = chip->read_byte(mtd);//讀廠家ID
dev_id = chip->read_byte(mtd);//讀設(shè)備ID
for (i = 0; nand_flash_ids[i].name != NULL; i++)//根據(jù)設(shè)備id在nand_flash_ids[i]數(shù)組中找到其類型
{
if (dev_id == nand_flash_ids[i].id)
{
type = &nand_flash_ids[i];
break;
}
}
nand_scan_tail(mtd);//這里設(shè)置了讀寫和擦除函數(shù)
mtd->erase = nand_erase;
mtd->read = nand_read;
mtd->write = nand_write;
s3c2410_nand_add_partition(info, nmtd, sets);//添加分區(qū)
add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
add_mtd_device(&mtd->mtd);
list_for_each(this, &mtd_notifiers)//詳見注釋1
{
struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
not->add(mtd);
}
注釋1:
list_for_each(this, &mtd_notifiers)
{
struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
not->add(mtd);
}
首先我們要搞清楚這個(gè)宏:list_entry(this, struct mtd_notifier, list);它會返回一個(gè)指向mtd_notifier結(jié)構(gòu)體的指針
這樣我們就明白了,我們需要知道在哪里定義了mtd_notifier這個(gè)東西,這樣才能知道它的成員add函數(shù)。我們發(fā)現(xiàn):
void register_mtd_user (struct mtd_notifier *new)
{
..........................................................
list_add(&new->list, &mtd_notifiers);
...........................................................
}
那么是誰調(diào)用了register_mtd_use函數(shù)呢?我們發(fā)現(xiàn):
driversmtdMtd_blkdevs.c和driversmtdMtdchar.c文件里面都調(diào)用了這個(gè)函數(shù),這兩個(gè)文件一個(gè)對應(yīng)字符設(shè)備一個(gè)對應(yīng)塊設(shè)備,這也說明了nandflash既可以作為字符設(shè)備,也可以作為塊設(shè)備。我們先進(jìn)入字符設(shè)備看看吧:
很快我們發(fā)現(xiàn)了add函數(shù)的定義:
static void mtd_notify_add(struct mtd_info* mtd)
{
if (!mtd)
return;
class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),NULL, "mtd%d", mtd->index);//創(chuàng)建設(shè)備節(jié)點(diǎn)
class_device_create(mtd_class, NULL,MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),NULL, "mtd%dro", mtd->index);//創(chuàng)建只讀設(shè)備節(jié)點(diǎn)
}
我們有必要分析一下這個(gè)文件:
register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops);//注冊字符設(shè)備
class_create(THIS_MODULE, "mtd");//創(chuàng)建類,但是并沒有在類下創(chuàng)建設(shè)備節(jié)點(diǎn)哦,這個(gè)節(jié)點(diǎn)要在適當(dāng)?shù)臅r(shí)候才來創(chuàng)建
mtd_notify_add(struct mtd_info* mtd);
我們再來看一下塊設(shè)備驅(qū)動的吧:
很快我們就發(fā)下了add函數(shù)的定義:
static void blktrans_notify_add(struct mtd_info *mtd)
{
struct list_head *this;
if (mtd->type == MTD_ABSENT)
return;
list_for_each(this, &blktrans_majors) {
struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
tr->add_mtd(tr, mtd);
}
}
不過我們好像還得繼續(xù)向上搜尋,我們要看一看是誰設(shè)置了blktrans_majors:
int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
..........................................................
list_add(&tr->list, &blktrans_majors);
..........................................................
}
那么我們還要看看register_mtd_blktrans這個(gè)函數(shù)在哪里調(diào)用:
我們在driversmtdMtdblock.c文件里面發(fā)現(xiàn)了調(diào)用:
很快我們找到了add函數(shù)的定義:
mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
add_mtd_blktrans_dev(dev);
alloc_disk(1 << tr->part_bits);//申請磁盤,一下就可以看出和我們上節(jié)課編寫的驅(qū)動程序掛鉤了哦!
gd->queue = tr->blkcore_priv->rq;//設(shè)置隊(duì)列
add_disk;//注冊磁盤
針對上面的分析,我們在這里整理一下,首先來說一說將nandflash當(dāng)作塊設(shè)備來用的時(shí)候,其工作流程:
我們其實(shí)是分成了幾個(gè)層次的,一個(gè)是塊設(shè)備,我們在之前已經(jīng)知道了,應(yīng)用程序?qū)K設(shè)備的讀寫請求被放放入隊(duì)列里面,也就是說塊設(shè)備對請求作了優(yōu)化。而我們從s3c2410_nand_add_partition函數(shù)分析出來,mtdblock_add_mtd里面注冊了這個(gè)隊(duì)列,那么請求就可以放在這個(gè)隊(duì)列里面,并且這個(gè)函數(shù)里面還分配的磁盤并且注冊了磁盤。下面還差