在linux中,mtd是指“內存技術設備”,是存儲設備中的一個子系統。linux引入MTD系統是為了給NOR FLASH和NAND FLASH設備提供統一接口。MTD設備通常可分為四層:設備節點、MTD設備層、MTD原始設備層、硬件驅動層。
本教程操作環境:linux5.9.8系統、Dell G3電腦。
Linux MTD是什么?
MTD全稱“Memory Technology Device”,意思為“內存技術設備”,是Linux的存儲設備中的一個子系統。
在Linux內核中,引入MTD層為NOR FLASH和NAND FLASH設備提供統一接口。MTD將文件系統與底層FLASH存儲器進行了隔離。
設計此MTD系統的目的是,對于內存類的設備,提供一個抽象層,一個接口,使得對于硬件驅動設計者來說,只需要去提供最簡單的底層硬件設備的讀/寫/擦除函數就可以了,數據對于上層使用者來說是如何表示的,可以不關心,因為MTD存儲設備子系統都幫你做好了。
MTD框架
Linux的MTD設備位于drivers/mtd/下面。
MTD文件下的內容如下:
MTD設備通常可分為四層
上到下依次是:設備節點、MTD設備層、MTD原始設備層和硬件驅動層。
1.cmdlinepart.c
當mtd分區表由u-boot通過cmd參數傳輸給linux時,linux內核可以不用對mtdparts進行注冊添加,只需要將MTD中的command line partition選項開啟即可。使用這種的方法u-boot下需要對MTD進行支持,且所傳輸的mtd分區參數要符合格式要求。
2.devices文件夾
當我們有一個spi flash設備時且要使用mtd進行管理,我們一般會將其放在devices文件夾下,如devices文件夾下面的m25p80.c就是一個典型的spi flash設備。
3.chips/nand/onenand文件夾
nand flash 驅動在nand文件夾下;
onenand flash 驅動在onenand文件夾下;
nor flash比較雜,下面幾個文件下都會有:
chips:cfi/jedec接口通用驅動
devices:nor flash底層驅動(spi flash)
maps:nor flash映射關系相關函數
4.核心文件
mtdchar.c : MTD字符設備接口相關實現,設備號31;
mtdblock.c : MTD塊設備接口相關實現,設備號90,;
mtdcore.c: MTD原始設備接口相關實現;
mtdpart.c : MTD分區接口相關實現。
5.ubi
ubifs文件的支持層,當使用ubifs文件系統時,需要將Device Drivers -> Memory Technology Device (MTD) support -> UBI -Unsorted block image 中的Enable UBI選中。
將File systems -> Miscellaneous filesystems中的UBIFS file system support選中。
MTD分區表的實現
在開機過程從console經常可以看到類似以下信息,
0x000000000000-0x000000100000?:?"Bootloade" 0x000000100000-0x000002000000?:?"Kernel" 0x000002000000-0x000003000000?:?"User" 0x000003000000-0x000008000000?:?"File?System"
這就是MTD給我們一種最直觀的表示形式,給我們展示了內存中各模塊的分區結構,但這些分區是怎樣實現的呢?分區表的實現方式有幾種,下面進行分別說明:
注:分區表實現的前提是MTD設備驅動已經成功了,否則連驅動都沒成功就無分區可說了。
1.內核中添加
在內核中添加這是一個比較經常使用的方法,隨便一本驅動移植的書上應該都有,主要就是在平臺設備里面添加mtd_partition,添加類似下面的信息,這邊就不過多描述
struct?mtd_partition?s3c_nand_part[]?=?{ ????{ ????????.name???????=?"Bootloader", ????????.offset?????=?0, ????????.size???????=?(1?*?SZ_1M), ????????.mask_flags?=?MTD_CAP_NANDFLASH, ????}, ????{ ????????.name???????=?"Kernel", ????????.offset?????=?(1?*?SZ_1M), ????????.size???????=?(31?*?SZ_1M)?, ????????.mask_flags?=?MTD_CAP_NANDFLASH, ????}, ????{ ????????.name???????=?"User", ????????.offset?????=?(32?*?SZ_1M), ????????.size???????=?(16?*?SZ_1M)?, ????}, ????{ ????????.name???????=?"File?System", ????????.offset?????=?(48?*?SZ_1M), ????????.size???????=?(96?*?SZ_1M), ????} }; static?struct?s3c_nand_set?s3c_nand_sets[]?=?{ ????[0]?=?{ ????????.name???????=?"nand", ????????.nr_chips???=?1, ????????.nr_partitions??=?ARRAY_SIZE(s3c_nand_part), ????????.partitions?=?ok6410_nand_part, ????}, }; static?struct?s3c_platform_nand?s3c_nand_info?=?{ ????.tacls??????=?25, ????.twrph0?????=?55, ????.twrph1?????=?40, ????.nr_sets????=?ARRAY_SIZE(s3c_nand_sets), ????.sets???????=?ok6410_nand_sets, }; static?void?__init?s3c_machine_init(void) { ????s3c_nand_set_platdata(&s3c_nand_info);? }
因為我們的MTD驅動已經完成了,當device和driver匹配后會調用驅動中的probe接口函數,我們需要在probe函數里面調用add_mtd_partitions(s3c_mtd, sets->partitions, sets->nr_partitions);實現分區表的添加。
2.u-boot傳參
在u-boot下可以通過添加mtdparts信息到bootargs中,u-boot啟動后會將bootargs中的信息傳送給kernel,,kernel在啟動的時候會解析bootargs中mtdparts的部分,這邊舉個例子:
mtdparts=nand.0:1M(Bootloader)ro,31M(Kernel)ro,16M(User),96M(File System),更具體的mtdparts格式可以查閱下相關資料。
為了使kernel能夠解析mtdparts信息,我們需要將內核中的Device Drivers -> Memory Technology Device (MTD) support ->Command line partition table parsing選項開啟,這在上面已經說過。
在內核中添加分區表的時候,我們是在平臺設備里面加入mtd_partition信息。這邊通過u-boot傳參則取消平臺設備里面的partition信息,那我們需要怎樣解析u-boot的傳過來的mtdparts呢。
u-boot傳參過來后,cmdlinepart.c中會將這些參數解析好,存在里面LIST_HEAD(part_parsers)鏈表里面,然后我們在驅動的probe函數中,通過調用mtd_device_parse_register(mtd, probe_types,&ppdata, NULL, 0);函數。
mtd_device_parse_register()函數位于drivers/mtd/mtdcore.c 中,內容如下:
int?mtd_device_parse_register(struct?mtd_info?*mtd,?const?char?*?const?*types, ??????????????????struct?mtd_part_parser_data?*parser_data, ??????????????????const?struct?mtd_partition?*parts, ??????????????????int?nr_parts) { ????int?err; ????struct?mtd_partition?*real_parts; ????err?=?parse_mtd_partitions(mtd,?types,?&real_parts,?parser_data); ????if?(err??0)?{ ????????err?=?add_mtd_partitions(mtd,?real_parts,?err); ????????kfree(real_parts); ????}?else?if?(err?==?0)?{ ????????err?=?add_mtd_device(mtd); ????????if?(err?==?1) ????????????err?=?-ENODEV; ????} ????return?err; }
可以看到該函數會先執行parse_mtd_partitions(mtd, types, &real_parts, parser_data);函數,后面還是通過add_mtd_partitions()函數來實現分區表的添加。
parse_mtd_partitions()函數位于drivers/mtd/mtdpart.c中,內容如下:
int?parse_mtd_partitions(struct?mtd_info?*master,?const?char?*const?*types, ?????????????struct?mtd_partition?**pparts, ?????????????struct?mtd_part_parser_data?*data) { ????struct?mtd_part_parser?*parser; ????int?ret?=?0; ????if?(!types) ????????types?=?default_mtd_part_types; ????for?(?;?ret?parse_fn)(master,?pparts,?data); ????????put_partition_parser(parser); ????????if?(ret?>?0)?{ ????????????printk(KERN_NOTICE?"%d?%s?partitions?found?on?MTD?device?%sn", ???????????????????ret,?parser->name,?master->name); ????????????break; ????????} ????} ????return?ret; }
進入parse_mtd_partitions()函數會先判斷types的類型,如果為空則給默認值,types的類型一般就兩種,如下:
static?const?char?*?const?default_mtd_part_types[]?=?{ ????"cmdlinepart", ????"ofpart", ????NULL };
第一個”cmdlinepart”即u-boot傳參的方式,第二個”ofpart”即下面要講到的使用dts傳參的方式,判斷完類型后,就通過get_partition_parser去解析part_parsers鏈表里面的數據,這樣就完成u-boot參數的解析。
3.dts傳參
在Linux3.14以后的linux版本中,加入一個新的知識DTS(Device tree),dts其實就是為了解決ARM Linux中的冗余代碼,在Linux2.6版本的arch/arm/plat.xxx和arch/arm/mach.xxx中充斥著大量的垃圾代碼,采用Device Tree后,許多硬件的細節可以直接透過它傳遞給Linux,而不再需要在kernel中進行大量的冗余編碼,關于dts可以自行查閱資料。
dts傳參的原理其實和u-boot一樣,區別在于:u-boot的時候是通過cmdlinepart.c文件實現分區信息寫入LIST_HEAD(part_parsers)鏈表,dts則是用過ofpart.c文件實現分區信息寫入LIST_HEAD(part_parsers)鏈表,所以同樣要把ofpart.c文件的宏打開,在調用mtd_device_parse_register(mtd, probe_types,&ppdata, NULL, 0);函數的時候types要設置成ofpart。
如果去對比Linux2.6版本和Linux3.14版本,會發現drivers/mtd/ofpart.c和drivers/mtd/mtdpart.c文件有所不同,Linux3.8版本里面多了Device tree這一部分的內容,感興趣的可以自己深究下。
這邊舉個dts的例子:
?pinctrl-0?=?; ????ranges?=?;???/*?CS0:?NAND?*/ ????nand@0,0?{ ????????partition@1?{ ????????????label?=?"Bootloader"; ????????????reg?=?; ????????}; ????????partition@2?{ ????????????label?=?"Kernel"; ????????????reg?=?; ????????}; ????????partition@3?{ ????????????label?=?"User"; ????????????reg?=?; ????????}; ????????partition@4?{ ????????????label?=?"File?System"; ????????????reg?=?; ????????}; ????};
Linux mtd system的分析就到這邊,有感悟時會持續會更新。
相關推薦:《Linux視頻教程》