1、簡介
向linux內核注冊驅動由driver_register()完成。它將驅動程序的信息添加到內核的驅動程序列表中,使得內核能夠在需要時與該驅動程序進行交互。
當調用driver_register()函數時,內核會將驅動程序添加到內核驅動程序列表中,并在需要時使用該驅動程序來匹配和初始化設備。驅動程序的探測函數(probe)將在設備與驅動程序匹配時調用,以便進行設備的初始化。移除函數(remove)將在設備從系統中移除時調用,以進行相關的清理操作。
通過調用driver_register()函數,驅動程序可以將自身注冊到內核中,從而使得內核能夠管理和與驅動程序進行交互。這為設備的探測、初始化、配置和移除提供了必要的框架和支持。
內核中,幾乎所有的驅動子系統都會以該函數進行封裝,開放出對應驅動的注冊函數,例如PCI驅動框架,在/drivers/pic/pci-driver.c文件中會調用該函數:
再比如對應i2c設備驅動,在i2c驅動框架下的/driver/i2c/i2c-core.c文件中有如下代碼:
綜上可知,driver_register()在幾乎所有的驅動子系統中都會使用到。兜兜轉轉,最后都會調用到該函數。
2、driver_register分析
在linux內核中,struct device_driver結構體用于表示一個設備驅動程序。它包含了驅動程序的相關信息,如名稱、總線類型、探測函數、移除函數等,用于與設備進行匹配、初始化和清理操作。struct device_driver結構體提供了設備驅動程序的基本信息和回調函數,用于與設備進行匹配、初始化、清理和管理。
通過使用該結構體,驅動程序能夠在設備與驅動程序匹配時進行初始化操作,并在設備移除或系統關機時進行相應的清理操作。此外,還可以通過屬性組和電源管理操作等擴展功能來增強驅動程序的功能和靈活性。該結構定義如下:
structdevice_driver{ constchar*name;// structbus_type*bus;//驅動程序所屬的總線類型。 structmodule*owner;//模塊擁有者 constchar*mod_name;//在構建內建模塊時使用 boolsuppress_bind_attrs;//是否禁用通過sysfsbound/unbound操作 conststructof_device_id*of_match_table;//設備樹匹配表 conststructacpi_device_id*acpi_match_table;//ACPI匹配表。 int(*probe)(structdevice*dev);//驅動程序的探測函數,用于在設備與驅動程序匹配時進行初始化。 int(*remove)(structdevice*dev);//驅動程序的移除函數,用于在設備從系統中移除時進行清理。 void(*shutdown)(structdevice*dev);//驅動程序的關機函數,用于在系統關機時進行相關的清理操作。 int(*suspend)(structdevice*dev,pm_message_tstate);//驅動程序的掛起函數,用于在設備進入掛起狀態時進行相關的操作。 int(*resume)(structdevice*dev);//驅動程序的恢復函數,用于在設備從掛起狀態恢復時進行相關的操作。 conststructattribute_group**groups;//驅動程序的屬性組,用于提供設備的特定屬性。 conststructdev_pm_ops*pm;//驅動程序的電源管理操作,用于控制設備的電源管理。 structdriver_private*p;//驅動核心的私有數據。驅動核心能訪問。 };
driver_register函數用于向設備驅動模型注冊一個設備驅動,實現在/drivers/base/driver.c文件中:
intdriver_register(structdevice_driver*drv) { intret; structdevice_driver*other; BUG_ON(!drv->bus->p); if((drv->bus->probe&&drv->probe)|| (drv->bus->remove&&drv->remove)|| (drv->bus->shutdown&&drv->shutdown)) printk(KERN_WARNING"Driver'%s'needsupdating-pleaseuse" "bus_typemethods ",drv->name); other=driver_find(drv->name,drv->bus); if(other){ printk(KERN_ERR"Error:Driver'%s'isalreadyregistered," "aborting... ",drv->name); return-EBUSY; } ret=bus_add_driver(drv); if(ret) returnret; ret=driver_add_groups(drv,drv->groups); if(ret){ bus_remove_driver(drv); returnret; } kobject_uevent(&drv->p->kobj,KOBJ_ADD); returnret; }
driver_register()具體執行流程如下:
(1)調用driver_find()通過名字找到bus上的driver。
(2)調用bus_add_driver()添加一個driver到bus。
(3)調用driver_add_groups()將屬性組(attribute_group)添加到驅動程序中。
(4)調用kobject_uevent()觸發內核KOBJ_ADD事件,用于向用戶空間發送KOBJ_ADD事件通知。
下文將展開driver_find()和bus_add_driver()進行分析。
(2-1)driver_find分析
該函數接收兩個參數:
(1)name:驅動程序的名稱。
(2)bus:待被掃描的bus。
函數實現如下:
調用kset_find_obj()根據name尋找是否有kobject,如果找到了,則使用to_driver()返解出struct driver_private,然后將driver_private->driver作為參數返回;否則返回NULL。
該行代碼中:
structkobject*k=kset_find_obj(bus->p->drivers_kset,name);
bus->p->drivers_kset本質是struct kset,struct kset是linux內核對象的集合,添加的設備驅動將作為內核對象添加到對應的kset集合中。kset_find_obj()則用于在kset中搜索出對應name的內核對象,該函數實現如下:
回到driver_register()中,如果driver_find()找到了對應的device_driver,則證明該設備驅動已經注冊過了,這時候則返回退出driver_register();否則繼續執行后續的bus_add_driver()操作。
(2-2)bus_add_driver分析
bus_add_driver()實現在/drivers/base/bus.c中,將執行下列具體的邏輯:
(1)從drv->bus中解析出bus,如果bus為NULL,則返回退出bus_add_driver:
bus=bus_get(drv->bus); if(!bus) return-EINVAL;
(2)調用kzalloc()分配一個struct driver_private內存空間:
(3)調用klist_init()初始化設備鏈表klist_devices:
klist_init(&priv->klist_devices,NULL,NULL);
(4)設置驅動私有數據的driver和kobj.kset字段,并將驅動私有數據設置到驅動程序的->p字段:
priv->driver=drv; drv->p=priv; priv->kobj.kset=bus->p->drivers_kset;
(5)調用kobject_init_and_add()初始化設備私有數據中的內核對象,并指定驅動類型driver_ktype:
error=kobject_init_and_add(&priv->kobj,&driver_ktype,NULL, "%s",drv->name); if(error) gotoout_unregister;
driver_ktype定義如下:
staticstructkobj_typedriver_ktype={ .sysfs_ops=&driver_sysfs_ops, .release=driver_release, };
(6)使用klist_add_tail()將priv->knode_bus節點添加到bus->p->klist_drivers總線的驅動鏈表中。
(7)如果設置了驅動所屬的bus的drivers_autoprobe,則調用drvier_attach()嘗試將驅動程序綁定到設備:
(8)使用moudle_add_drvier將設備驅動程序添加到內核模塊系統中,使之可以與設備進行關聯。通過調用這個函數,可以將設備驅動程序注冊到內核的設備驅動程序列表中,以便在設備被發現時自動加載并與之匹配。
(9)調用driver_create_file()為設備驅動創建sysfs中的屬性文件。該文件將顯示在/sys/bus/
(10)調用driver_add_groups()將設備驅動程序的bus的屬性組添加到sysfs中,這些屬性組將顯示在/sys/bus/
3、總結
driver_register()是linux 內核中用于注冊設備驅動程序的函數。它屬于linux設備驅動模型的一部分,用于將驅動程序添加到內核的設備驅動程序列表中,以便內核可以與相應的硬件設備進行交互。在內核中的大部分驅動都會形成自己的驅動框架核心,例如:USB、i2c、spi等,這些驅動框架核心也一般都會封裝出自己的注冊函數,但是這些注冊函數本質上都是調用driver_register()實現驅動的注冊。例如下圖所示:
審核編輯:劉清
-
USB接口
+關注
關注
9文章
699瀏覽量
55574 -
電源管理
+關注
關注
115文章
6154瀏覽量
144231 -
LINUX內核
+關注
關注
1文章
316瀏覽量
21617 -
I2C驅動
+關注
關注
0文章
9瀏覽量
7034 -
ADD
+關注
關注
1文章
20瀏覽量
9404
原文標題:linux內核中竟有如此“高冷”的driver_register
文章出處:【微信號:嵌入式小生,微信公眾號:嵌入式小生】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論