我的首個驅動移植——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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章