1. 程式人生 > >Linux MTD系統剖析【轉】

Linux MTD系統剖析【轉】

過程 當下 default targe lin 讀取內容 state yaffs2 lock

轉自:http://blog.csdn.net/lwj103862095/article/details/21545791

MTD,Memory Technology Device即內存技術設備,在Linux內核中,引入MTD層為NOR FLASH和NAND FLASH設備提供統一接口。MTD將文件系統與底層FLASH存儲器進行了隔離。

技術分享圖片

如上圖所示,MTD設備通常可分為四層,從上到下依次是:設備節點、MTD設備層、MTD原始設備層、硬件驅動層。

Flash硬件驅動層:Flash硬件驅動層負責對Flash硬件的讀、寫和擦除操作。MTD設備的Nand Flash芯片的驅動則drivers/mtd/nand/子目錄下,Nor Flash芯片驅動位於drivers/mtd/chips/子目錄下。

MTD原始設備層:用於描述MTD原始設備的數據結構是mtd_info,它定義了大量的關於MTD的數據和操作函數。其中mtdcore.c: MTD原始設備接口相關實現,mtdpart.c : MTD分區接口相關實現。

MTD設備層:基於MTD原始設備,linux系統可以定義出MTD的塊設備(主設備號31)字符設備(設備號90)。其中mtdchar.c : MTD字符設備接口相關實現,mtdblock.c : MTD塊設備接口相關實現。

設備節點:通過mknod在/dev子目錄下建立MTD塊設備節點(主設備號為31)MTD字符設備節點(主設備號為90)。通過訪問此設備節點即可訪問MTD字符設備和塊設備

MTD數據結構:

1.Linux內核使用mtd_info結構體表示MTD原始設備,這其中定義了大量關於MTD的數據和操作函數(後面將會看到),所有的mtd_info結構體存放在mtd_table結構體數據裏。在/drivers/mtd/mtdcore.c裏:

[cpp] view plain copy
  1. struct mtd_info *mtd_table[MAX_MTD_DEVICES];
2.Linux內核使用mtd_part結構體表示分區,其中mtd_info結構體成員用於描述該分區,大部分成員由其主分區mtd_part->master決定,各種函數也指向主分區的相應函數。

[cpp] view plain copy
  1. struct mtd_part {
  2. struct mtd_info mtd; /* 分區信息, 大部分由master決定 */
  3. struct mtd_info *master; /* 分區的主分區 */
  4. uint64_t offset; /* 分區的偏移地址 */
  5. int index; /* 分區號 (Linux3.0後不存在該字段) */
  6. struct list_head list; /* 將mtd_part鏈成一個鏈表mtd_partitons */
  7. int registered;
  8. };
mtd_info結構體主要成員,為了便於觀察,將重要的數據放在前面,不大重要的編寫在後面。

[cpp] view plain copy
  1. struct mtd_info {
  2. u_char type; /* MTD類型,包括MTD_NORFLASH,MTD_NANDFLASH等(可參考mtd-abi.h) */
  3. uint32_t flags; /* MTD屬性標誌,MTD_WRITEABLE,MTD_NO_ERASE等(可參考mtd-abi.h) */
  4. uint64_t size; /* mtd設備的大小 */
  5. uint32_t erasesize; /* MTD設備的擦除單元大小,對於NandFlash來說就是Block的大小 */
  6. uint32_t writesize; /* 寫大小, 對於norFlash是字節,對nandFlash為一頁 */
  7. uint32_t oobsize; /* OOB字節數 */
  8. uint32_t oobavail; /* 可用的OOB字節數 */
  9. unsigned int erasesize_shift; /* 默認為0,不重要 */
  10. unsigned int writesize_shift; /* 默認為0,不重要 */
  11. unsigned int erasesize_mask; /* 默認為1,不重要 */
  12. unsigned int writesize_mask; /* 默認為1,不重要 */
  13. const char *name; /* 名字, 不重要*/
  14. int index; /* 索引號,不重要 */
  15. int numeraseregions; /* 通常為1 */
  16. struct mtd_erase_region_info *eraseregions; /* 可變擦除區域 */
  17. void *priv; /* 設備私有數據指針,對於NandFlash來說指nand_chip結構體 */
  18. struct module *owner; /* 一般設置為THIS_MODULE */
  19. /* 擦除函數 */
  20. int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
  21. /* 讀寫flash函數 */
  22. int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
  23. int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
  24. /* 帶oob讀寫Flash函數 */
  25. int (*read_oob) (struct mtd_info *mtd, loff_t from,
  26. struct mtd_oob_ops *ops);
  27. int (*write_oob) (struct mtd_info *mtd, loff_t to,
  28. struct mtd_oob_ops *ops);
  29. int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
  30. int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
  31. int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
  32. int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
  33. int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
  34. int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
  35. int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
  36. int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
  37. /* Sync */
  38. void (*sync) (struct mtd_info *mtd);
  39. /* Chip-supported device locking */
  40. int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
  41. int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
  42. /* 電源管理函數 */
  43. int (*suspend) (struct mtd_info *mtd);
  44. void (*resume) (struct mtd_info *mtd);
  45. /* 壞塊管理函數 */
  46. int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
  47. int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
  48. void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
  49. unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
  50. unsigned long len,
  51. unsigned long offset,
  52. unsigned long flags);
  53. struct backing_dev_info *backing_dev_info;
  54. struct notifier_block reboot_notifier; /* default mode before reboot */
  55. /* ECC status information */
  56. struct mtd_ecc_stats ecc_stats;
  57. int subpage_sft;
  58. struct device dev;
  59. int usecount;
  60. int (*get_device) (struct mtd_info *mtd);
  61. void (*put_device) (struct mtd_info *mtd);
  62. };
