吴忠躺衫网络科技有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux設備與驅動之間的聯系描述

嵌入式小生 ? 來源:嵌入式小生 ? 2023-01-16 09:23 ? 次閱讀

一、導讀

linux設備驅動模型中,總線是一個抽象的概念,是一類特殊的設備。在設備模型的實現中,內核規定了系統中的每個設備都需要連接到一個總線上,這個總線可以是一個內部的Bus、虛擬的Bus或者Platform 總線。在內核中通過struct but_type結構來描述總線,定義在include/linux/device.h中。

本文首先描述與總線相關的數據結構,重點描述struct bus_type結構體內部各個元素的含義以及內部之間的聯系。接著會描述linux設備驅動模型初始化過程中關于總線的初始化流程,這部分由buses_init()完成,最后會描述對總線的幾個操作接口函數。

本文所有源碼分析基于linux內核版本:4.1.15。

一、導讀

二、與總線相關的數據結構

(2-1)struct bus_type

(2-2)struct subsys_private

三、總線的初始化

四、總線的操作接口

(4-1)總線的注冊

(4-2)總線的注銷

(4-3)device和device_driver的添加

(4-4)driver的probe

二、與總線相關的數據結構

(2-1)struct bus_type

總線是處理器和更多設備之間的通道,對于linux的設備模型,所有的設備都通過總線連接在一起。總線之間可以互相連接,例如:USB控制器通常是一個PCI設備,設備模型表示總線和它們控制的設備之間的實際連接。總線由struct bus_type結構表示,該結構包含了總線名稱、默認屬性、總線的方法、PM操作和驅動核心的私有數據。sturct bus_type定義如下:

structbus_type{
constchar*name;
constchar*dev_name;
structdevice*dev_root;
structdevice_attribute*dev_attrs;/*usedev_groupsinstead*/
conststructattribute_group**bus_groups;
conststructattribute_group**dev_groups;
conststructattribute_group**drv_groups;

int(*match)(structdevice*dev,structdevice_driver*drv);
int(*uevent)(structdevice*dev,structkobj_uevent_env*env);
int(*probe)(structdevice*dev);
int(*remove)(structdevice*dev);
void(*shutdown)(structdevice*dev);

int(*online)(structdevice*dev);
int(*offline)(structdevice*dev);

int(*suspend)(structdevice*dev,pm_message_tstate);
int(*resume)(structdevice*dev);

conststructdev_pm_ops*pm;

conststructiommu_ops*iommu_ops;

structsubsys_private*p;
structlock_class_keylock_key;
};

name :總線的名稱。

dev_name : 用于子系統枚舉設備等,例如("foo%u", dev->id)。

dev_root : 表示要用于父設備的默認設備。

dev_attrs: 設備屬性組。

bus_groups: bus屬性組。

dev_groups: dev屬性組。

drv_groups: drv屬性組。

match: 是一個需要由具體bus驅動實現的回調函數,當屬于該bus的所有device和驅動添加到內核時,內核都會調用該接口函數。

uevent:也是一個由具體的bus驅動實現的回到函數,當屬于該bus的設備,觸發添加、移除或者其他動作時,bus模塊核心就會調用該接口,這樣可以讓bus的驅動能夠修改環境變量。

probe、 remove:這兩個也是回調函數,當有新的設備或者驅動添加到這個bus時,內核則會首先調用這個bus的probe,然后再調用具體驅動程序的probe去初始化匹配設備;當有設備從這個bus上移除的時候則會調用remove,所以這個兩個回調函數非常重要。

shutdown:在需要shutdown的時候調用該回調函數,以讓設備停止工作。該函數與電源管理相關。

online:當設備再脫機后重新聯機時調用該函數。該函數與電源管理相關。

offline:當讓設備脫機以便進行熱插拔時調用該函數。

suspend:當總線上的設備想要進入睡眠模式時調用。

resume:讓這個bus上的一個設備退出睡眠模式時調用該函數。

pm:是與之對應的bus的電源管理操作,會去回調執行特定驅動程序的pm的ops。

iommu_ops:該總線的IOMMU特定操作,用于將IOMMU驅動程序實現附加到總線上,并允許驅動程序進行總線上特殊的設定操作。

p: 一個struct subsys_private類型的指針,是驅動核心的私有數據,只有驅動核心可以使用,

