linux裝置模型八(bus)
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相關的結構,會一併說明。
/** * struct bus_type - The bus type of the device * * @name: The name of the bus. * @dev_name: Used for subsystems to enumerate devices like ("foo%u", dev->id). * @dev_root: Default device to use as the parent. * @dev_attrs: Default attributes of the devices on the bus. * @bus_groups: Default attributes of the bus. * @dev_groups: Default attributes of the devices on the bus. * @drv_groups: Default attributes of the device drivers on the bus. * @match: Called, perhaps multiple times, whenever a new device or driver * is added for this bus. It should return a positive value if the * given device can be handled by the given driver and zero * otherwise. It may also return error code if determining that * the driver supports the device is not possible. In case of * -EPROBE_DEFER it will queue the device for deferred probing. * @uevent: Called when a device is added, removed, or a few other things * that generate uevents to add the environment variables. * @probe: Called when a new device or driver add to this bus, and callback * the specific driver's probe to initial the matched device. * @remove: Called when a device removed from this bus. * @shutdown: Called at shut-down time to quiesce the device. * * @online: Called to put the device back online (after offlining it). * @offline: Called to put the device offline for hot-removal. May fail. * * @suspend: Called when a device on this bus wants to go to sleep mode. * @resume: Called to bring a device on this bus out of sleep mode. * @pm: Power management operations of this bus, callback the specific * device driver's pm-ops. * @iommu_ops: IOMMU specific operations for this bus, used to attach IOMMU * driver implementations to a bus and allow the driver to do * bus-specific setup * @p: The private data of the driver core, only the driver core can * touch this. * @lock_key: Lock class key for use by the lock validator * * A bus is a channel between the processor and one or more devices. For the * purposes of the device model, all devices are connected via a bus, even if * it is an internal, virtual, "platform" bus. Buses can plug into each other. * A USB controller is usually a PCI device, for example. The device model * represents the actual connections between buses and the devices they control. * A bus is represented by the bus_type structure. It contains the name, the * default attributes, the bus' methods, PM operations, and the driver core's * private data. */ struct bus_type { const char *name; const char *dev_name; struct device *dev_root; struct device_attribute *dev_attrs; /* use dev_groups instead */ const struct attribute_group **bus_groups; const struct attribute_group **dev_groups; const struct attribute_group **drv_groups; int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); int (*online)(struct device *dev); int (*offline)(struct device *dev); int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); const struct dev_pm_ops *pm; const struct iommu_ops *iommu_ops; struct subsys_private *p; struct lock_class_key lock_key; };
name,該bus的名稱,會在sysfs中以目錄的形式存在,如platform bus在sysfs中表現為"/sys/bus/platform”。
dev_name,該名稱和Linux裝置模型五(device和device driver)所講述的struct device結構中的init_name有關。對有些裝置而言(例如批量化的USB裝置),設計者根本就懶得為它起名字的,而核心也支援這種懶惰,允許將裝置的名字留空。這樣當設備註冊到核心後,裝置模型的核心邏輯就會用"bus->dev_name+device ID”的形式,為這樣的裝置生成一個名稱。用官方翻譯說:用於子系統列舉像(“foo%u”,dev-> id)這樣的裝置
dev_attr,總線上裝置的預設屬性
bus_groups、bus_groups、bus_groups,一些預設的attribute,可以在bus、device或者device_driver新增到核心時,自動為它們新增相應的attribute。(老一些版本的核心是叫device_attribute,driver_attribute,bus_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的原理類似,電源管理相關的實現,暫不說明。
online:被呼叫以使裝置重新上線(在離線後)。
offline:被呼叫以使裝置離線以進行熱移除。可能會失敗。
pm,電源管理相關的邏輯,暫不說明。
iommu_ops,此匯流排的IOMMU特定操作,用於連線IOMMU驅動程式實現到匯流排並允許驅動程式執行匯流排專用設定。
p,一個struct subsys_private型別的指標,驅動核心的私有資料,只有驅動核心才可以觸控這個。後面我們會用一個小節說明。
2.2 struct subsys_private
該結構和device_driver中的struct driver_private類似,在前面的章節 "裝置模型驅動七(device_driver細節)"中有提到它,但沒有詳細說明。
要說明subsys_private的功能,讓我們先看一下該結構的定義:
/**
* struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
*
* @subsys - the struct kset that defines this subsystem
* @devices_kset - the subsystem's 'devices' directory
* @interfaces - list of subsystem interfaces associated
* @mutex - protect the devices, and interfaces lists.
*
* @drivers_kset - the list of drivers associated
* @klist_devices - the klist to iterate over the @devices_kset
* @klist_drivers - the klist to iterate over the @drivers_kset
* @bus_notifier - the bus notifier list for anything that cares about things
* on this bus.
* @bus - pointer back to the struct bus_type that this structure is associated
* with.
*
* @glue_dirs - "glue" directory to put in-between the parent device to
* avoid namespace conflicts
* @class - pointer back to the struct class that this structure is associated
* with.
*
* This structure is the one that is the actual kobject allowing struct
* bus_type/class to be statically allocated safely. Nothing outside of the
* driver core should ever touch these fields.
*/
struct subsys_private {
struct kset subsys;
struct kset *devices_kset;
struct list_head interfaces;
struct mutex mutex;
struct kset *drivers_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
struct kset glue_dirs;
struct class *class;
};
看到結構內部的欄位,就清晰多了,沒事不要亂起名字嘛!什麼subsys啊,看的暈暈的!不過還是試著先理解一下為什麼起名為subsys吧:
按理說,這個結構就是集合了一些bus模組需要使用的私有資料,例如kset啦、klist啦等等,命名為bus_private會好點(就像device、driver模組一樣)【事實上早期版本確實是命名為bus_type_private】。不過為什麼核心最終拋棄了呢呢?看看include/linux/device.h中的struct class結構(我們會在下一篇文章中介紹class)就知道了,因為class結構中也包含了一個一模一樣的struct subsys_private指標,看來class和bus很相似啊,所以在核心的subsys_private在現在最新版本就是這樣。
2.6.35.7 最開始bus和class的private是分開的,已經很相似了
struct class_private {
struct kset class_subsys;
struct klist class_devices;
struct list_head class_interfaces;
struct kset class_dirs;
struct mutex class_mutex;
struct class *class;
};
struct bus_type_private {
struct kset subsys;
struct kset *drivers_kset;
struct kset *devices_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
};
3.xxx bus有需求升級為subsys_private ,同時為後面去掉class_private 做基礎
struct subsys_private {
struct kset subsys;
struct kset *devices_kset;
struct list_head interfaces;
struct mutex mutex;
struct kset *drivers_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
struct kset glue_dirs;
struct class *class;
};
struct class_private {
struct kset class_subsys;
struct klist class_devices;
struct list_head class_interfaces;
struct kset class_dirs;
struct mutex class_mutex;
struct class *class;
};
3.x後期,兩者完全統一用這個,class_private 在這個版本已經完全看不到了
struct subsys_private {
struct kset subsys;
struct kset *devices_kset;
struct list_head interfaces;
struct mutex mutex;
struct kset *drivers_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus; //
struct kset glue_dirs;
struct class *class; //
};
想到這裡,就好理解了,無論是bus,還是class,還是我們會在後面看到的一些虛擬的子系統,它都構成了一個“子系統(sub-system)”,該子系統會包含形形色色的device或device_driver,就像一個獨立的王國一樣,存在於核心中。而這些子系統的表現形式,就是/sys/bus(或/sys/class,或其它)目錄下面的子目錄,每一個子目錄,都是一個子系統(如/sys/bus/spi/)。
/**
* bus_register - register a driver-core subsystem
* @bus: bus to register
*
* Once we have that, we register the bus with the kobject
* infrastructure, then register the children subsystems it has:
* the devices and drivers that belong to the subsystem.
*/
int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
struct lock_class_key *key = &bus->lock_key;
/* 分配子系統 */
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = bus;
bus->p = priv;
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
priv->subsys.kobj.kset = bus_kset; /* 繫結核心再初始化時就位所有的bus統一申請的kset */
priv->subsys.kobj.ktype = &bus_ktype; /* 繫結ktype */
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys); /* 註冊kset,即在bus目錄下就出現了對應目錄 */
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_uevent); /** /
if (retval)
goto bus_uevent_fail;
/* bus目錄下建立devices和drivers目錄,用來將來存放繫結該匯流排的驅動 */
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
/* 初始化 */
INIT_LIST_HEAD(&priv->interfaces);
__mutex_init(&priv->mutex, "subsys mutex", key);
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
/* 匯流排的最重要的目的是匹配device和driver,用的就是probe機制 */
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
/* 匯流排可以屬於其它匯流排的組裡面 */
retval = bus_add_groups(bus, bus->bus_groups);
if (retval)
goto bus_groups_fail;
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
bus_groups_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
kset_unregister(&bus->p->subsys);
out:
kfree(bus->p);
bus->p = NULL;
return retval;
}
匯流排最主要的功能是匹配繫結到它下面的device和driver,上面的其它都是建立sys檔案系統的關聯性的東西。
最後以一個小例子,來實現一條簡單匯流排。(該例子來自國嵌的老視訊,新核心有部分改動)
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
MODULE_AUTHOR("David Xie");
MODULE_LICENSE("Dual BSD/GPL");
static char *Version = "$Revision: 1.0 $";
static int my_match(struct device *dev, struct device_driver *driver)
{
/* 比較裝置和驅動的名字 */
return !strncmp(dev_name(dev), driver->name, strlen(driver->name));
}
struct bus_type my_bus_type = {
.name = "my_bus",
.match = my_match,
};
EXPORT_SYMBOL(my_bus_type);
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
static int __init my_bus_init(void)
{
int ret;
/*註冊匯流排*/
ret = bus_register(&my_bus_type);
if (ret)
return ret;
/*建立屬性檔案*/
if (bus_create_file(&my_bus_type, &bus_attr_version))
printk(KERN_NOTICE "Fail to create version attribute!\n");
return ret;
}
static void my_bus_exit(void)
{
bus_unregister(&my_bus_type);
}
module_init(my_bus_init);
module_exit(my_bus_exit);
上面繼續複習了前面的attribute屬性,以及基本的驅動模型的使用。匯流排的match函式,即比較裝置和驅動的函式名。
參考部落格