1、簡(jiǎn)介
本文基于內(nèi)核源碼4.19.4分析。
linux內(nèi)核設(shè)備的注冊(cè)由device_register()函數(shù)完成,這個(gè)函數(shù)是linux設(shè)備驅(qū)動(dòng)模型的核心函數(shù),實(shí)現(xiàn)在/drivers/base/core.c中:
在device_register()函數(shù)中,分為兩個(gè)步驟:
(1)調(diào)用device_initialize():該步驟用于初始化一個(gè)device。
(2)調(diào)用device_add():該函數(shù)用于將device添加到linux內(nèi)核的device樹中。
2、device_initialize分析
該函數(shù)接收一個(gè)struct device *dev參數(shù),在該函數(shù)中初始化struct device結(jié)構(gòu)中的幾個(gè)重要成員:
設(shè)置dev->kobj.kset為device_kset。device_kset是一個(gè)struct kset類型的全局變量,用于向sysfs文件系統(tǒng)中導(dǎo)出目錄:/sys/device/* 。
初始化dev中的kobject,并指定與這個(gè)對(duì)象相關(guān)聯(lián)的ktype為device_type。
初始化dma_pools鏈表。
初始化struct device中的各種鎖:
初始化device的電源管理:
如果在NUMA下,還會(huì)初始化設(shè)置device的numa_node為-1。
接著初始化device下的links中的鏈表:
在struct device 中的links表示鏈接到該設(shè)備的suppliers和consumers,由struct dev_links_info表示:
設(shè)置device下的links.status值為DL_DEV_NO_DRIVER,表示此時(shí)還沒(méi)有對(duì)應(yīng)驅(qū)動(dòng)attach到這個(gè)設(shè)備。
以上步驟則是device_initialize()初始化設(shè)備時(shí)完成的操作。
3、device_add分析
(1)調(diào)用get_device(dev)增加device的引用計(jì)數(shù)。
(2)如果dev->p為NULL,則調(diào)用device_private_init()設(shè)置device的私有數(shù)據(jù):
(3)設(shè)置device的name:
如果開啟支持pr_debug()函數(shù),則會(huì)打印出對(duì)應(yīng)的設(shè)備名稱。
(4)尋找父設(shè)備和父設(shè)備對(duì)應(yīng)的kobj,并調(diào)用kobject_add()將dev->kobj添加到dev->kobj.parent:
(5)使用device_create_file為device創(chuàng)建sysfs屬性文件:
error=device_create_file(dev,&dev_attr_uevent);
dev_attr_uevent是一個(gè)struct device_attribute類型的數(shù)據(jù),該結(jié)構(gòu)用于描述導(dǎo)出設(shè)備屬性的接口,定義如下:
structdevice_attribute{ structattributeattr; ssize_t(*show)(structdevice*dev,structdevice_attribute*attr, char*buf); ssize_t(*store)(structdevice*dev,structdevice_attribute*attr, constchar*buf,size_tcount); };
(6)添加類的符號(hào)鏈接:
error=device_add_class_symlinks(dev);
device_add_class_symlinks()的功能是將設(shè)備添加到指定的設(shè)備類中,并在/sys/class目錄下為設(shè)備創(chuàng)建符號(hào)鏈接,以便用戶空間程序能夠方便地訪問(wèn)和管理設(shè)備。
(7)調(diào)用device_add_attrs()為設(shè)備添加屬性:
error=device_add_attrs(dev);
device_add_attrs()的功能是為設(shè)備添加屬性,并在/sys/devices目錄下創(chuàng)建相應(yīng)的屬性文件。這樣,用戶空間程序可以通過(guò)訪問(wèn)設(shè)備的屬性文件來(lái)讀取和修改設(shè)備的屬性值。這個(gè)函數(shù)在設(shè)備驅(qū)動(dòng)的初始化過(guò)程中常常被調(diào)用,以確保設(shè)備的屬性能夠正確地顯示和訪問(wèn)。
(8)調(diào)用bus_add_device()添加設(shè)備到bus:
bus_add_device用于將設(shè)備添加到總線上。它的功能是將一個(gè)設(shè)備(struct device結(jié)構(gòu)體)添加到指定總線(struct bus_type結(jié)構(gòu)體)上,并進(jìn)行相應(yīng)的初始化和注冊(cè)操作。
bus_add_device的執(zhí)行邏輯:
(1)從dev->bus中取得bus_type*類型的指針bus,如果獲取bus不成功,則函數(shù)直接返回;如果bus獲取成功,則會(huì)繼續(xù)后續(xù)的第(2)步操作。
(2)調(diào)用device_add_attrs接口,將由bus->dev_attrs指針定義的默認(rèn)attribute添加到內(nèi)核中,這個(gè)操作會(huì)體現(xiàn)在sysfs文件系統(tǒng)中的/sys/devices/xxx/xxx_device/目錄中。
(3)調(diào)用device_add_groups將bus_dev_groups添加到內(nèi)核中。
(4)調(diào)用sysfs_create_link將該設(shè)備在sysfs中的目錄,鏈接到該bus的devices目錄下
(5)接著依然調(diào)用sysfs_create_link,在該設(shè)備的sysfs目錄中,創(chuàng)建一個(gè)指向該設(shè)備所在bus目錄的鏈接,命名為subsystem。
(6)前面幾個(gè)操作實(shí)則是向sysfs文件系統(tǒng)注冊(cè)關(guān)于設(shè)備的信息,向用戶空間拋出接口。最后步驟則是調(diào)用klist_add_tail()將該設(shè)備指針保存到bus->p->klist_devices中。
(9)調(diào)用device_pm_add()將一個(gè)設(shè)備添加到PM核心的active設(shè)備鏈表中。
(10)創(chuàng)建設(shè)備節(jié)點(diǎn):
(11)通過(guò)bus_notifier告知系統(tǒng)設(shè)備已經(jīng)添加:
(12)調(diào)用bus_probe_device()為該設(shè)備probe一個(gè)驅(qū)動(dòng)。該函數(shù)實(shí)現(xiàn)如下:
具體執(zhí)行流程如下:
(1)從dev中解析出該dev所在而bus,如果bus不存在,則退出該函數(shù)。
(2)如果設(shè)置了driver_autoprobe,則調(diào)用device_initial_probe(dev)。該函數(shù)本質(zhì)調(diào)用到device_attach(),嘗試將設(shè)備連接到驅(qū)動(dòng)程序。
(3)遍歷bus上的子系統(tǒng)接口鏈表interfaces,如果add_dev函數(shù)指針存在,則調(diào)用對(duì)應(yīng)的函數(shù)。(從源碼來(lái)看有些驅(qū)動(dòng)程序,會(huì)使用struct subsys_interface來(lái)實(shí)現(xiàn),在此處實(shí)現(xiàn)對(duì)注冊(cè)的subsys_interface下的add_dev的調(diào)用執(zhí)行)
(13)如果父設(shè)備存在,則會(huì)將該設(shè)備添加到父設(shè)備的klist_children鏈表中(klist_children是包含此設(shè)備的所有子節(jié)點(diǎn)的鏈表):
(14)如果設(shè)備的class不為NULL,則會(huì)將class綁定到device:
klist_add_tail(&dev->p->knode_class,&dev->class->p->klist_devices);
(15)通知所有的interface接口:
在內(nèi)核中,struct class_interface是用于表示設(shè)備類和設(shè)備驅(qū)動(dòng)之間的接口的結(jié)構(gòu)體。它定義了設(shè)備類與設(shè)備驅(qū)動(dòng)之間的關(guān)聯(lián)關(guān)系,允許設(shè)備驅(qū)動(dòng)在注冊(cè)時(shí)與相應(yīng)的設(shè)備類進(jìn)行關(guān)聯(lián),并提供了一組函數(shù)指針,用于設(shè)備類調(diào)用設(shè)備驅(qū)動(dòng)中的操作。
struct class_interface結(jié)構(gòu)體定義如下:
structclass_interface{ structlist_headnode; structclass*class; int(*add)(structdevice*dev,structclass_interface*class_intf); void(*remove)(structdevice*dev,structclass_interface*class_intf); };
node: 用于將struct class_interface鏈接到設(shè)備類的接口鏈表中。
class: 指向與該接口相關(guān)聯(lián)的設(shè)備類。
add: 指向設(shè)備類調(diào)用設(shè)備驅(qū)動(dòng)的添加操作的函數(shù)指針。當(dāng)設(shè)備添加到設(shè)備類時(shí),會(huì)調(diào)用此函數(shù)。
remove: 指向設(shè)備類調(diào)用設(shè)備驅(qū)動(dòng)的移除操作的函數(shù)指針。當(dāng)設(shè)備從設(shè)備類中移除時(shí),會(huì)調(diào)用此函數(shù)。
通過(guò)使用struct class_interface,設(shè)備驅(qū)動(dòng)可以與設(shè)備類進(jìn)行交互,以便在設(shè)備添加或移除時(shí)執(zhí)行相應(yīng)的操作。這種機(jī)制允許設(shè)備驅(qū)動(dòng)與設(shè)備類解耦,使得設(shè)備驅(qū)動(dòng)可以在設(shè)備類的上下文中執(zhí)行一些操作,而無(wú)需直接操作設(shè)備類。
回到device_add()中,使用了list_for_each_entry()遍歷interfaces鏈表,如果設(shè)置了class_intf->add_dev,則調(diào)用該回調(diào)函數(shù)指針指向的函數(shù)。
4、總結(jié)
結(jié)合本文內(nèi)容和linux內(nèi)核源碼,得出以下結(jié)論:
(1)在設(shè)備驅(qū)動(dòng)模型中,所有的設(shè)備注冊(cè)操作最后都會(huì)調(diào)用device_register()函數(shù)實(shí)現(xiàn)。
(2)在筆者分析的linux版本下的device_register()中,存在兩個(gè)數(shù)據(jù)結(jié)構(gòu):struct class_interface 和struct subsys_interface。從內(nèi)核源碼來(lái)看,這兩個(gè)結(jié)構(gòu)只在為數(shù)不多的幾個(gè)特定驅(qū)動(dòng)程序中使用,那么可證明這可能是歷史發(fā)展遺留下來(lái)的代碼,在device_register中仍然保留了對(duì)這部分代碼的支持。
(3)在device_register()中調(diào)用了bus_probe_device(),從而證明在注冊(cè)設(shè)備的時(shí)候發(fā)生了『設(shè)備與驅(qū)動(dòng)匹配』的過(guò)程。
審核編輯:劉清
-
電源管理
+關(guān)注
關(guān)注
117文章
6425瀏覽量
145961 -
Linux系統(tǒng)
+關(guān)注
關(guān)注
4文章
605瀏覽量
28455 -
dma
+關(guān)注
關(guān)注
3文章
576瀏覽量
102884 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
317瀏覽量
22303 -
numa
+關(guān)注
關(guān)注
0文章
7瀏覽量
3939
原文標(biāo)題:一臉懵,萬(wàn)千設(shè)備,linux內(nèi)核如何知道?
文章出處:【微信號(hào):嵌入式小生,微信公眾號(hào):嵌入式小生】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解:基于最新的Linux 4.0內(nèi)核
什么是linux設(shè)備驅(qū)動(dòng)看了就知道
如何編譯設(shè)備樹和Linux內(nèi)核鏡像文件
Linux的內(nèi)核教程
linux內(nèi)核kernel-api
《Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解》第4章、Linux內(nèi)核模塊

谷歌Android設(shè)備內(nèi)核引入主線Linux內(nèi)核難嗎?

linux內(nèi)核是什么_linux內(nèi)核學(xué)習(xí)路線
linux內(nèi)核參數(shù)設(shè)置_linux內(nèi)核的功能有哪些

最硬核的Linux內(nèi)核文章

快速理解什么是Linux內(nèi)核以及Linux內(nèi)核的內(nèi)容

如何才能編譯Linux的內(nèi)核
如何使用Linux內(nèi)核實(shí)現(xiàn)USB驅(qū)動(dòng)程序框架

評(píng)論