1?驅動編寫的 3 種方法
以 LED 驅動為例
1.1?傳統寫法
使用哪個引腳,怎么操作引腳,都寫死在代碼中。
最簡單,不考慮擴展性,可以快速實現功能。
修改引腳時,需要重新編譯。
應用程序調用open等函數最簡單的方法是驅動層也提供對應的drv_open,應用程序調用read,驅動層也提供對應的drv_read等等。需要寫出驅動層的函數,為了便于管理,將這些函數放到file_operations結構體中,即第一步定義對應的file_operations結構體,并實現對應的open等程序(第一步);實現完成之后,將file_operations結構體通過register_chrdev注冊到內核(第二步);然后通過入口函數調用注冊函數(chrdev),安裝驅動程序的時候,內核會調用入口函數,完成file_operations結構體的注冊(第三步);有入口函數就有出口函數(第四步)。對于字符設備驅動程序而言,file_operations結構體是核心,每一個驅動程序都對應file_operations結構體,內核中有眾多file_operations結構體,怎么才能找到對應的結構體呢?
應用程序要訪問驅動程序,需要打開一個設備結點,里面有主設備號,根據設備結點的主設備號在內核中找到對應的file_operations結構體。注冊結構體時足以提供主設備號,可以讓內核分配;最后就是完善信息,創建類,創建設備。
之后有了面向對象/分層/分離
1.2?總線設備驅動模型
引入 platform_device/platform_driver,將“資源”與“驅動”分離開來。
代碼稍微復雜,但是易于擴展。
冗余代碼太多,修改引腳時設備端的代碼需要重新編譯。
更換引腳時 , 圖中的led_drv.c基本不用改,但是需要修改led_dev.c。
左邊是和具體硬件打交道,右邊對應是抽象。通過總線bus管理
1.3?設備樹
通過配置文件──設備樹來定義“資源”(內核之外),會被編譯成dtb文件,再傳給內核,內核解析dtb文件,構造出一系列的platform?device。
代碼稍微復雜,但是易于擴展。
無冗余代碼,修改引腳時只需要修改 dts 文件并編譯得到 dtb 文件,把它傳給內核。
無需重新編譯內核/驅動。
2?在 Linux 中實現“分離”: Bus/Dev/Drv 模型
實例
有一個platform?device,里面有resource,resource指向一個數組,里面定義了設備的資源(中斷),平臺設備指定資源;平臺platform driver有probe函數,這個函數用來做驅動相關的事情;platform?device和platform?driver通過name建立聯系。
找到platform_device.h(/include/linux)
有注冊和注銷函數
搜索,隨便找一個是全局變量的
有一個platform?device,里面有resource,resource指向一個數組,里面定義了設備的資源(中斷),平臺設備指定資源;平臺platform driver有probe函數,這個函數用來做驅動相關的事情;platform?device和platform?driver通過name建立聯系。
這里的resource需要遵守一定的規則
搜索platform?driver-serial8250
platform?device和platform?driver是怎么掛鉤的
搜索-serial8250_isa_driver
也是注冊進內核
內核中有虛擬的總線platform_bus_type,有兩個鏈表,左邊是設備鏈表,右邊是driver鏈表。當注冊平臺設備platform?device時,平臺設備就會放到左邊的鏈表,當注冊平臺驅動platform?driver時,平臺驅動就會放入到右邊的鏈表。放入之后就會和對方的成員比較,匹配成功就調用對應driver中的probe函數。
是否匹配成功
怎么匹配呢
參看源碼
* Platform device IDs are assumed to be encoded like this:* "<name><instance>", where <name> is a short description of the type of* device, like "pci" or "floppy", and <instance> is the enumerated* instance of the device, like '0' or '42'. Driver IDs are simply* "<name>". So, extract the <name> from the platform_device structure,* and compare it against the name of the driver. Return whether they match* or not.*/
static int platform_match(struct device *dev, struct device_driver *drv)
{struct platform_device *pdev = to_platform_device(dev);struct platform_driver *pdrv = to_platform_driver(drv);/* When driver_override is set, only bind to the matching driver */// 1先用它if (pdev->driver_override)return !strcmp(pdev->driver_override, drv->name);/* Attempt an OF style match first */if (of_driver_match_device(dev, drv))return 1;/* Then try ACPI style match */if (acpi_driver_match_device(dev, drv))return 1;/* Then try to match against the id table */// 2再用它if (pdrv->id_table)return platform_match_id(pdrv->id_table, pdev) != NULL;/* fall-back to driver name match */// 3最后用它return (strcmp(pdev->name, drv->name) == 0);
}// 第2步
static const struct platform_device_id *platform_match_id(const struct platform_device_id *id,struct platform_device *pdev)
{while (id->name[0]) {if (strcmp(pdev->name, id->name) == 0) {pdev->id_entry = id;return id;}id++;}return NULL;
}
匹配過程是怎么啟動的呢
3?匹配規則
3.1?最先比較
platform_device.driver_override 和 platform_driver.driver.name,可以設置 platform_device 的 driver_override,強制選擇某個 platform_driver
3.2?然后比較
platform_device. name 和 platform_driver.id_table[i].name
Platform_driver.id_table 是“platform_device_id”指針,表示該 drv 支持若干個 device,它里面列出了各個 device 的{.name, .driver_data},其中的“ name”表示該 drv 支持的設備的名字, driver_data 是些提供給該 device 的私有數據。
3.3?最后比較
platform_device.name 和 platform_driver.driver.name
platform_driver.id_table 可能為空,
這時可以根據 platform_driver.driver.name 來尋找同名的 platform_device。
3.4?函數調用關系
platform_device_register
platform_device_adddevice_addbus_add_device // 放入鏈表bus_probe_device // probe 枚舉設備,即找到匹配的(dev, drv)device_initial_probe__device_attachbus_for_each_drv(...,__device_attach_driver,...)__device_attach_driverdriver_match_device(drv, dev) // 是否匹配driver_probe_device // 調用 drv 的 probeplatform_driver_register__platform_driver_registerdriver_registerbus_add_driver // 放入鏈表driver_attach(drv)bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);__driver_attachdriver_match_device(drv, dev) // 是否匹配driver_probe_device // 調用 drv 的 probe
4?常用函數
這些函數可查看內核源碼: drivers/base/platform.c,根據函數名即可知道其含義。下面摘取常用的幾個函數
4.1?注冊/反注冊
platform_device_register/ platform_device_unregister
platform_driver_register/ platform_driver_unregister
platform_add_devices // 注冊多個 device
4.2?獲得資源
返回該 dev 中某類型(type)資源中的第幾個(num)
struct resource *platform_get_resource(struct platform_device *dev,unsigned int type,unsigned int num)
返回該 dev 所用的第幾個(num)中斷:
int platform_get_irq(struct platform_device *dev, unsigned int num)
通過名字(name)返回該 dev 的某類型(type)資源:
struct resource *platform_get_resource_byname(struct platform_device *dev,unsigned int type,const char *name)
通過名字(name)返回該 dev 的中斷號
int platform_get_irq_byname(struct platform_device *dev, const char *name)
5?怎么寫程序
5.1?分配/設置/注冊 platform_device 結構體
在里面定義所用資源,指定設備名字。
5.2?分配/設置/注冊 platform_driver 結構體
在 其 中 的 probe 函 數里,分配/設置/注冊 file_operations 結構體 ,并從platform_device 中確實所用硬件資源, 指定 platform_driver 的名字。