我们先查看内核的启动信息,以搞清楚从哪个文件着手来分析:
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
结果我们在drivers\mtd\nand\s3c2410.c文件里发现了打印这句话的函数:
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 Electronics\n");
platform_driver_register(&s3c2412_nand_driver);
platform_driver_register(&s3c2440_nand_driver);
return platform_driver_register(&s3c2410_nand_driver);
}
我们看到了,nandflash采用的是平台总线设备机制,当发现名字是s3c2440-nand的设备的时候,就会调用probe函数,那么我们就从probe函数入手了:
s3c24xx_nand_probe
s3c2410_nand_inithw(info, pdev);//初始化硬件
s3c2410_nand_init_chip(info, nmtd, sets);//初始化芯片,这里设置了chip的一些信息
nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);//扫描芯片
nand_scan_ident(struct mtd_info *mtd, int maxchips)
nand_set_defaults(chip, busw);//设置默认函数
nand_get_flash_type(mtd, chip, busw, &nand_maf_id);//获取nandflash类型
chip->select_chip(mtd, 0);//选中芯片
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//发出读ID的命令,NAND_CMD_READID为90h
*maf_id = chip->read_byte(mtd);//读厂家ID
dev_id = chip->read_byte(mtd);//读设备ID
for (i = 0; nand_flash_ids[i].name != NULL; i++) //根据设备id在nand_flash_ids[i]数组中找到其类型
{
if (dev_id == nand_flash_ids[i].id)
{
type = &nand_flash_ids[i];
break;
}
}
nand_scan_tail(mtd);//这里设置了读写和擦除函数
mtd->erase = nand_erase;
mtd->read = nand_read;
mtd->write = nand_write;
s3c2410_nand_add_partition(info, nmtd, sets);//添加分区
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结构体的指针
这样我们就明白了,我们需要知道在哪里定义了mtd_notifier这个东西,这样才能知道它的成员add函数。我们发现:
void register_mtd_user (struct mtd_notifier *new)
{
..........................................................
list_add(&new->list, &mtd_notifiers);
...........................................................
}
那么是谁调用了 register_mtd_use 函数呢?我们发现:
drivers\mtd\Mtd_blkdevs.c和drivers\mtd\Mtdchar.c文件里面都调用了这个函数,这两个文件一个对应字符设备一个对应块设备,这也说明了nandflash既可以作为字符设备,也可以作为块设备。我们先进入字符设备看看吧:
很快我们发现了add函数的定义:
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);//创建设备节点
class_device_create(mtd_class, NULL,MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),NULL, "mtd%dro", mtd->index);//创建只读设备节点
}
我们有必要分析一下这个文件:
register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops);//注册字符设备
class_create(THIS_MODULE, "mtd");//创建类,但是并没有在类下创建设备节点哦,这个节点要在适当的时候才来创建
mtd_notify_add(struct mtd_info* mtd);
我们再来看一下块设备驱动的吧:
很快我们就发下了add函数的定义:
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);
}
}
不过我们好像还得继续向上搜寻,我们要看一看是谁设置了blktrans_majors:
int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
..........................................................
list_add(&tr->list, &blktrans_majors);
..........................................................
}
那么我们还要看看register_mtd_blktrans这个函数在哪里调用:
我们在drivers\mtd\Mtdblock.c文件里面发现了调用:
很快我们找到了add函数的定义:
mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
add_mtd_blktrans_dev(dev);
alloc_disk(1 << tr->part_bits);//申请磁盘,一下就可以看出和我们上节课编写的驱动程序挂钩了哦!
gd->queue = tr->blkcore_priv->rq;//设置队列
add_disk;//注册磁盘
针对上面的分析,我们在这里整理一下,首先来说一说将nandflash当作块设备来用的时候,其工作流程:
我们其实是分成了几个层次的,一个是块设备,我们在之前已经知道了,应用程序对块设备的读写请求被放放入队列里面,也就是说块设备对请求作了优化。而我们从s3c2410_nand_add_partition函数分析出来,mtdblock_add_mtd里面注册了这个队列,那么请求就可以放在这个队列里面,并且这个函数里面还分配的磁盘并且注册了磁盘。下面还差一步,那就是对硬件的操作,它在哪里呢?它就在nand_scan旗下的函数里面实现,里面定义了一些读写函数以及擦除函数,可以对硬件进行操作。其实这个框架和我们之前用内存模拟磁盘的框架式一致的,不过这里比较复杂,而且用了分层的概念。
不过这里面有几个结构体我们还是要说一下的:
struct nand_chip:用于描述芯片参数
struct mtd_info:定义了大量的MTD设备信息以及操作函数
关于字符设备驱动程序就比较简单了:
在nand_scan旗下的函数里面完成了注册,并设置了操作函数,那样就可以对nandflash进行操作了。
上一篇:DMA通道的使用
下一篇:使用Jtag烧写uboot,以及bootdelay=0的解决办法
推荐阅读最新更新时间:2024-03-16 15:23