硬件環境: 飛凌OK6410,256MB DDR,2GB NAND Flash、 NAND Flash 型號:K9G8G08U9A 、 分析源碼:Linux 2.6.36.2 內核源碼。
一、 MTD 設備驅動。
1、先來簡單介紹一下MTD
在Linux 系統中, 提供了MTD(Memory Technology Device , 內存技術設備)系統來建立 Flash 針對 Linux 的系統、抽象的接口, MTD 將文件系統 與 底層的Flash
存儲器進行了隔離, 使 Flash 驅動工程師 無需關心Flash 作為字符設備和 塊 設備與 LInux內核的接口。
2、在引入MTD 後Linux 系統中的Flash 設備及接口可分為4層, 從上到下依次是:設備節點、MTD 設備層、MTD原始設備層 和 硬件驅動層。 這 4 層的作用定義如下:
1-> 硬件驅動層: Flash 硬件驅動層負責 Flash 硬件設備的讀、寫、擦除, LInux MTD 設備的 NOR Flash 芯片驅動位於 drivers/mtd/chips 子目錄下, NAND Flash
的驅動程序則 位於 drivers/mtd/nand 子目錄下。
2->MTD 原始設備層: MTD原始設備層由兩部分組成, 一部分是MTD 原始設備的通用代碼, 另一部分是各個特定 Flash 的數據,例如分區。
3->MTD設備層: 基於MTD 原始設備,Linux 系統可以定義出 MTD 的塊設備的結構(主設備號 31) 和 字符設備 (設備號 90) ,構成MTD 設備層, MTD 字符設備定義
在mtdchar.c 中實現,MTD 塊設備則是定義在一個描述MTD 塊設備的結構 mtdblk_dev ,並聲明了一個名為 mtdblks 的指針數組,這個數組 中的每個mtdblk_dev
和 mtd_table 中的每一個mtd_info 一一對應。
4->設備節點: 通過mknod 在/dev 子目錄下建立MTD字符設備節點 和 塊設備節點,用戶通過訪問此此設備節點即可訪問 MTD 字符設備和塊設備。
3、分析Linux MTD 系統接口 mtd_info 結構體代碼分析 此結構體定義在 ./include/linux/mtd/mtd.h 中
關鍵詞詞解析:
XIP :XIP eXecute In Place,即芯片內執行,指應用程序可以直接在flash閃存內運行,不必再把代碼讀到系統RAM中。flash內執行
是指nor flash 不需要初始化,可以直接在flash內執行代碼。但往往只執行部分代碼,比如初始化RAM.
OOB :Out Of Brower 傳輸層協議使用帶外數據(out-of-band,OOB)來發送一些重要的數據,如果通信一方有重要的數據需要通知對方時,協議能夠將這些數據
快速地發送到對方.為了發送這些數據
iovec-base : iovec 結構體基礎。struct iovec定義了一個向量元素。通常,這個結構用作一個多元素的數組。對於每一個傳輸的元素,指針成員iov_base指向
一個緩沖區,這個緩沖區是存放的是readv所接收的數據或是writev將要發送的數據。成員iov_len在各種情況下分別確定了接收的最大長度以及實際寫入的長度。
Sync : 函數, 函數說明:此函數負責將系統緩沖區的內容寫回磁盤,以確保數據同步。
- struct mtd_info {
- u_char type; // 內存技術的類型
- uint32_t flags; // 標志位
- uint64_t size; // Total size of the MTD 、mtd 設備的大小
-
- /* "Major" erase size for the device. Na茂ve users may take this
- * to be the only erase size available, or may use the more detailed
- * information below if they desire
- */
- uint32_t erasesize; // 主要的擦除塊大小 erase size of main block
- /* Minimal writable flash unit size. In case of NOR flash it is 1 (even
- * though individual bits can be cleared), in case of NAND flash it is
- * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
- * it is of ECC block size, etc. It is illegal to have writesize = 0.
- * Any driver registering a struct mtd_info must ensure a writesize of
- * 1 or larger.
- */
- uint32_t writesize; // 最小的可寫單元的字節數
-
- uint32_t oobsize; // Amount of OOB data per block (e.g. 16) OOB 字節數
- uint32_t oobavail; // Available OOB bytes per block 可用OBB 字節數
-
- /*
- * If erasesize is a power of 2 then the shift is stored in
- * erasesize_shift otherwise erasesize_shift is zero. Ditto writesize.
- */
- unsigned int erasesize_shift;
- unsigned int writesize_shift;
- /* Masks based on erasesize_shift and writesize_shift */
- unsigned int erasesize_mask;
- unsigned int writesize_mask;
-
- // Kernel-only stuff starts here.
- const char *name;
- int index;
-
- /* ecc layout structure pointer - read only ! */
- struct nand_ecclayout *ecclayout; // ECC 布局結構體指針
-
- /* Data for variable erase regions. If numeraseregions is zero,
- * it means that the whole device has erasesize as given above.
- */
- int numeraseregions; // 不同的erasesize 的區域 數目通常是1
- struct mtd_erase_region_info *eraseregions;
-
- /*
- * Erase is an asynchronous operation. Device drivers are supposed
- * to call instr->callback() whenever the operation completes, even
- * if it completes with a failure.
- * Callers are supposed to pass a callback function and wait for it
- * to be called before writing to the block.
- */
- int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
-
- /* This stuff for eXecute-In-Place */
- /* phys is optional and may be set to NULL */
- int (*point) (struct mtd_info *mtd, loff_t from, size_t len, // 針對 eXecute-In- Place
- size_t *retlen, void **virt, resource_size_t *phys);
-
- /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
- void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len); // 如果unpoint 為空,不允許 XIP
-
- /* Allow NOMMU mmap() to directly map the device (if not NULL)
- * - return the address to which the offset maps
- * - return -ENOSYS to indicate refusal to do the mapping
- */
- unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
- unsigned long len,
- unsigned long offset,
- unsigned long flags);
-
- /* Backing device capabilities for this device
- * - provides mmap capabilities
- */
- struct backing_dev_info *backing_dev_info;
-
-
- int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); // 讀 flash
- int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); // 寫 flash
-
- /* In blackbox flight recorder like scenarios we want to make successful
- writes in interrupt context. panic_write() is only intended to be
- called when its known the kernel is about to panic and we need the
- write to succeed. Since the kernel is not going to be running for much
- longer, this function can break locks and delay to ensure the write
- succeeds (but not sleep). */
-
- int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); // Kernel panic 時序讀寫
-
- int (*read_oob) (struct mtd_info *mtd, loff_t from, // 讀 out-of-band
- struct mtd_oob_ops *ops);
- int (*write_oob) (struct mtd_info *mtd, loff_t to, // 寫 out-of-band
- struct mtd_oob_ops *ops);
-
- /*
- * Methods to access the protection register area, present in some
- * flash devices. The user data is one time programmable but the
- * factory data is read only.
- */
- 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-based read/write methods.
- NB: The 'count' parameter is the number of _vectors_, each of
- which contains an (ofs, len) tuple.
- */
- int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); // iovec-based 讀寫函數
-
- /* Sync */
- void (*sync) (struct mtd_info *mtd); // Sync
-
- /* Chip-supported device locking */
- 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 (*is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
-
- /* Power Management functions */
- int (*suspend) (struct mtd_info *mtd); // 電源管理函數
- void (*resume) (struct mtd_info *mtd);
-
- /* Bad block management functions */
- int (*block_isbad) (struct mtd_info *mtd, loff_t ofs); // 壞塊管理函數
- int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
-
- struct notifier_block reboot_notifier; /* default mode before reboot */
-
- /* ECC status information */
- struct mtd_ecc_stats ecc_stats;
- /* Subpage shift (NAND) */
- int subpage_sft;
-
- void *priv; // 私有函數
-
- struct module *owner;
- struct device dev;
- int usecount;
-
- /* If the driver is something smart, like UBI, it may need to maintain
- * its own reference counting. The below functions are only for driver.
- * The driver may register its callbacks. These callbacks are not
- * supposed to be called by MTD users */
- int (*get_device) (struct mtd_info *mtd);
- void (*put_device) (struct mtd_info *mtd);
- };
mtd_info 中的 read(). write(). read_oob(). write_oob(). erase() 是 MTD 設備驅動主要實現的函數。在在後面我將要介紹的nand flahs 驅動中幾乎看不到mtd_info
的成員函數(也就是說這些函數對於Flash 芯片來說是透明的),這是因為在Linux MTD 下層實現了針對 NOR、NAND Flsh 的同游mtd_info 成員函數。
Flash 驅動中使用如下兩個函數來注冊 和注銷MTD 設備:
int add_mtd_device(struct mtd_info *mtd);
int del_mtd_device (struct mtd_info *mtd)