嵌入式Linux设备驱动程序:发现硬件配置
嵌入式Linux設(shè)備驅(qū)動(dòng)程序:發(fā)現(xiàn)硬件配置
Embedded Linux device drivers: Discovering the hardware configuration
Interfacing with Device Drivers
了解硬件配置
虛擬驅(qū)動(dòng)程序演示了一個(gè)設(shè)備驅(qū)動(dòng)程序的結(jié)構(gòu),但是由于它只操作內(nèi)存結(jié)構(gòu),因此它缺乏與實(shí)際硬件的交互。設(shè)備驅(qū)動(dòng)程序通常是用來與硬件交互的。部分原因是能夠在第一時(shí)間發(fā)現(xiàn)硬件,記住它可能位于不同配置的不同地址。
在某些情況下,硬件本身提供信息。PCI或USB等可發(fā)現(xiàn)總線上的設(shè)備具有查詢模式,該模式返回資源需求和唯一標(biāo)識(shí)符。內(nèi)核將標(biāo)識(shí)符和可能的其他特征與設(shè)備驅(qū)動(dòng)程序匹配,并將它們結(jié)合起來。
然而,嵌入式板上的大多數(shù)硬件塊沒有這樣的標(biāo)識(shí)符。您必須自己以設(shè)備樹的形式或稱為平臺(tái)數(shù)據(jù)的C結(jié)構(gòu)來提供信息。
在Linux的標(biāo)準(zhǔn)驅(qū)動(dòng)程序模型中,設(shè)備驅(qū)動(dòng)程序向相應(yīng)的子系統(tǒng)注冊(cè):PCI、USB、開放固件(設(shè)備樹)、平臺(tái)設(shè)備等等。注冊(cè)包括一個(gè)標(biāo)識(shí)符和一個(gè)稱為探測(cè)函數(shù)的回調(diào)函數(shù),如果硬件ID和驅(qū)動(dòng)程序的ID匹配,則調(diào)用該函數(shù)。對(duì)于PCI和USB,ID基于供應(yīng)商和設(shè)備的產(chǎn)品ID;對(duì)于設(shè)備樹和平臺(tái)設(shè)備,它是一個(gè)名稱(文本字符串)。
設(shè)備樹
我在第三章中介紹了設(shè)備樹,都是關(guān)于引導(dǎo)程序的。在這里,我想向您展示Linux設(shè)備驅(qū)動(dòng)程序是如何與這些信息連接起來的。
作為一個(gè)例子,我將使用ARM多功能板,arch/ARM/boot/dts/Versatile-ab.dts公司,以太網(wǎng)適配器在此處定義:
net@10010000 { compatible = “smsc,lan91c111”; reg = <0x10010000 0x10000>; interrupts = <25>;};
平臺(tái)數(shù)據(jù)
在沒有設(shè)備樹支持的情況下,有一種使用C結(jié)構(gòu)描述硬件的后備方法,稱為平臺(tái)數(shù)據(jù)。
每個(gè)硬件由struct platform_device描述,它有一個(gè)名稱和一個(gè)指向資源數(shù)組的指針。資源的類型由標(biāo)志確定,這些標(biāo)志包括:
IORESOURCE_MEM:這是內(nèi)存區(qū)域的物理地址
IORESOURCE_IO:這是IO寄存器的物理地址或端口號(hào)
IORESOURCE_IRQ:這是中斷號(hào)
下面是一個(gè)以太網(wǎng)控制器的平臺(tái)數(shù)據(jù)示例,該數(shù)據(jù)取自arch/arm/mach versatile/core.c,為清晰起見,對(duì)其進(jìn)行了編輯:
#define VERSATILE_ETH_BASE 0x10010000 #define IRQ_ETH 25 static struct resource smc91x_resources[] = { [0] = { .start = VERSATILE_ETH_BASE, .end = VERSATILE_ETH_BASE + SZ_64K - 1, .flags = IORESOURCE_MEM,},
[1] = { .start = IRQ_ETH, .end = IRQ_ETH, .flags = IORESOURCE_IRQ,},
}; static struct platform_device smc91x_device = { .name = “smc91x”, .id = 0, .num_resources = ARRAY_SIZE(smc91x_resources), .resource = smc91x_resources,};
它有一個(gè)64KB的內(nèi)存區(qū)和一個(gè)中斷。平臺(tái)數(shù)據(jù)必須在內(nèi)核中注冊(cè),通常在板初始化時(shí):
void __init versatile_init(void) { platform_device_register(&versatile_flash_device); platform_device_register(&versatile_i2c_device); platform_device_register(&smc91x_device); [ …]
將硬件與設(shè)備驅(qū)動(dòng)程序鏈接
在上一節(jié)中,您已經(jīng)看到了如何使用設(shè)備樹和平臺(tái)數(shù)據(jù)來描述以太網(wǎng)適配器。相應(yīng)的驅(qū)動(dòng)程序代碼在drivers/net/ethernet/smsc/smc91x.c中,它同時(shí)處理設(shè)備樹和平臺(tái)數(shù)據(jù)。以下是初始化代碼,為清晰起見再次編輯:
static const struct of_device_id smc91x_match[] = { { .compatible = “smsc,lan91c94”, }, { .compatible = “smsc,lan91c111”, }, {}, }; MODULE_DEVICE_TABLE(of, smc91x_match); static struct platform_driver smc_driver = {.probe = smc_drv_probe,
.remove = smc_drv_remove, .driver ={ .name = "smc91x", .of_match_table = of_match_ptr(smc91x_match), }, }; static int __init smc_driver_init(void) { return platform_driver_register(&smc_driver); } static void __exit smc_driver_exit(void) { platform_driver_unregister(&smc_driver); } module_init(smc_driver_init); module_exit(smc_driver_exit);
當(dāng)驅(qū)動(dòng)程序初始化時(shí),它調(diào)用platform_driver_register(),指向struct
platform_driver,其中有一個(gè)對(duì)探測(cè)函數(shù)的回調(diào)、一個(gè)驅(qū)動(dòng)程序名smc91x和一個(gè)指向“設(shè)備”id的struct的指針。
如果這個(gè)驅(qū)動(dòng)程序是由設(shè)備樹配置的,內(nèi)核將在設(shè)備樹節(jié)點(diǎn)中的compatible屬性和compatible structure元素所指向的字符串之間尋找匹配。對(duì)于每個(gè)匹配,它調(diào)用probe函數(shù)。
另一方面,如果它是通過平臺(tái)數(shù)據(jù)配置的,則將為指向的字符串上的每個(gè)匹配調(diào)用probe函數(shù)驅(qū)動(dòng)程序名.
probe函數(shù)提取有關(guān)接口的信息:
static int smc_drv_probe(struct platform_device *pdev) { struct smc91x_platdata *pd = dev_get_platdata(&pdev->dev); const struct of_device_id *match = NULL; struct resource *res, *ires; int irq;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); [...] addr = ioremap(res->start, SMC_IO_EXTENT); irq = ires->start;[...]
}
對(duì)platform_get_resource()的調(diào)用從設(shè)備樹或平臺(tái)數(shù)據(jù)中提取內(nèi)存和irq信息。由驅(qū)動(dòng)程序映射內(nèi)存并安裝中斷處理程序。第三個(gè)參數(shù)在前面的兩種情況下都為零,如果有一個(gè)以上的特定類型的資源,則會(huì)起作用。
設(shè)備樹允許您配置的不僅僅是基本內(nèi)存范圍和中斷。probe函數(shù)中有一段代碼從設(shè)備樹中提取可選參數(shù)。在此代碼段中,它獲取register io width屬性:
match = of_match_device(of_match_ptr(smc91x_match), &pdev->dev); if (match) { struct device_node *np = pdev->dev.of_node; u32 val; […] of_property_read_u32(np, “reg-io-width”, &val); […]}
對(duì)于大多數(shù)驅(qū)動(dòng)程序,在Documentation/deviceree/bindings中記錄了特定的綁定。對(duì)于這個(gè)特定的驅(qū)動(dòng)程序,信息在Documentation/deviceree/bindings/net/smsc911x.txt中。
這里要記住的主要一點(diǎn)是,驅(qū)動(dòng)程序應(yīng)該注冊(cè)一個(gè)探測(cè)函數(shù)和足夠的信息,以便內(nèi)核調(diào)用探測(cè),因?yàn)樗l(fā)現(xiàn)與它所知道的硬件匹配。設(shè)備樹描述的硬件和設(shè)備驅(qū)動(dòng)程序之間的鏈接是通過compatible屬性實(shí)現(xiàn)的。平臺(tái)數(shù)據(jù)和驅(qū)動(dòng)程序之間的鏈接是通過名稱實(shí)現(xiàn)的。
摘要
設(shè)備驅(qū)動(dòng)程序的工作是處理設(shè)備,通常是物理硬件,但有時(shí)是虛擬接口,并以一致和有用的方式將它們呈現(xiàn)給用戶空間。Linux設(shè)備驅(qū)動(dòng)程序分為三大類:字符、塊和網(wǎng)絡(luò)。在這三種接口中,字符驅(qū)動(dòng)接口是最靈活的,因此也是最常見的。Linux驅(qū)動(dòng)程序適合于一個(gè)稱為驅(qū)動(dòng)程序模型的框架,該模型通過sysfs公開。在/sys中幾乎可以看到設(shè)備和驅(qū)動(dòng)程序的整個(gè)狀態(tài)。
每個(gè)嵌入式系統(tǒng)都有自己獨(dú)特的硬件接口和需求集。Linux為大多數(shù)標(biāo)準(zhǔn)接口提供了驅(qū)動(dòng)程序,通過選擇正確的內(nèi)核配置,您可以很快得到一個(gè)工作的目標(biāo)板。這就給您留下了非標(biāo)準(zhǔn)組件,您必須添加自己的設(shè)備支持。
在某些情況下,您可以通過使用GPIO、I2C等的通用驅(qū)動(dòng)程序來回避這個(gè)問題,并編寫用戶空間代碼來完成這項(xiàng)工作。我建議將此作為一個(gè)起點(diǎn),因?yàn)樗屇袡C(jī)會(huì)在不編寫內(nèi)核代碼的情況下熟悉硬件。編寫內(nèi)核驅(qū)動(dòng)程序并不是特別困難,但是如果你真的這么做了,你需要小心編碼,以免損害系統(tǒng)的穩(wěn)定性。
我已經(jīng)討論過如何編寫內(nèi)核驅(qū)動(dòng)程序代碼:如果你沿著這條路走下去,你將不可避免地想知道如何檢查它是否正常工作并檢測(cè)出任何錯(cuò)誤。
總結(jié)
以上是生活随笔為你收集整理的嵌入式Linux设备驱动程序:发现硬件配置的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 嵌入式Linux的OTA更新,基础知识和
- 下一篇: 嵌入式Linux设备驱动程序:用户空间中