二、Linux中Flash硬件知識(略)
二、Linux中Flash軟件知識
- 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 [options] name {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原始設備層又提供了哪些接口呢?下面開始瞭解。 - 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)個設備
-
- 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;
};
- MTD子系統中重要的一些數據結構: