天天看點

linux塊裝置驅動程式分析之 nor flash驅動分析 以及使用記憶體模拟 nor flash

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");

繼續閱讀