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