lock_key: 該參數供鎖驗證器使用。

(2-2)struct subsys_private

在bus_type和class結構中都有一個指向struct subsys_private的指針,用于保存bus_type和class結構的驅動程序核心部分的私有數據。從命名上似乎不容易理解struct subsys_private,由于bus_type和clsss結構中都有一個struct subsys_private指針,所以可以將subsys_private理解成bus_type和class的上層,包含了bus和class。

struct subsys_private結構定義如下(/drivers/base/base.h):

structsubsys_private{
structksetsubsys;
structkset*devices_kset;
structlist_headinterfaces;
structmutexmutex;

structkset*drivers_kset;
structklistklist_devices;
structklistklist_drivers;
structblocking_notifier_headbus_notifier;
unsignedintdrivers_autoprobe:1;
structbus_type*bus;

structksetglue_dirs;
structclass*class;
};

subsys:用于描述本subsystem的kset,用于代表其自身。

devices_kset: 表示subsystem的device目錄。

interfaces: interfaces是一個list_head類型數據,用于保存與之相關的interface。在內核中interface用于抽象bus下所有關聯設備的一些特殊的功能。

mutex: mutex類型鎖,用于保護設備和interface鏈表。

drivers_kset: 表示subsystem中驅動相關鏈表。

klist_devices: 設備鏈表,用于保存本bus下所有的device的指針,以方便查找。

klist_drivers: 驅動鏈表,用于保存本bus下所有的device_driver的指針,以方便查找。

bus_notifier: bus_notifier是一個總線通知列表,用于監測bus上發生的任何事情。

drivers_autoprobe:用于控制該bus下的drivers或者device是否具有自動probe屬性。

bus:是一個指向與之關聯的struct bus_type類型的指針。用于保存上層的bus。

glue_dirs:表示glue目錄,用于放在父設備之間,以避免名稱空間出現沖突。

class:是一個指向與之關聯的struct class類型的指針。用于保存上層的class。

三、總線的初始化

總線屬于linux驅動模型的一部分,所以在內核啟動過程中,在driver_init()函數中會調用buses_init(),完成總線相關的初始化操作:

0b3084c2-9535-11ed-bfe3-dac502259ad0.png在buses_init()的操作邏輯中,完成了以下幾件事情:

(1)動態創建bus內核kset,并指定其事件操作函數,然后添加到sysfs中。

(2)動態創建system內核kset,并指定其父級kset為devices_kset->kobj,然后添加到sysfs中。

四、總線的操作接口

本小節描述linux內核中對總線的操作API接口:

//添加設備到總線
externintbus_add_device(structdevice*dev);
//為新的設備探測驅動
externvoidbus_probe_device(structdevice*dev);
//從總線中將設備移除
externvoidbus_remove_device(structdevice*dev);

//添加一個驅動到總線
externintbus_add_driver(structdevice_driver*drv);
//從總線將移除驅動
externvoidbus_remove_driver(structdevice_driver*drv);

//將驅動程序從該驅動控制的所有設備中分離
externvoiddriver_detach(structdevice_driver*drv);
//嘗試將設備和驅動程序綁定在一起
externintdriver_probe_device(structdevice_driver*drv,structdevice*dev);
//注冊一個驅動核心總線子系統
externint__must_checkbus_register(structbus_type*bus);

//注銷驅動核心總線子系統
externvoidbus_unregister(structbus_type*bus);

/*注冊總線通知器。總線通知器,用于獲取當設備添加/移除或者驅動程序與
設備綁定/解綁定時的通知。*/
externintbus_register_notifier(structbus_type*bus,
structnotifier_block*nb);

//注銷總線通知器
externintbus_unregister_notifier(structbus_type*bus,
structnotifier_block*nb);

(4-1)總線的注冊

調用bus_register執行具體總線的注冊操作,該函數實現在/drivers/base/bus.c中,具體執行邏輯如下:

(1)調用kzalloc()為struct subsys_private創建內存,設置priv->bus的值為想要注冊的總線類型,然后將bus->p賦值為priv。

(2)初始化總線通知器。

(3)為priv->subsys.kobj重新設置名稱,即總線的名稱。

(4)初始化priv->subsys.kobj的kset和ktype字段。

