我的首个驱动移植——flash驱动(一)

二、Linux中Flash硬件知识(略)

二、Linux中Flash软件知识

  1. Linux MTD子系统:

      在Linux系统中,提供了MTD(内存技术设备)子系统来建立Flash针对Linux的统一、抽象的接口。MTD子系统将上层文件系统与底层Flash硬件进行了隔离,使Flash驱动开发者无需再关心Flash作为字符设备或者块设备与Linux内核的接口。MTD将Linux系统Flash设备驱动及接口分成了4个层次,如图所示,从上往下分别为:设备节点、MTD设备层、MTD原始设备层和Flash硬件驱动层。


    设备节点:用户在/dev目录下使用mknod命令建立MTD字符设备节点(主设备号为90),或者MTD块设备节点(主设备号为31),使用该设备节点即可访问MTD设备。
               mknod [optionsname {bc} major minor 
               b:块专用文件             c:字符专用文件
    MTD设备层:基于MTD原始设备层,系统将MTD设备可以定义为MTD字符设备(在/mtd/mtdchar.c中实现)和MTD块设备(在/mtd/mtdblock.c中实现)。

    MTD原始设备层:MTD原始设备层由两个部分组成,分别是MTD原始设备的通用代码和各个特定的Flash的数据,如分区信息。

    Flash硬件驱动层:Flash硬件驱动层负责对Flash硬件的读、写和擦除操作。MTD设备的Nor Flash芯片驱动一般位于drivers/mtd/chips/子目录下,Nand Flash芯片的驱动则位于drivers/mtd/nand/子目录下。

    综合上述我们可知,MTD子系统已经对Flash设备对于上层的应用进行了封装,我们在写硬件驱动的时候直接调用MTD原始设备层提供的接口函数做相应的操作即可。那么,对于MTD设备层,MTD原始设备层提供了哪些接口呢?对于Flash硬件驱动层,MTD原始设备层又提供了哪些接口呢?下面开始了解。
  2. MTD子系统接口:

      在MTD子系统中,MTD设备层、MTD原始设备层和Flash硬件驱动层之间的接口关系如下图所示:

    从上图可知,MTD设备层是通过原始设备层提供的接口来注册MTD字符设备或MTD块设备的,同样,驱动工程师要编写的Flash硬件驱动也是通过原始设备层提供的接口来添加MTD设备和MTD分区的,分别如下:

    //使用这两个接口函数进行添加和删除MTD设备
    int add_mtd_device(struct mtd_info *mtd);
    int del_mtd_device(struct mtd_info *mtd)

     

    //使用这两个接口函数进行添加和删除MTD分区
    int add_mtd_partitions(struct mtd_info *master, struct mtd_partition *parts, int nbparts);
    int del_mtd_partitions(struct mtd_info *master)



    在MTD中,一个MTD原始设备用mtd_info结构体来表示,定义在include/linux/mtd/mtd.h中;一个MTD原始设备分区用mtd_part结构体来表示,定义在drivers/mtd/mtdpart.c中。其中每个分区也被认为是一个mtd_info,比如:有一个MTD原始设备,上面有3个分区,那么将共有3个mtd_info,而这3个mtd_info的指针将被存放在mtd_table的数组中进行管理,定义在drivers/mtd/mtdcore.h中,如下所示:

    extern struct mtd_info *mtd_table[MAX_MTD_DEVICES];//最多有MAX_MTD_DEVICES(默认定义为32)个设备



    1. MTD子系统中重要的一些数据结构:

      struct mtd_info 
      {
          //硬件设备的类型,如:MTD_RAM,MTD_ROM,MTD_NORFlash,MTD_NANDFlash,MTD_PEROM等
          u_char type;
          
          //设备支持的选项,如:MTD_ERASEABLE(可擦除),MTD_WRITEB_WRITEALBE(可编程),
          //MTD_XIP(可片内执行),MTD_OOB(NAND额外数据),MTD_ECC(支持自动ECC)等
          uint32_t flags;
          
          uint64_t size;//MTD设备的大小
          uint32_t erasesize;//主要的擦除块大小(注意:同一个MTD设备可能有几种不同的erasesize)
          uint32_t writesize;//编程块大小
          uint32_t oobsize;//OOB数据大小
          uint32_t oobavail;

          unsigned int erasesize_shift;
          unsigned int writesize_shift;
          unsigned int erasesize_mask;
          unsigned int writesize_mask;

          const char *name;
          int index;

          struct nand_ecclayout *ecclayout;//ECC布局结构

          int numeraseregions;//擦除区域的个数,通常为1
          struct mtd_erase_region_info *eraseregions;
      //擦除区域的指针

          //此方法将一个erase_info结构放入擦除队列中(此处的定义不是很明白,貌似可以看成是函数)
          int (*erase) (struct mtd_info *mtd, struct erase_info *instr);

          //point和unpoint方法分别用于允许和禁止芯片内执行(eXecute-In-Place,简称XIP),如果unpoint为NULL,则表示禁止XIP
          int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, void **virt, resource_size_t *phys);
          void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);

          //如果不为NULL,则表示允许无MMU单元的虚拟地址映射
          unsigned long (*get_unmapped_area) (struct mtd_info *mtd,unsigned long len,unsigned long offset,unsigned long flags);
          struct backing_dev_info *backing_dev_info;

          //read和write分别用于MTD设备的读和写
          int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
          int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
          int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

          //read_oob和write_oob分别用于读写MTD设备的OOB数据
          int (*read_oob) (struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops);
          int (*write_oob) (struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops);

          //一下几个方法是用于实现访问一些受保护的寄存器(一般这只是出现在某些特定的Flash设备上)
          int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
          int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
          int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
          int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
          int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
          int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);

          //基于kvec的形式写
          int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);

          //实现MTD设备的同步操作
          void (*sync) (struct mtd_info *mtd);

          //实现特定芯片的加锁和解锁
          int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
          int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);

          //实现支持电源管理
          int (*suspend) (struct mtd_info *mtd);
          void (*resume) (struct mtd_info *mtd);

          //坏块管理功能
          int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
          int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);

          //默认重启的MTD设备工作模式
          struct notifier_block reboot_notifier;

          //用于记录ECC状态的信息
          struct mtd_ecc_stats ecc_stats;
          
          /* Subpage shift (NAND) */
          int subpage_sft;

          //私有数据,注意是void类型的指针
          void *priv;

          struct module *owner;
          struct device dev;
          int usecount;//记录用户的个数

          //这两个方法用于设备驱动的回调,可以根据具体需要来决定是否实现他们
          int (*get_device) (struct mtd_info *mtd);
          void (*put_device) (struct mtd_info *mtd);
      };


      struct mtd_part 
      {
          struct mtd_info mtd;    //本分区信息(会被加入到mtd_table中,其大部分成员由其master决定)
          struct mtd_info *master;//该分区的主分区(不作为一个mtd_info加入到mtd_table。这也解释了上面的一个比喻,1个原始设备上有3个分区,最后将只有3个mtd_info加入到mtd_table而不是4个)
          uint64_t offset;        //该分区的偏移地址
          int index;              //该分区号
          struct list_head list;    
          int registered;                    
      };


 

发布了40 篇原创文章 · 获赞 4 · 访问量 6万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章