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