Linux之platform平臺設(shè)備驅(qū)動詳解
在 Linux 設(shè)備驅(qū)動模型中,總線(Bus)是連接處理器與設(shè)備的橋梁,而 Platform 總線是一種虛擬總線,專門用于管理那些不依賴于物理總線(如 I2C、PCI、USB 等)的嵌入式設(shè)備(如 SoC 內(nèi)部的硬件外設(shè))。
所以platform 總線的主要作用就是統(tǒng)一設(shè)備模型,將未掛載到物理總線的設(shè)備納入統(tǒng)一的設(shè)備驅(qū)動框架。
通過 platform_bus_type 虛擬一條總線,使得這些設(shè)備可以像物理總線設(shè)備一樣被管理。
platform驅(qū)動注冊
結(jié)構(gòu)體是struct platform_driver,主要包含probe、remove等接口。
struct platform_driver { int (*probe)(struct platform_device *); /* * Traditionally the remove callback returned an int which however is * ignored by the driver core. This led to wrong expectations by driver * authors who thought returning an error code was a valid error * handling strategy. To convert to a callback returning void, new * drivers should implement .remove_new() until the conversion it done * that eventually makes .remove() return void. */ int (*remove)(struct platform_device *); void (*remove_new)(struct platform_device *); void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); int (*resume)(struct platform_device *); struct device_driver driver; const struct platform_device_id *id_table; bool prevent_deferred_probe; /* * For most device drivers, no need to care about this flag as long as * all DMAs are handled through the kernel DMA API. For some special * ones, for example VFIO drivers, they know how to manage the DMA * themselves and set this flag so that the IOMMU layer will allow them * to setup and manage their own I/O address space. */ bool driver_managed_dma; };
驅(qū)動注冊接口是platform_driver_register,主要是把bus配成platform_bus_type后調(diào)用driver_register注冊。
代碼示例:
int zsl_drv_probe(struct platform_device *dev) { struct property *pp = NULL; printk(KERN_INFO "%s: \n",__func__); dump_stack(); // 打印堆棧 return 0; } int zsl_drv_remove(struct platform_device *dev) { printk(KERN_INFO "%s: \n",__func__); return 0; } struct platform_driver zsl_drv = { .driver = { .name = "zsltest", }, .probe = zsl_drv_probe, .remove = zsl_drv_remove, };
再使用platform_driver_register(&zsl_drv)注冊這個驅(qū)動。
platform設(shè)備注冊
結(jié)構(gòu)體是struct platform_device,主要包含probe、remove等接口。
struct platform_device { const char *name; int id; bool id_auto; struct device dev; u64 platform_dma_mask; struct device_dma_parameters dma_parms; u32 num_resources; struct resource *resource; const struct platform_device_id *id_entry; /* * Driver name to force a match. Do not set directly, because core * frees it. Use driver_set_override() to set or clear it. */ const char *driver_override; /* MFD cell pointer */ struct mfd_cell *mfd_cell; /* arch specific additions */ struct pdev_archdata archdata; };
注冊接口是platform_device_register,主要是把設(shè)備屬性填充后,后調(diào)用device_add注冊。
代碼示例:
struct platform_device zsl_dev = { .name = "zsltest", .dev = { .release = zsl_dev_release, }, };
再使用platform_device_register(&zsl_dev)注冊這個設(shè)備,其中zsl_dev里的name和zsl_drv的name保持一樣,才能讓platform device和platform driver匹配上,從而調(diào)用zsl_drv.probe。
跟platform驅(qū)動注冊配套使用后,運(yùn)行打印如下,可以看到zsl_drv.probe會被調(diào)用到。
運(yùn)行結(jié)果:
- 先注冊dev,再注冊drv
- 先注冊drv,再注冊dev
設(shè)備樹
支持設(shè)備的內(nèi)核里,更推薦使用設(shè)備樹的方式,而不是platform設(shè)備注冊的方式。
去掉zsl_dev設(shè)備的注冊代碼,在zsl_drv變量里增加.of_match_table = zsl_of_match,并且zsl_of_match表里增加.compatible = "rockchip,zslzsl",然后在設(shè)備樹里增加以下代碼。
保持兩邊的compatible一致,并且status是okay的。
這樣就會調(diào)用zsl_drv.probe,并且可以拿到設(shè)備樹里的屬性內(nèi)容。
zsl: zsl { compatible = "rockchip,zslzsl"; status = "okay"; testdata = "test"; };
如下修改zsl_drv_probe接口,增加拿testdata屬性的代碼
int zsl_drv_probe(struct platform_device *dev) { struct property *pp = NULL; printk(KERN_INFO "%s: \n",__func__); pp = of_find_property(dev->dev.of_node, "testdata", NULL); if (pp) printk(KERN_INFO "%s: %d:%s \n",__func__,pp->length,(char *)pp->value); dump_stack(); // 打印堆棧 return 0; }
編譯運(yùn)行后如下,可以看到zsl_drv.probe會被調(diào)用到,并且能拿到設(shè)備樹里的testdata屬性。
Platform驅(qū)動和設(shè)備的關(guān)系
根據(jù)堆棧打印跟蹤代碼,調(diào)用調(diào)用關(guān)系如下
platform_driver_register driver_register bus_add_driver klist_add_tail driver_attach driver_match_device(struct device *dev, void *data)=platform_match(struct device *dev, struct device_driver *drv) //找dev driver_probe_device really_probe dev->bus->probe=platform_probe drv->probe=zsl_drv_probe
Driver注冊時通過bus_add_driver將driver加入總線(klist_add_tail到總線的driver列表),觸發(fā)driver_attach,遍歷總線的device列表,通過platform_match匹配已有設(shè)備。
基本就是按順序?qū)υO(shè)備樹、id_table name的字符串匹配。匹配成功后,通過really_probe調(diào)用總線默認(rèn)的platform_probe,最終執(zhí)行driver的probe函數(shù)。
platform_device_register platform_device_add device_add bus_probe_device device_initial_probe=__device_attach __device_attach_driver driver_match_device(struct device *dev, void *data)=platform_match(struct device *dev, struct device_driver *drv) //找drv driver_probe_device really_probe dev->bus->probe=platform_probe drv->probe=zsl_drv_probe klist_add_tail
Device注冊時通過device_add將device加入總線(klist_add_tail到總線的device列表)觸發(fā)bus_probe_device,遍歷總線的driver列表,通過platform_match匹配已有驅(qū)動,匹配成功則調(diào)用driver的probe函數(shù)。
這種雙向注冊機(jī)制確保了無論driver和device的注冊順序如何,都能正確觸發(fā)匹配和初始化。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解在Linux系統(tǒng)中如何識別和解決端口占用問題
在日常的 Linux 系統(tǒng)管理和開發(fā)過程中,端口占用是一個常見且令人頭疼的問題,無論是部署新服務(wù)、調(diào)試應(yīng)用程序,還是進(jìn)行系統(tǒng)維護(hù),遇到端口被占用都可能導(dǎo)致服務(wù)無法正常啟動或運(yùn)行,本文將詳細(xì)介紹在 Linux 系統(tǒng)中如何識別和解決端口占用問題,需要的朋友可以參考下2025-01-01Ubuntu搭建web站點(diǎn)并發(fā)布公網(wǎng)訪問詳細(xì)步驟(內(nèi)網(wǎng)穿透)
這篇文章主要給大家介紹了關(guān)于Ubuntu搭建web站點(diǎn)并發(fā)布公網(wǎng)訪問(內(nèi)網(wǎng)穿透)的相關(guān)資料,內(nèi)網(wǎng)穿透是一種實現(xiàn)在外網(wǎng)任意地點(diǎn)訪問內(nèi)網(wǎng)的方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12Linux /etc/network/interfaces配置接口方法
在本篇文章里小編給各位分享的是一篇關(guān)于Linux /etc/network/interfaces配置接口方法知識點(diǎn),需要的朋友們可以學(xué)習(xí)下。2020-02-02