mtd_info結構體中的read()、write()、read_oob()、write_oob()、erase()是MTD設備驅動要實現的主要函數,幸運的是Linux大牛已經幫我們實現了一套適合大部分FLASH設備的mtd_info成員函數。

如果MTD設備只有一個分區,那麽使用下面兩個函數註冊和註銷MTD設備。

[cpp] view plain copy
  1. int add_mtd_device(struct mtd_info *mtd)
  2. int del_mtd_device (struct mtd_info *mtd)
如果MTD設備存在其他分區,那麽使用下面兩個函數註冊和註銷MTD設備。
[cpp] view plain copy
  1. int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)
  2. int del_mtd_partitions(struct mtd_info *master)
其中mtd_partition結構體表示分區的信息 [cpp] view plain copy
  1. struct mtd_partition {
  2. char *name; /* 分區名,如TQ2440_Board_uboot、TQ2440_Board_kernel、TQ2440_Board_yaffs2 */
  3. uint64_t size; /* 分區大小 */
  4. uint64_t offset; /* 分區偏移值 */
  5. uint32_t mask_flags; /* 掩碼標識,不重要 */
  6. struct nand_ecclayout *ecclayout; /* OOB布局 */
  7. struct mtd_info **mtdp; /* pointer to store the MTD object */
  8. };
  9. 其中nand_ecclayout結構體:
  10. struct nand_ecclayout {
  11. __u32 eccbytes; /* ECC字節數 */
  12. __u32 eccpos[64]; /* ECC校驗碼在OOB區域存放位置 */
  13. __u32 oobavail;
  14. /* 除了ECC校驗碼之外可用的OOB字節數 */
  15. struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
  16. };
關於nand_ecclayout結構體實例,更多可參考drivers/mtd/nand/nand_base.c下的nand_oob_8、nand_oob_16、nand_oob_64實例。
MTD設備層:

mtd字符設備接口:

/drivers/mtd/mtdchar.c文件實現了MTD字符設備接口,通過它,可以直接訪問Flash設備,與前面的字符驅動一樣,通過file_operations結構體裏面的open()、read()、write()、ioctl()可以讀寫Flash,通過一系列IOCTL 命令可以獲取Flash 設備信息、擦除Flash、讀寫NAND 的OOB、獲取OOB layout 及檢查NAND 壞塊等(MEMGETINFO、MEMERASE、MEMREADOOB、MEMWRITEOOB、MEMGETBADBLOCK IOCRL)

mtd塊設備接口:

/drivers/mtd/mtdblock.c文件實現了MTD塊設備接口,主要原理是將Flash的erase block 中的數據在內存中建立映射,然後對其進行修改,最後擦除Flash 上的block,將內存中的映射塊寫入Flash 塊。整個過程被稱為read/modify/erase/rewrite 周期。 但是,這樣做是不安全的,當下列操作序列發生時,read/modify/erase/poweroff,就會丟失這個block 塊的數據。
MTD硬件驅動層:

