Linux下的platform機制
時間:2018-08-16作者:華清遠見
作者:楊老師,華清遠見教育科技集團講師。 從Linux2.6起,內核引入了一套新的驅動管理和注冊機制:Platform_device和Platform_driver。現在Linux中大部分的設備驅動都可以使用這套機制,總線為platform_bus,設備用platform_device表示,驅動用platform_driver進行注冊。 Linux的這種platform driver機制和傳統(tǒng)的device_driver機制相比,一個十分明顯的優(yōu)勢在于platform機制將本身的資源注冊進內核,由內核統(tǒng)一管理,在驅動程序中使用這些資源時通過platform_device提供的標準接口進行申請并使用。這樣提高了驅動和資源管理的獨立性,并且擁有較好的可移植性和安全性。下面是SPI驅動層次示意圖,Linux中的SPI總線可理解為SPI控制器引出的總線:
![]()
和傳統(tǒng)的驅動一樣,platform機制也分為三個步驟: 1、總線注冊階段: 內核啟動初始化時的main.c文件中的start_kernel() →rest_init() →kernel_init()→do_basic_setup()→driver_init()→platform_bus_init()→ bus_register(&platform_bus_type),注冊了一條platform總線(虛擬總線,platform_bus)。 2、添加設備階段: int platform_device_register(struct platform_device *pdev); //注冊一個設備 int platform_add_devices(struct platform_device **pdevs, int ndev); //注冊多個設備 設備注冊的時候Platform_device_register()/platform_device_add()→(pdev→dev.bus = &platform_bus_type)→device_add(),就這樣把設備給掛到虛擬的總線上。 由分析可以知道: 使用逆向的分析可以知道:Platform_device_register() 這個函數在arch/arm/mach-s5pv210/mach-smdkv210.c 文件中使用了, 文件位置346行: platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices)); 然后在349行處 定義了一個宏 :MACHINE_START(SMDKV210, "SMDKV210") ,跟蹤這個宏可以知道這個宏的詳細定義, #define MACHINE_START(_type,_name) \ static const struct machine_desc __mach_desc_##_type \ __used \ __attribute__((__section__(".arch.info.init"))) = { \ .nr = MACH_TYPE_##_type, \ .name = _name, #define MACHINE_END \ }; 可以知道段代碼被定義在了.arch.info.init段 由系統(tǒng)的連接腳本指定,具體可以查看 arch/arm/kernel/vmlinux.lds 腳本可以知道 3、驅動注冊階段: Platform_driver_register()→driver_register()→bus_add_driver()→driver_attach()→bus_for_each_dev(), 對在每個掛在虛擬的platform bus的設備作__driver_attach()→driver_probe_device(),判斷drv→bus→match()是否執(zhí)行成功,此時通過指針執(zhí)行platform_match→strncmp(pdev→name , drv→name , BUS_ID_SIZE),如果相符就調用really_probe(實際就是執(zhí)行相應設備的platform_driver→probe(platform_device)。)開始真正的探測,如果probe成功,則綁定設備到該驅動。 從上面可以看出,platform機制后還是調用了bus_register() , device_add() , driver_register()這三個關鍵的函數。 下面看幾個結構體 struct platform_device (/include/linux/Platform_device.h) { constchar * name; int id; struct device dev; u32 num_resources; struct resource * resource; }; Platform_device結構體描述了一個platform結構的設備,在其中包含了一般設備的結構體struct device dev;設備的資源結構體struct resource * resource;還有設備的名字const char * name。(注意,這個名字一定要和后面platform_driver.driver àname相同,原因會在后面說明。) 該結構體中重要的就是resource結構,這也是之所以引入platform機制的原因。 struct resource ( /include/linux/ioport.h) { resource_size_t start; resource_size_t end; constchar *name; unsigned long flags; struct resource *parent, *sibling, *child; }; 其中 flags位表示該資源的類型,start和end分別表示該資源的起始地址和結束地址(/include/linux/Platform_device.h): struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); int (*suspend_late)(struct platform_device *, pm_message_t state); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *); struct device_driver driver; }; Platform_driver結構體描述了一個platform結構的驅動。其中除了一些函數指針外,還有一個一般驅動的device_driver結構。 名字要一致的原因: 上面說的驅動在注冊的時候會調用函數bus_for_each_dev(), 對在每個掛在虛擬的platform bus的設備作__driver_attach()→driver_probe_device(),在此函數中會對dev和drv做初步的匹配,調用的是drv- staticint platform_match(struct device * dev, struct device_driver * drv) { struct platform_device *pdev = container_of(dev, struct platform_device, dev); return (strncmp(pdev- } 是比較dev和drv的name,相同則會進入really_probe()函數,從而進入自己寫的probe函數做進一步的匹配。所以dev→name和driver→drv→name在初始化時一定要填一樣的。 不同類型的驅動,其match函數是不一樣的,這個platform的驅動,比較的是dev和drv的名字,還記得usb類驅動里的match嗎?它比較的是Product ID和Vendor ID。 個人總結Platform機制的好處: 1、提供platform_bus_type類型的總線,把那些不是總線型的soc設備都添加到這條虛擬總線上。使得,總線——設備——驅動的模式可以得到普及。 2、提供platform_device和platform_driver類型的數據結構,將傳統(tǒng)的device和driver數據結構嵌入其中,并且加入resource成員,以便于和Open Firmware這種動態(tài)傳遞設備資源的新型bootloader和kernel 接軌。 發(fā)表評論
|