1. 概述
在Linux設備模型中,Bus(總線)是一類特殊的設備,它是連接處理器和其它設備之間的通道(channel)。為了方便設備模型的實現,內核規定,系統中的每個設備都要連接在一個Bus上,這個Bus可以是一個內部Bus、虛擬Bus或者Platform Bus。
內核通過struct bus_type結構,抽象Bus,它是在include/linux/device.h中定義的。本文會圍繞該結構,描述Linux內核中Bus的功能,以及相關的實現邏輯。最后,會簡單的介紹一些標準的Bus(如Platform),介紹它們的用途、它們的使用場景。
2. 功能說明
按照老傳統,描述功能前,先介紹一下該模塊的一些核心數據結構,對bus模塊而言,核心數據結構就是struct bus_type,另外,還有一個sub system相關的結構,會一并說明。
2.1 struct bus_type
1: /* inlcude/linux/device.h, line 93 */
2: struct bus_type {
3: ????const char *name;
4: ????const char *dev_name;
5: ????struct device *dev_root;
6: ????struct bus_attribute *bus_attrs;
7: ????struct device_attribute *dev_attrs;
8: ????struct driver_attribute *drv_attrs;
9:
10:????int (*match)(struct device *dev, struct device_driver *drv);
11:????int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
12:????int (*probe)(struct device *dev);
13:????int (*remove)(struct device *dev);
14:????void (*shutdown)(struct device *dev);
15:
16:????int (*suspend)(struct device *dev, pm_message_t state);
17:????int (*resume)(struct device *dev);
18:
19:????const struct dev_pm_ops *pm;
20:
21:????struct iommu_ops *iommu_ops;
22:
23:????struct subsys_private *p;
24:????struct lock_class_key lock_key;
25: };
name,該bus的名稱,會在sysfs中以目錄的形式存在,如platform bus在sysfs中表現為"/sys/bus/platform”。
dev_name,該名稱和"Linux設備模型(5)_device和device driver”所講述的struct device結構中的init_name有關。對有些設備而言(例如批量化的USB設備),設計者根本就懶得為它起名字的,而內核也支持這種懶惰,允許將設備的名字留空。這樣當設備注冊到內核后,設備模型的核心邏輯就會用"bus->dev_name+device ID”的形式,為這樣的設備生成一個名稱。
bus_attrs、dev_attrs、drv_attrs,一些默認的attribute,可以在bus、device或者device_driver添加到內核時,自動為它們添加相應的attribute。
dev_root,根據內核的注釋,dev_root設備為bus的默認父設備(Default device to use as the parent),但在內核實際實現中,只和一個叫sub system的功能有關,隨后會介紹。
match,一個由具體的bus driver實現的回調函數。當任何屬于該Bus的device或者device_driver添加到內核時,內核都會調用該接口,如果新加的device或device_driver匹配上了自己的另一半的話,該接口要返回非零值,此時Bus模塊的核心邏輯就會執行后續的處理。
uevent,一個由具體的bus driver實現的回調函數。當任何屬于該Bus的device,發生添加、移除或者其它動作時,Bus模塊的核心邏輯就會調用該接口,以便bus driver能夠修改環境變量。
probe、remove,這兩個回調函數,和device_driver中的非常類似,但它們的存在是非常有意義的。可以想象一下,如果需要probe(其實就是初始化)指定的device話,需要保證該device所在的bus是被初始化過、確保能正確工作的。這就要就在執行device_driver的probe前,先執行它的bus的probe。remove的過程相反。?
注1:并不是所有的bus都需要probe和remove接口的,因為對有些bus來說(例如platform bus),它本身就是一個虛擬的總線,無所謂初始化,直接就能使用,因此這些bus的driver就可以將這兩個回調函數留空。
shutdown、suspend、resume,和probe、remove的原理類似,電源管理相關的實現,暫不說明。
pm,電源管理相關的邏輯,暫不說明。
iommu_ops,暫不說明。
p,一個struct subsys_private類型的指針,后面我們會用一個小節說明。
2.2 struct subsys_private
該結構和device_driver中的struct driver_private類似,在"Linux設備模型(5)_device和device driver”章節中有提到它,但沒有詳細說明。
要說明subsys_private的功能,讓我們先看一下該結構的定義:
1: /* drivers/base/base.h, line 28 */
2: struct subsys_private {
3: ????struct kset subsys;
4: ????struct kset *devices_kset;
5: ????struct list_head interfaces;
6: ????struct mutex mutex;
7:
8: ????struct kset *drivers_kset;
9: ????struct klist klist_devices;
10:????struct klist klist_drivers;
11:????struct blocking_notifier_head bus_notifier;
12:????unsigned int drivers_autoprobe:1;
13:????struct bus_type *bus;
14:
15:????struct kset glue_dirs;
16:????struct class *class;
17: };
看到結構內部的字段,就清晰多了,沒事不要亂起名字嘛!什么subsys啊,看的暈暈的!不過還是試著先理解一下為什么起名為subsys吧:
按理說,這個結構就是集合了一些bus模塊需要使用的私有數據,例如kset啦、klist啦等等,命名為bus_private會好點(就像device_driver模塊一樣)。不過為什么內核沒這么做呢?看看include/linux/device.h中的struct class結構(我們會在下一篇文章中介紹class)就知道了,因為class結構中也包含了一個一模一樣的struct subsys_private指針,看來class和bus很相似啊。
想到這里,就好理解了,無論是bus,還是class,還是我們會在后面看到的一些虛擬的子系統,它都構成了一個“子系統(sub-system)”,該子系統會包含形形色色的device或device_driver,就像一個獨立的王國一樣,存在于內核中。而這些子系統的表現形式,就是/sys/bus(或/sys/class,或其它)目錄下面的子目錄,每一個子目錄,都是一個子系統(如/sys/bus/spi/)。
好了,我們回過頭來看一下struct subsys_private中各個字段的解釋:
subsys、devices_kset、drivers_kset是三個kset,由"Linux設備模型(2)_Kobject”中對kset的描述可知,kset是一個特殊的kobject,用來集合相似的kobject,它在sysfs中也會以目錄的形式體現。其中subsys,代表了本bus(如/sys/bus/spi),它下面可以包含其它的kset或者其它的kobject;devices_kset和drivers_kset則是bus下面的兩個kset(如/sys/bus/spi/devices和/sys/bus/spi/drivers),分別包括本bus下所有的device和device_driver。
interface是一個list head,用于保存該bus下所有的interface。有關interface的概念后面會詳細介紹。
klist_devices和klist_drivers是兩個鏈表,分別保存了本bus下所有的device和device_driver的指針,以方便查找。
drivers_autoprobe,用于控制該bus下的drivers或者device是否自動probe,"Linux設備模型(5)_device和device driver”中有提到。
bus和class指針,分別保存上層的bus或者class指針。
2.3 功能總結
根據上面的核心數據結構,可以總結出bus模塊的功能包括:
bus的注冊和注銷
本bus下有device或者device_driver注冊到內核時的處理
本bus下有device或者device_driver從內核注銷時的處理
device_drivers的probe處理
管理bus下的所有device和device_driver
3. 內部執行邏輯分析
3.1 bus的注冊
bus的注冊是由bus_register接口實現的,該接口的原型是在include/linux/device.h中聲明的,并在drivers/base/bus.c中實現,其原型如下:
1: /* include/linux/device.h, line 118 */
2: extern int __must_check bus_register(struct bus_type *bus);
該功能的執行邏輯如下:
為bus_type中struct subsys_private類型的指針分配空間,并更新priv->bus和bus->p兩個指針為正確的值
初始化priv->subsys.kobj的name、kset、ktype等字段,啟動name就是該bus的name(它會體現在sysfs中),kset和ktype由bus模塊實現,分別為bus_kset和bus_ktype
調用kset_register將priv->subsys注冊到內核中,該接口同時會向sysfs中添加對應的目錄(如/sys/bus/spi)
調用bus_create_file向bus目錄下添加一個uevent attribute(如/sys/bus/spi/uevent)
調用kset_create_and_add分別向內核添加devices和device_drivers kset,同時會體現在sysfs中
初始化priv指針中的mutex、klist_devices和klist_drivers等變量
調用add_probe_files接口,在bus下添加drivers_probe和drivers_autoprobe兩個attribute(如/sys/bus/spi/drivers_probe和/sys/bus/spi/drivers_autoprobe),其中drivers_probe允許用戶空間程序主動出發指定bus下的device_driver的probe動作,而drivers_autoprobe控制是否在device或device_driver添加到內核時,自動執行probe
調用bus_add_attrs,添加由bus_attrs指針定義的bus的默認attribute,這些attributes最終會體現在/sys/bus/xxx目錄下
3.2 device和device_driver的添加
我們有在"Linux設備模型(5)_device和device driver”中講過,內核提供了device_register和driver_register兩個接口,供各個driver模塊使用。而這兩個接口的核心邏輯,是通過bus模塊的bus_add_device和bus_add_driver實現的,下面我們看看這兩個接口的處理邏輯。
這兩個接口都是在drivers/base/base.h中聲明,在drivers/base/bus.c中實現,其原型為:
1: /* drivers/base/base.h, line 106 */
2: extern int bus_add_device(struct device *dev);
3:
4: /* drivers/base/base.h, line 110 */
5: extern int bus_add_driver(struct device_driver *drv);
bus_add_device的處理邏輯:
調用內部的device_add_attrs接口,將由bus->dev_attrs指針定義的默認attribute添加到內核中,它們會體現在/sys/devices/xxx/xxx_device/目錄中
調用sysfs_create_link接口,將該device在sysfs中的目錄,鏈接到該bus的devices目錄下,例如:?
xxx# ls /sys/bus/spi/devices/spi1.0 -l?????????????????????????????????????????????????????????
lrwxrwxrwx root???? root????????????? 2014-04-11 10:46?spi1.0?-> ../../../devices/platform/s3c64xx-spi.1/spi_master/spi1/spi1.0?
其中/sys/devices/…/spi1.0,為該device在sysfs中真正的位置,而為了方便管理,內核在該設備所在的bus的xxx_bus/devices目錄中,創建了一個符號鏈接
調用sysfs_create_link接口,在該設備的sysfs目錄中(如/sys/devices/platform/alarm/)中,創建一個指向該設備所在bus目錄的鏈接,取名為subsystem,例如:?
xxx # ls /sys/devices/platform/alarm/subsystem?-l?????????????????????????????????????????????????
lrwxrwxrwx root???? root????????????? 2014-04-11 10:28 subsystem ->?../../../bus/platform
最后,毫無疑問,要把該設備指針保存在bus->priv->klist_devices中
bus_add_driver的處理邏輯:
為該driver的struct driver_private指針(priv)分配空間,并初始化其中的priv->klist_devices、priv->driver、priv->kobj.kset等變量,同時將該指針保存在device_driver的p處
將driver的kset(priv->kobj.kset)設置為bus的drivers kset(bus->p->drivers_kset),這就意味著所有driver的kobject都位于bus->p->drivers_kset之下(寄/sys/bus/xxx/drivers目錄下)
以driver的名字為參數,調用kobject_init_and_add接口,在sysfs中注冊driver的kobject,體現在/sys/bus/xxx/drivers/目錄下,如/sys/bus/spi/drivers/spidev
將該driver保存在bus的klist_drivers鏈表中,并根據drivers_autoprobe的值,選擇是否調用driver_attach進行probe
調用driver_create_file接口,在sysfs的該driver的目錄下,創建uevent attribute
調用driver_add_attrs接口,在sysfs的該driver的目錄下,創建由bus->drv_attrs指針定義的默認attribute
同時根據suppress_bind_attrs標志,決定是否在sysfs的該driver的目錄下,創建bind和unbind attribute(具體可參考"Linux設備模型(5)_device和device driver”中的介紹)?
3.3 driver的probe
我們在"Linux設備模型(5)_device和device driver”中,我們已經介紹過driver的probe時機及過程,其中大部分的邏輯會依賴bus模塊的實現,主要為bus_probe_device和driver_attach接口。同樣,這兩個接口都是在drivers/base/base.h中聲明,在drivers/base/bus.c中實現。
這兩個結構的行為類似,邏輯也很簡單,既:搜索所在的bus,比對是否有同名的device_driver(或device),如果有并且該設備沒有綁定Driver(注:這一點很重要,通過它,可以使同一個Driver,驅動相同名稱的多個設備,后續在Platform設備的描述中會提及)則調用device_driver的probe接口。
4. 雜項
4.1 再談Subsystem
在舊的Linux內核版本中(以蝸蝸使用的linux2.6.23版本的內核為例),sysfs下所有的頂層目錄(包括一些二級目錄)都是以調用subsystem_register接口,以sub-system的形式注冊到內核的,如:
/sys/bus/
/sys/devices/
/sys/devices/system/
/sys/block
/sys/kernel/
/sys/slab/
…
那時的subsystem_register的實現很簡單,就是調用kset_register,創建一個kset。我們知道,kset就是一堆kobject的集合,并會在sysfs中以目錄的形式呈現出來。
在新版本的內核中(如“Linux內核分析”系列文章所參考的linux3.10.29),subsystem的實現有了很大變化,例如:去掉了subsystem_register接口(但為了兼容/sys/device/system子系統,在drivers/base/bus.c中,增加了一個subsys_register的內部接口,用于實現相應的功能)。根據這些變化,現在注冊subsystem有兩種方式:
方式一,在各自的初始化函數中,調用kset_create_and_add接口,創建對應的子系統,包括:
bus子系統,/sys/bus/,buses_init(drivers/base/bus.c)
class子系統,/sys/class
kernel子系統,/sys/kernel
firmware子系統,/sys/firmware
等等
其中bus子系統就是本文所講的Bus模塊,而其它的,我們會在后續的文章中陸續講述。這個方式和舊版本內核使用kset_register接口的方式基本一樣。
方式二,在bus模塊中,利用subsys_register接口,封裝出兩個API:subsys_system_register和subsys_virtual_register,分別用于注冊system設備(/sys/devices/system/*)和virtual設備(/sys/devices/virtual/*)。 而該方式和方式一的區別是:它不僅僅創建了sysfs中的目錄,同時會注冊同名的bus和device。
4.2 system/virtual/platform
在Linux內核中,有三種比較特殊的bus(或者是子系統),分別是system bus、virtual bus和platform bus。它們并不是一個實際存在的bus(像USB、I2C等),而是為了方便設備模型的抽象,而虛構的。
system bus是舊版內核提出的概念,用于抽象系統設備(如CPU、Timer等等)。而新版內核認為它是個壞點子,因為任何設備都應歸屬于一個普通的子系統(New subsystems should use plain subsystems, drivers/base/bus.c, line 1264),所以就把它拋棄了(不建議再使用,它的存在只為兼容舊有的實現)。
virtaul bus是一個比較新的bus,主要用來抽象那些虛擬設備,所謂的虛擬設備,是指不是真實的硬件設備,而是用軟件模擬出來的設備,例如虛擬機中使用的虛擬的網絡設備(有關該bus的描述,可參考該鏈接處的解釋:https://lwn.net/Articles/326540/)。
platform bus就比較普通,它主要抽象集成在CPU(SOC)中的各種設備。這些設備直接和CPU連接,通過總線尋址和中斷的方式,和CPU交互信息。?
我們會在后續的文章中,進一步分析這些特殊的bus,這里就暫時不詳細描述了。
4.3 subsys interface
subsys interface是一個很奇怪的東西,除非有一個例子,否則很難理解。代碼中是這樣注釋的:
Interfaces usually represent?a specific functionality of a subsystem/class of devices.
字面上理解,它抽象了bus下所有設備的一些特定功能。
kernel使用struct subsys_interface結構抽象subsys interface,并提供了subsys_interface_register/subsys_interface_unregister用于注冊/注銷subsys interface,bus下所有的interface都掛載在struct subsys_private變量的“interface”鏈表上(具體可參考2.2小節的描述)。struct subsys_interface的定義如下:
/**
* struct subsys_interface - interfaces to device functions
* @name: name of the device function
* @subsys: subsytem of the devices to attach to
* @node: the list of functions registered at the subsystem
* @add_dev: device hookup to device function handler
* @remove_dev: device hookup to device function handler
*
* Simple interfaces attached to a subsystem. Multiple interfaces can
* attach to a subsystem and its devices. Unlike drivers, they do not
* exclusively claim or control devices. Interfaces usually represent
* a specific functionality of a subsystem/class of devices.
*/
struct subsys_interface {
const char *name;
struct bus_type *subsys;
struct list_head node;
int (*add_dev)(struct device *dev, struct subsys_interface *sif);
int (*remove_dev)(struct device *dev, struct subsys_interface *sif);
};
name,interface的名稱。
subsys,interface所屬的bus。
node,用于將interface掛到bus中。
add_dev/remove_dev,兩個回調函數,subsys interface的核心功能。當bus下有設備增加或者刪除的時候,bus core會調用它下面所有subsys interface的add_dev或者remove_dev回調。設計者可以在這兩個回調函數中實現所需功能,例如綁定該“specific functionality”所對應的driver,等等。
subsys interface的實現邏輯比較簡單,這里不再詳細描述了,具體可參考“drivers/base/bus.c”中相應的代碼。另外,后續分析cpufreq framework的時候,會遇到使用subsys interface的例子,到時候我們再進一步理解它的現實意義。
?
評論
查看更多