塊設(shè)備驅(qū)動程序之nandflash——基本框架
我們先查看內(nèi)核的啟動信息,以搞清楚從哪個文件著手來分析:
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è)備機制,當(dāng)發(fā)現(xiàn)名字是s3c2440-nand的設(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);
}
首先我們要搞清楚這個宏:list_entry(this, struct mtd_notifier, list);它會返回一個指向mtd_notifier結(jié)構(gòu)體的指針
這樣我們就明白了,我們需要知道在哪里定義了mtd_notifier這個東西,這樣才能知道它的成員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)用了這個函數(shù),這兩個文件一個對應(yīng)字符設(shè)備一個對應(yīng)塊設(shè)備,這也說明了nandflash既可以作為字符設(shè)備,也可以作為塊設(shè)備。我們先進入字符設(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é)點
class_device_create(mtd_class, NULL,MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),NULL, "mtd%dro", mtd->index);//創(chuàng)建只讀設(shè)備節(jié)點
}
我們有必要分析一下這個文件:
register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops);//注冊字符設(shè)備
class_create(THIS_MODULE, "mtd");//創(chuàng)建類,但是并沒有在類下創(chuàng)建設(shè)備節(jié)點哦,這個節(jié)點要在適當(dāng)?shù)臅r候才來創(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這個函數(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è)置隊列
add_disk;//注冊磁盤
針對上面的分析,我們在這里整理一下,首先來說一說將nandflash當(dāng)作塊設(shè)備來用的時候,其工作流程:
我們其實是分成了幾個層次的,一個是塊設(shè)備,我們在之前已經(jīng)知道了,應(yīng)用程序?qū)K設(shè)備的讀寫請求被放放入隊列里面,也就是說塊設(shè)備對請求作了優(yōu)化。而我們從s3c2410_nand_add_partition函數(shù)分析出來,mtdblock_add_mtd里面注冊了這個隊列,那么請求就可以放在這個隊列里面,并且這個函數(shù)里面還分配的磁盤并且注冊了磁盤。下面還差