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如下:

/*
 *参考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");

   


   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值