(5)調用kset_register將private->subsys.kobj注冊到內核中。

(6)調用bus_create_file向sysfs文件系統中的bus目錄下添加一個uevnet attribute:0b5a5afe-9535-11ed-bfe3-dac502259ad0.png

(7)調用kset_create_and_add()向內核分別添加devices和drivers kset,這樣便可以在sysfs中查看了:0b783f88-9535-11ed-bfe3-dac502259ad0.png

(8)初始化priv指針中的mutex、klist_devices和klist_drivers等變量。

(9)調用add_probe_files函數,在bus下添加bus_attr_drivers_probe和bus_attr_drivers_autoprobe兩個attribute:0b9f2b3e-9535-11ed-bfe3-dac502259ad0.png

(10)調用bus_add_groups添加bus_groups屬性組。

(4-2)總線的注銷

調用bus_unregister執行具體總線的注銷操作,該函數同樣實現在/drivers/base/bus.c中:

voidbus_unregister(structbus_type*bus)
{
pr_debug("bus:'%s':unregistering
",bus->name);
if(bus->dev_root)
device_unregister(bus->dev_root);
bus_remove_groups(bus,bus->bus_groups);
remove_probe_files(bus);
kset_unregister(bus->p->drivers_kset);
kset_unregister(bus->p->devices_kset);
bus_remove_file(bus,&bus_attr_uevent);
kset_unregister(&bus->p->subsys);
}

(4-3)device和device_driver的添加

linux內核的驅動模型中,提供了device_register()和driver_register()兩個接口,供各個驅動模塊使用。從linux內核多個子系統的源碼中可以發現,對于各種驅動程序的注冊最終都會調用到driver_register()。然而這兩個接口函數的核心邏輯中,是通過調用總線的bus_add_device()和bus_add_driver()實現的:在driver_register()中調用driver_find()在給定的總線中查找給定名稱的驅動,如果驅動已經存在則返回-EBUSY;如果驅動在總線中不存在,則調用bus_add_driver()注冊驅動。在device_register()中則首先調用device_initialize初始化設備(本質上是對sturct device結構賦值),然后調用device_add向linux內核驅動模型注冊設備。

device_register()和driver_register()兩個接口都在/drivers/base/bus.c文件中實現。下文來具體看看這兩個接口的執行邏輯:

1、bus_add_device的執行邏輯:

(1)從dev->bus中取得bus_type*類型的指針bus,如果獲取bus不成功,則函數直接返回;如果bus獲取成功,則會繼續后續的第(2)步操作。

(2)調用device_add_attrs接口,將由bus->dev_attrs指針定義的默認attribute添加到內核中,這個操作會體現在sysfs文件系統中的/sys/devices/xxx/xxx_device/目錄中。

(3)調用device_add_groups將bus_dev_groups添加到內核中。

(4)調用sysfs_create_link將該設備在sysfs中的目錄,鏈接到該bus的devices目錄下

(5)接著依然調用sysfs_create_link,在該設備的sysfs目錄中,創建一個指向該設備所在bus目錄的鏈接,命名為subsystem。

(6)前面幾個操作實則是向sysfs文件系統注冊關于設備的信息,向用戶空間拋出接口。最后步驟則是調用klist_add_tail()將該設備指針保存到bus->p->klist_devices中。

2、bus_add_driver的執行邏輯:

(1)首先調用bus_get從驅動程序中獲取到該驅動程序所屬的bus_type指針。如果該指針為零(即獲取失敗)則返回-EINVAL;反之則繼續執行后續操作。

(2)為該驅動的struct driver_private指針(priv)分配空間,并初始化其中的priv->klist_devices、priv->driver、priv->kobj.kset等變量,同時將priv保存到device_driver的p中。

(3)調用kobject_init_and_add,并傳入該驅動的名稱作為參數,向sysfs中注冊driver的kobject。該操作體現在sysfs文件中的/sys/bus/xxx/drivers/目錄下。

(4)調用klist_add_tail將驅動添加到總線的klist_drivers鏈表中,如果該驅動的drivers_autoprobe為真,還將調用driver_attach嘗試將驅動綁定到設備。

(5)調用module_add_driver()將驅動添加到drv->owner中,咱不過多分析。

(6)調用driver_create_file,在sysfs文件系統中的driver目錄下,創建uevent attribute。

(7)調用driver_add_groups將bus->drv_groups屬性組添加到驅動中。

(4-4)driver的probe

當一個driver在進行probe的時候,大部分邏輯都會依賴總線的具體實現,核心操作是bus_probe_device()和driver_attach()兩個接口,這兩個接口都是在drivers/base/base.h中聲明,在drivers/base/bus.c中實現。

bus_probe_device()實現如下:

0bb5ac4c-9535-11ed-bfe3-dac502259ad0.png

driver_attach()實現如下:

0be8011a-9535-11ed-bfe3-dac502259ad0.png

從上圖可知,driver_attach中會調用bus_for_each_dev遍歷設備,該函數用于遍歷drv->bus的設備列表,并為每個設備調用__driver_attach(),并將drv傳遞給__driver_attach,故而可以明確知道對每個設備執行的操作則是__driver_attach(),該函數定義在/drivers/base/dd.c文件中,具體的執行邏輯如下:

(1)使用driver_match_device()判斷驅動和設備是否匹配(本質上是判斷是否指定了drv->bus->match,如果指定了則執行與之對應的函數,否則返回1),如果驅動和設備已經匹配了則直接返回;否則,則繼續執行后續的操作。

(2)調用driver_probe_device()嘗試將驅動和設備綁定在一起。

總體上,bus_probe_device()和driver_attach()這兩個接口的操作流程類似,即:搜索所在的總線,比對是否有同名的device_driver(或device),如果有并且該設備沒有綁定Driver(注:這一點很重要,則可以使同一個Driver驅動相同名稱的多個設備)則調用device_driver的probe接口。






審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 處理器
    +關注

    關注

    68

    文章

    19407

    瀏覽量

    231183
  • USB控制器
    +關注

    關注

    1

    文章

    35

    瀏覽量

    11934
  • PCI
    PCI
    +關注

    關注

    4

    文章

    671

    瀏覽量

    130473
  • platform
    +關注

    關注

    0

    文章

    19

    瀏覽量

    17436
  • Linux驅動
    +關注

    關注

    0

    文章

    43

    瀏覽量

    10014

原文標題:Linux設備與驅動的“鵲橋” | Bus

文章出處:【微信號:嵌入式小生,微信公眾號:嵌入式小生】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    淺談Linux發行版之間聯系和區別

    前言:現如今Linux的發行版本多之又多,其大家族可謂是“枝繁葉茂,子孫滿堂”。那么它們各版本之間有著怎樣的聯系和區別呢?Linux發行版之間
    發表于 07-08 08:25

    嵌入式Linux設備驅動開發

    嵌入式Linux設備驅動開發 Linux 設備驅動的基本概念
    發表于 09-10 13:10 ?82次下載
    嵌入式<b class='flag-5'>Linux</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>開發

    嵌入式Linux字符設備驅動的設計與應用

    描述了基于嵌入式Linux的字符設備驅動程序的設計方法和實現過程。以電機、數碼管、串口和mini鍵盤的驅動設計為例,詳細闡述了嵌入式
    發表于 02-23 15:45 ?24次下載

    嵌入式Linux字符設備驅動的設計與應用

    描述了基于嵌入式Linux的字符設備驅動程序的設計方法和實現過程。以電機、數碼管、串口和mini鍵盤的驅動設計為例,詳細闡述了嵌入式
    發表于 07-14 17:31 ?31次下載

    Linux設備驅動開發詳解》第23章、Linux設備驅動的移植

    Linux設備驅動開發詳解》第23章、Linux設備驅動的移植
    發表于 10-27 10:58 ?9次下載
    《<b class='flag-5'>Linux</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>開發詳解》第23章、<b class='flag-5'>Linux</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>的移植

    Linux設備驅動開發詳解》第17章、Linux音頻設備驅動

    Linux設備驅動開發詳解》第17章、Linux音頻設備驅動
    發表于 10-27 11:14 ?17次下載
    《<b class='flag-5'>Linux</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>開發詳解》第17章、<b class='flag-5'>Linux</b>音頻<b class='flag-5'>設備</b><b class='flag-5'>驅動</b>

    Linux設備驅動開發詳解》第16章、Linux網絡設備驅動

    Linux設備驅動開發詳解》第16章、Linux網絡設備驅動
    發表于 10-27 11:17 ?5次下載
    《<b class='flag-5'>Linux</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>開發詳解》第16章、<b class='flag-5'>Linux</b>網絡<b class='flag-5'>設備</b><b class='flag-5'>驅動</b>

    Linux設備驅動開發詳解》第14章、Linux終端設備驅動

    Linux設備驅動開發詳解》第14章、Linux終端設備驅動
    發表于 10-27 11:22 ?8次下載
    《<b class='flag-5'>Linux</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>開發詳解》第14章、<b class='flag-5'>Linux</b>終端<b class='flag-5'>設備</b><b class='flag-5'>驅動</b>

    Linux設備驅動開發詳解》第13章、Linux設備驅動

    Linux設備驅動開發詳解》第13章、Linux設備驅動
    發表于 10-27 11:24 ?18次下載
    《<b class='flag-5'>Linux</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>開發詳解》第13章、<b class='flag-5'>Linux</b>塊<b class='flag-5'>設備</b><b class='flag-5'>驅動</b>

    Linux設備驅動開發詳解》第9章、Linux設備驅動中的異步通知與異步IO

    Linux設備驅動開發詳解》第9章、Linux設備驅動中的異步通知與異步IO
    發表于 10-27 11:33 ?0次下載
    《<b class='flag-5'>Linux</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>開發詳解》第9章、<b class='flag-5'>Linux</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>中的異步通知與異步IO

    Linux設備驅動開發詳解》第8章、Linux設備驅動中的阻塞與非阻塞IO

    Linux設備驅動開發詳解》第8章、Linux設備驅動中的阻塞與非阻塞IO
    發表于 10-27 11:35 ?9次下載
    《<b class='flag-5'>Linux</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>開發詳解》第8章、<b class='flag-5'>Linux</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>中的阻塞與非阻塞IO

    Linux設備驅動開發詳解》第7章、Linux設備驅動中的并發控制

    Linux設備驅動開發詳解》第7章、Linux設備驅動中的并發控制
    發表于 10-27 11:37 ?10次下載
    《<b class='flag-5'>Linux</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>開發詳解》第7章、<b class='flag-5'>Linux</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>中的并發控制

    嵌入式Linux設備驅動原理原來是這樣編寫的!

    系統調用是操作系統內核和應用程序之間的接口,設備驅動程序是操作系統內核和機器硬件之間的接口。Linux
    發表于 04-28 15:15 ?2001次閱讀

    Linux設備驅動程序分類有哪些

    Linux設備驅動程序是操作系統與硬件設備之間的橋梁,負責實現硬件設備與操作系統
    的頭像 發表于 08-30 15:11 ?704次閱讀

    linux系統的設備驅動一般分幾類

    Linux系統的設備驅動是操作系統與硬件設備之間的橋梁,負責實現操作系統與硬件設備
    的頭像 發表于 08-30 15:13 ?549次閱讀
    机械百家乐技巧| 百家乐游戏全讯网2| 黄金城| 百家乐官网规则技法| 百家乐官网扑克发牌器| 真博百家乐官网的玩法技巧和规则 | 百家乐投注哪个信誉好| 百家乐翻天电影| 博彩排名| 百家乐官网出千的高科技| 百家乐赢的秘诀| 百家乐娱乐平台会员注册| 大发888网页版体育| 门头沟区| 蓝盾百家乐官网平台| 真人百家乐导航| 大发888洗码| 澳门百家乐官网庄闲的玩法| 木星百家乐官网的玩法技巧和规则 | 百家乐官网怎样捉住长开| 摩纳哥百家乐官网的玩法技巧和规则 | 百家乐官网园搏彩论坛| 24卦| 威尼斯人娱乐网注册| 文安县| 汉百家乐官网春| 新澳博百家乐的玩法技巧和规则| 战神娱乐城| 百家乐官网技巧下载| 权威百家乐信誉网站| 百家乐中的概率| 大发888娱乐充值| 邯郸百家乐官网园真钱区| 百家乐庄和闲的赌法| 大发娱乐| 百家乐官网衬衣| 法拉利百家乐的玩法技巧和规则 | 沙龙国际娱乐| 八卦图24山代表的| 德州扑克游戏| 英皇百家乐官网的玩法技巧和规则|