Linux內核再MTD層下實現了通用的NAND驅動(/driver/mtd/nand/nand_base.c),因此芯片級的NAND驅動不再需要實現mtd_info結構體中的read()、write()、read_oob()、write_oob()等成員函數。

MTD使用nand_chip來表示一個NAND FLASH芯片, 該結構體包含了關於Nand Flash的地址信息,讀寫方法,ECC模式,硬件控制等一系列底層機制。

[cpp] view plain copy
  1. struct nand_chip {
  2. void __iomem *IO_ADDR_R; /* 讀8位I/O線地址 */
  3. void __iomem *IO_ADDR_W; /* 寫8位I/O線地址 */
  4. /* 從芯片中讀一個字節 */
  5. uint8_t (*read_byte)(struct mtd_info *mtd);
  6. /* 從芯片中讀一個字 */
  7. u16 (*read_word)(struct mtd_info *mtd);
  8. /* 將緩沖區內容寫入芯片 */
  9. void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
  10. /* 讀芯片讀取內容至緩沖區/ */
  11. void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
  12. /* 驗證芯片和寫入緩沖區中的數據 */
  13. int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
  14. /* 選中芯片 */
  15. void (*select_chip)(struct mtd_info *mtd, int chip);
  16. /* 檢測是否有壞塊 */
  17. int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
  18. /* 標記壞塊 */
  19. int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
  20. /* 命令、地址、數據控制函數 */
  21. void (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);
  22. /* 設備是否就緒 */
  23. int (*dev_ready)(struct mtd_info *mtd);
  24. /* 實現命令發送 */
  25. void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
  26. int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
  27. /* 擦除命令的處理 */
  28. void (*erase_cmd)(struct mtd_info *mtd, int page);
  29. /* 掃描壞塊 */
  30. int (*scan_bbt)(struct mtd_info *mtd);
  31. int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
  32. /* 寫一頁 */
  33. int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
  34. const uint8_t *buf, int page, int cached, int raw);
  35. int chip_delay; /* 由板決定的延遲時間 */
  36. /* 與具體的NAND芯片相關的一些選項,如NAND_NO_AUTOINCR,NAND_BUSWIDTH_16等 */
  37. unsigned int options;
  38. /* 用位表示的NAND芯片的page大小,如某片NAND芯片
  39. * 的一個page有512個字節,那麽page_shift就是9
  40. */
  41. int page_shift;
  42. /* 用位表示的NAND芯片的每次可擦除的大小,如某片NAND芯片每次可
  43. * 擦除16K字節(通常就是一個block的大小),那麽phys_erase_shift就是14
  44. */
  45. int phys_erase_shift;
  46. /* 用位表示的bad block table的大小,通常一個bbt占用一個block,
  47. * 所以bbt_erase_shift通常與phys_erase_shift相等
  48. */
  49. int bbt_erase_shift;
  50. /* 用位表示的NAND芯片的容量 */
  51. int chip_shift;
  52. /* NADN FLASH芯片的數量 */
  53. int numchips;
  54. /* NAND芯片的大小 */
  55. uint64_t chipsize;
  56. int pagemask;
  57. int pagebuf;
  58. int subpagesize;
  59. uint8_t cellinfo;
  60. int badblockpos;
  61. nand_state_t state;
  62. uint8_t *oob_poi;
  63. struct nand_hw_control *controller;
  64. struct nand_ecclayout *ecclayout; /* ECC布局 */
  65. struct nand_ecc_ctrl ecc; /* ECC校驗結構體,裏面有大量的函數進行ECC校驗 */
  66. struct nand_buffers *buffers;
  67. struct nand_hw_control hwcontrol;
  68. struct mtd_oob_ops ops;
  69. uint8_t *bbt;
  70. struct nand_bbt_descr *bbt_td;
  71. struct nand_bbt_descr *bbt_md;
  72. struct nand_bbt_descr *badblock_pattern;
  73. void *priv;
  74. };
最後,我們來用圖表的形式來總結一下,MTD設備層、MTD原始設備層、FLASH硬件驅動層之間的聯系。

技術分享圖片

Linux MTD系統剖析【轉】