在清楚了kobject之後,就可以繼續分析device、driver、bus了,這三者是設備驅動程序的基本數據結構。
我們可以這樣理解,內核用device來表示各種設備,然後用driver來表示它的驅動,而設備有很多種,也屬於相同類型或不同類型,而其對應的驅動可能同時也是另外一個設備的驅動,爲了管理這些設備和驅動,就引入了總線bus_type,總線上有兩個集合(也可以理解爲兩條鏈,如上圖中的bus),分別用來存放該總線類型的設備和驅動,當添加一個設備時就將設備添加到總線的設備集合(圖中操作2),同時可能會到驅動集合去匹配適合它的驅動(圖中操作3,在此之前device和driver沒有掛鉤),如何找到了就會將它們聯繫起來(圖中操作6)。同樣註冊一個驅動,就會把它掛到相應總線類型的驅動集合裏(圖中操作1),同時去設備集合中找出它鎖驅動的設備(圖中操作4,在此之前device和driver沒有掛鉤),如果找到就把設備鏈接到它支持的設備鏈表上(圖中操作6)。
下面我們進入到代碼中去:
struct bus_type結構定義如下:
struct bus_type {
const char *name;
struct bus_attribute *bus_attrs;
struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs;
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 (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
struct bus_type_private *p;
};
較之先前一些內核版本,bus_type把部分私有字段封裝到bus_type_private類型結構裏:
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;
};
字段klist_devices和klist_drivers分別表示掛在bus_type上的驅動和設備鏈表,bus_type的其他字段和函數指針將在分析過程中說明。
首先我們要爲設備和驅動註冊一個總線類型:
int bus_register(struct bus_type *bus)
{
int retval;
struct bus_type_private *priv;
priv = kzalloc(sizeof(struct bus_type_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;
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys);
if (retval)
goto out;
這裏我們看到了subsys用來表示它的文件系統,可以回想上一節kset的註冊。
這個bus_kset是系統啓動是創建的,系統init進程kernel_init()中調用do_basic_setup(),其中調用driver_init(),其中調用的buses_init(),如下
int __init buses_init(void)
{
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
return 0;
}
從而知道創建的文件系統目錄在/sys/bus下。
static struct kset_uevent_ops bus_uevent_ops = {
.filter = bus_uevent_filter,
};
static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
{
struct kobj_type *ktype = get_ktype(kobj);
if (ktype == &bus_ktype)
return 1;
return 0;
}
繼續bus_register()中的代碼:
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
bus_create_file()如下:
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
{
int error;
if (bus_get(bus)) {
error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
bus_put(bus);
} else
error = -EINVAL;
return error;
}
用bus_attr_uevent創建了bus->p->subsys.kobj的屬性文件,由上面的賦值知道其讀寫操作在bus_ktype的sysfs_ops,其定義如下:
static struct kobj_type bus_ktype = {
.sysfs_ops = &bus_sysfs_ops,
};
static struct sysfs_ops bus_sysfs_ops = {
.show = bus_attr_show,
.store = bus_attr_store,
};
static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct bus_attribute *bus_attr = to_bus_attr(attr);
struct bus_type_private *bus_priv = to_bus(kobj);
ssize_t ret = 0;
if (bus_attr->show)
ret = bus_attr->show(bus_priv->bus, buf);
return ret;
}
static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct bus_attribute *bus_attr = to_bus_attr(attr);
struct bus_type_private *bus_priv = to_bus(kobj);
ssize_t ret = 0;
if (bus_attr->store)
ret = bus_attr->store(bus_priv->bus, buf, count);
return ret;
}
由上面的程序可以看出文件的讀寫操作最終會回到struct bus_attribute &bus_attr_uevent的show和store方法。
bus_attr_uevent是通過宏定義的:
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
#define BUS_ATTR(_name, _mode, _show, _store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
其show方法爲NULL,說明不可讀。
static ssize_t bus_uevent_store(struct bus_type *bus,
const char *buf, size_t count)
{
enum kobject_action action;
if (kobject_action_type(buf, count, &action) == 0)
kobject_uevent(&bus->p->subsys.kobj, action);
return count;
}
在用戶空間可以控制事件的發生,如echo add > event將產生一個add的事件。
接着繼續bus_register()中的代碼:
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;
}
創建兩個kset,其內嵌object的parent都指向priv->subsys.kobj,說明其文件系統在bus所在目錄下。由上回分析中知道kset_create_and_add()時其內嵌kobj的ktype指向kset_ktype,而這裏沒有輸入參數的uevent_ops爲NULL,則會以priv->subsys.kobj->kset->uevent_ops來產生事件,我們上面分析中知道這個uevent_ops爲bus_uevent_ops,其filter會比較kobj的ktype是不是&bus_ktype,而這裏是&kset_ktype,所以這裏是忽略了事件。
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
初始化總線上設備和驅動的鏈表。
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
add_probe_files()函數如下:
static int add_probe_files(struct bus_type *bus)
{
int retval;
retval = bus_create_file(bus, &bus_attr_drivers_probe);
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
if (retval)
bus_remove_file(bus, &bus_attr_drivers_probe);
out:
return retval;
}
同上面創建bus_attr_uevent屬性一樣創建bus_attr_drivers_probe和bus_attr_drivers_autoprobe屬性文件。粘出屬性的代碼:
static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
show_drivers_autoprobe, store_drivers_autoprobe);
bus_attr_drivers_autoprobe的show指向NULL,說明其改文件不可寫。
static ssize_t store_drivers_probe(struct bus_type *bus,
const char *buf, size_t count)
{
struct device *dev;
dev = bus_find_device_by_name(bus, NULL, buf);
if (!dev)
return -ENODEV;
if (bus_rescan_devices_helper(dev, NULL) != 0)
return -EINVAL;
return count;
}
將用戶輸(在用戶空間)和的設備名稱對應的設備與驅動匹配一次。
static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
{
return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
}
在用戶空間可以打印drivers_autoprobe的值,如cat drivers_autoprobe
static ssize_t store_drivers_autoprobe(struct bus_type *bus,
const char *buf, size_t count)
{
if (buf[0] == '0')
bus->p->drivers_autoprobe = 0;
else
bus->p->drivers_autoprobe = 1;
return count;
}
在用戶空間可以改變drivers_autoprobe的值,如echo 1 > drivers_autoprobe
繼續分析bus_register()中的代碼:
retval = bus_add_attrs(bus);
if (retval)
goto bus_attrs_fail;
bus_add_attrs()如下:
static int bus_add_attrs(struct bus_type *bus)
{
int error = 0;
int i;
if (bus->bus_attrs) {
for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
error = bus_create_file(bus, &bus->bus_attrs[i]);
if (error)
goto err;
}
}
done:
return error;
err:
while (--i >= 0)
bus_remove_file(bus, &bus->bus_attrs[i]);
goto done;
}
如果bus->bus_attrs存在,則同樣爲其創建屬性文件。
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
bus_attrs_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);
kfree(bus->p);
out:
bus->p = NULL;
return retval;
}
bus_register()分析完了,總結一下,它註冊了一個總線類型,創建對應的文件系統(包括目錄和屬性),初始化總線上的驅動和設備,這樣我們就可以通過內核提供的函數往總線上註冊設備和驅動了。
接下一篇文章。