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如下:
#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;
unsigned long i;
allff = map_word_ff(map);
for(i=0;i<instr->len;i+= map_bankwidth(map))
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)
{
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;
}
v_nor_map = kzalloc(sizeof(struct map_info), GFP_KERNEL);
if(!v_nor_map){
printk("nor:map_info alloc failed\n");
return -1;
}
v_nor_map->name = "vitual_gq_nor";
v_nor_map->size = 0x100000;
v_nor_map->phys = (resource_size_t)v_nor_flash;
v_nor_map->bankwidth = 2;
v_nor_map->virt = v_nor_flash;
#if 0
v_nor_map->virt = ioremap(nor_map->phys,nor_map->size);
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;
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");