struct map_info {
char *name;
unsigned long size;
resource_size_t phys;
#define NO_XIP (-1UL)
void __iomem *virt;
void *cached;
int bankwidth;
#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
map_word (*read)(struct map_info *, unsigned long);
void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);
void (*write)(struct map_info *, const map_word, unsigned long);
void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
#endif
void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
void (*set_vpp)(struct map_info *, int);
unsigned long map_priv_1;
unsigned long map_priv_2;
void *fldrv_priv;
struct mtd_chip_driver *fldrv;
};
1 从nor 驱动程序的入口函数开始,分析需要自己写的代码部分
xx_nor_init
>struct map_info *nor_map = kzmalloc()//分配一个map_info的结构体,然后填充
>simple_map_init(nor_map)//填充map_info结构体的read copy_from write copy_to 指针函数
>struct mtd_info *nor_mtd = do_map_probe("cfi_probe", nor_map);
do_map_probe
>struct mtd_chip_driver *drv=get_mtd_chip_driver(name)//指到芯片相应的结构体变量
+get_mtd_chip_driver
+ >list_for_each(pos, &chip_drvs_list) //遍历mtd_chip_driver结构体链表 chip_drvs_list 中的每一个元素
+ >strcmp(this->name, name) //比较mtd_chip_driver结构体中的名字和传入的参数名字是否相同,
+ //相同就把相应的mtd_chip_driver结构体变量的地址返回
>struct mtd_info *ret = drv->probe(map);//调用mtd_chip_driver结构体变量提供的probe函数
>module_put(drv->module);//减小引用计数
>return ret;返回probe函数得到的mtd_info结构体指针
2 在代码中,先使用cfi 操作的方式调用do_map_probe函数,如果不支持的话,再使用jedec操作方式调用do_map_probe函数
那么,接下来就分析cfi 的操作方式
a.cfi方式的nor flash操作
分析linux-2.6.22源码分析\linux-2.6.22\drivers\mtd\chips\cfi_probe.c 这个文件
先定义了一个全局变量结构体
static struct mtd_chip_driver cfi_chipdrv = {
.probe = cfi_probe,
.name = "cfi_probe",
.module = THIS_MODULE
};
模块的入口函数static int __init cfi_probe_init(void) //比以前的驱动入口函数多了个__init,个人估计是这个
//c文件应该是被编译进内核了,也就是比用户的驱动程序
//再早地运行
>register_mtd_chip_driver(&cfi_chipdrv); //把cfi_chipdrv加入到mtd_chip_driver结构体链表 chip_drvs_list 中
>list_add(&drv->list, &chip_drvs_list);
那么当用户的驱动程序调用do_map_probe("cfi_probe", nor_map)时,就用把“cfi_probe” 与mtd_chip_driver结构体链表
chip_drvs_list 中的每一个元素的.name项比较,相同就把对应的元素返回,然后调用它的probe函数。
所以当调用了do_map_probe("cfi_probe", nor_map)后,再调用ret->probe()函数,其实就是在调用cfi_probe()。
cfi_probe.c中还定义了一个如下的全局变量
static struct chip_probe cfi_chip_probe = {
.name = "CFI",
.probe_chip = cfi_probe_chip
};
分析cfi_probe的代码:
cfi_probe(struct map_info *map)
>mtd_do_chip_probe(map, &cfi_chip_probe)
+ >struct cfi_private *cfi= genprobe_ident_chips(map, &cfi_chip_probe);//首先探测下看是否支持CFI
+ >genprobe_new_chip(map, &cfi_chip_probe, &cfi)
+ >cfi_chip_probe->probe_chip() //调用chip_probe结构体的probe_chip函数,
+ || //这里也就是: cfi_probe_chip
+ =cfi_probe_chip() //在cfi_probe_chip()函数中实现了CFI方式 对nor flash的探测。
>check_cmd_set(map, 1)
>cfi_cmdset_0020 //CONFIG_MTD_CFI_STAA
>cfi_cmdset_unknown //default
>cfi_cmdset_0001 //CONFIG_MTD_CFI_INTELEXT
>cfi_cmdset_0002 //CONFIG_MTD_CFI_AMDSTD
//填充函数完成mtd_info结构体的操作函数
mtd->erase = cfi_amdstd_erase_varsize;
mtd->write = cfi_amdstd_write_words;
mtd->read = cfi_amdstd_read;
mtd->sync = cfi_amdstd_sync;
mtd->suspend = cfi_amdstd_suspend;
mtd->resume = cfi_amdstd_resume;
mtd->flags = MTD_CAP_NORFLASH;
由nand flash相关驱动的代码分析可知,当应用程序对块设备如nor flash,进行读写等操作时,文件系统经过
一系列的函数调用,最后会调用块设备驱动程序里的mtd_info结构体的erase write read等操作函数,上述函数
则正好完成了相应的接口,从而完成对Nor flash硬件的读、写与擦除。
到此就完成了CFI方式下对Nor Flash的操作
b.jedec方式的nor flash操作
开始的部分和CFI方式相类似:
static struct mtd_chip_driver jedec_chipdrv = {
.probe = jedec_probe,
.name = "jedec_probe",
.module = THIS_MODULE
};
static int __init jedec_probe_init(void)
>register_mtd_chip_driver(&jedec_chipdrv) //加入到chip_drvs_list链表中
>list_add(&drv->list, &chip_drvs_list);
用户提供的驱动调用do_map_probe后,会比较传入的"操作方式"与链表中的.name进行比较,如果有jedec_probe
则最终会调用jedec_probe
jedec_probe
>mtd_do_chip_probe
>struct mtd_info *mtd = check_cmd_set(map, 1)
//看到这里发现,又和CFI方式殊途同归啦!接下来又是设置mtd_info结构体中相关的操作函数
3.接下来,就用内存来模拟一个nor flash
闲话不说,上代码:
virtual_nor_probe.c如下:
/*
*参考drivers\mtd\chips\Cfi_probe.c
*参考drivers\mtd\chips\map_ram.c
*此程序还有一点瑕疵:就是rmmod时不能卸载驱动,这是为什么呢?
*
*/
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/gfp.h>
#include<linux/slab.h>
#include<linux/mtd/map.h>
#include<linux/mtd/mtd.h>
#include<asm/page.h>
static struct mtd_info * v_nor_probe(struct map_info *map);
static struct mtd_chip_driver v_nor_drv = {
.probe = v_nor_probe,
.name = "v_nor_probe",
.module = THIS_MODULE,
};
static int v_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct map_info *map = mtd->priv;
map_word allff;/*定义一个nor位宽相同的数组*/
unsigned long i;
allff = map_word_ff(map);/*全写入1*/
for(i=0;i<instr->len;i+= map_bankwidth(map)) /*擦除:也就是全写入1*/
map_write(map, allff, instr->addr + i);
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return 0;
}
static int v_nor_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
struct map_info *map = mtd->priv;
map_copy_from(map, buf, from, len);
*retlen = len;
return 0;
}
static int v_nor_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
{
struct map_info *map = mtd->priv;
map_copy_to(map, to, buf, len);
*retlen = len;
return 0;
}
static void v_nor_sync(struct mtd_info *mtd)
{
/*nothing to do*/
static int cnt;
printk(" *kernel virtual sync: %d\n",++cnt);
}
static struct mtd_info * v_nor_probe(struct map_info *map)
{
struct mtd_info *mtd;
mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
if(!mtd){
printk("v_nor_probe:kzalloc mtd_info failed\n");
return NULL;
}
map->fldrv = &v_nor_drv;
mtd->priv = map;
mtd->name = map->name;
mtd->size = map->size;
mtd->type = MTD_RAM;
mtd->erase = v_nor_erase;
mtd->read = v_nor_read;
mtd->write = v_nor_write;
mtd->sync = v_nor_sync;
mtd->flags = MTD_CAP_RAM;
mtd->writesize = 1;
mtd->erasesize = PAGE_SIZE;
while( mtd->size & (mtd->erasesize -1) )
mtd->erasesize >>= 1;
__module_get(THIS_MODULE);
return mtd;
}
static int __init virtual_nor_init(void)
{
register_mtd_chip_driver(&v_nor_drv);
return 0;
}
static void __exit virtual_nor_exit(void)
{
unregister_mtd_chip_driver(&v_nor_drv);
}
module_init(virtual_nor_init);
module_exit(virtual_nor_exit);
MODULE_LICENSE("GPL");
virtual_nor_dev.c 如下:
/*
* 其实可以把这两个程序写成一个程序,或者写成一个平台驱动
*/
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include<linux/slab.h>
#include<linux/mtd/mtd.h>
#include<linux/mtd/map.h>
#include<linux/mtd/partitions.h>
#include<linux/gfp.h>
#include<asm/sizes.h>
#define V_NOR_SIZE 0x100000
static struct mtd_info *v_nor_mtd;
static struct map_info *v_nor_map;
static char *v_nor_flash;
static struct mtd_partition v_nor_parts[]= {
[0] = {
.name = "vitual_nor1",
.offset = 0,
.size = SZ_256K,
},
[1] = {
.name = "vitual_nor2",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
},
};
static int gq_nor_init(void)
{
v_nor_flash = kzalloc(V_NOR_SIZE, GFP_KERNEL);
if(!v_nor_flash){
printk("malloc memory vitual nor failed\n");
return -1;
}
/* 1 为结构体分配内存*/
v_nor_map = kzalloc(sizeof(struct map_info), GFP_KERNEL);
if(!v_nor_map){
printk("nor:map_info alloc failed\n");
return -1;
}
/* 2 设置结构体*/
v_nor_map->name = "vitual_gq_nor";
v_nor_map->size = 0x100000;/*nor flash的容量,要大于2M==实际大小*/
v_nor_map->phys = (resource_size_t)v_nor_flash;/*虚拟磁盘"物理地址"*/
v_nor_map->bankwidth = 2;/*位宽=bankwidth*8 */
v_nor_map->virt = v_nor_flash;/*v_nor_flash本身就是虚拟地址,所以不需要再进行映射*/
#if 0
v_nor_map->virt = ioremap(nor_map->phys,nor_map->size);/*为nor flash映射内存空间*/
if(!nor_map->virt){
printk("nor flash map memory failed\n");
kfree(nor_map);
return -2;
}
#endif
simple_map_init(v_nor_map);
v_nor_mtd = do_map_probe("v_nor_probe", v_nor_map);
if(!v_nor_mtd)
{
v_nor_mtd = do_map_probe("jedec_probe", v_nor_map);
if(!v_nor_mtd){
printk("do_map_probe failed\n");
kfree(v_nor_mtd);
return -3;
}
}
v_nor_mtd->owner = THIS_MODULE;
/* 3 add_mtd_partitions*/
if(add_mtd_partitions(v_nor_mtd,v_nor_parts,2)){
printk("add_mtd_partitions failed \n");
return -4;
}
return 0;
}
static void gq_nor_exit(void)
{
del_mtd_partitions(v_nor_mtd);
// iounmap(nor_map->virt);
kfree(v_nor_map);
kfree(v_nor_mtd);
}
module_init(gq_nor_init);
module_exit(gq_nor_exit);
MODULE_LICENSE("GPL");