原文地址:http://blog.chinaunix.net/uid-24631445-id-3418241.html
如何创建总线设备?
linux中用struct bus_type来表示总线类型,
用struct bus_attribute来表示总线属性。
当你定义一个struct bus_type对象后,调用bus_register()来注册总线,注册总线也就是将其加入整个设备模型中。
在注册总线时,会调用bus_create_file来为属性创建文件。
下面来看linux内核是如何创建一个PCI总线的。
1:总线类型的结构体和pci_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;
-
};
-
-
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;
-
};
-
-
struct bus_type pci_bus_type = {
-
.name = "pci",
-
.match = pci_bus_match,
-
.uevent = pci_uevent,
-
.probe = pci_device_probe,
-
.remove = pci_device_remove,
-
.shutdown = pci_device_shutdown,
-
.dev_attrs = pci_dev_attrs,
-
.bus_attrs = pci_bus_attrs,
-
.pm = PCI_PM_OPS_PTR,
-
};
-
-
-
struct bus_attribute {
-
struct attribute attr;
-
ssize_t (*show)(struct bus_type *bus, char *buf);
-
ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
-
};
上面定义了变量pci_bus_type,下面就是用bus_register来注册总线了。
2:bus_register
又是一个大函数,只需关心前面的部分。
-
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;
-
-
retval = bus_create_file(bus, &bus_attr_uevent);
-
if (retval)
-
goto bus_uevent_fail;
-
-
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;
-
}
-
-
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
-
klist_init(&priv->klist_drivers, NULL, NULL);
-
-
/*
-
此函数创建了两个属性文件 drivers_autoprobe和drivers_probe,相应变量的定义为:
-
-
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);
-
*/
-
retval = add_probe_files(bus);
-
if (retval)
-
goto bus_probe_files_fail;
-
/* 对struct bus_type中的bus_attrs数组中的属性,调用bus_create_file来创建属性文件,
-
对pci_bus_type,就是为pci_bus_attrs中的属性创建文件(rescan文件) */
-
retval = bus_add_attrs(bus);
-
if (retval)
-
goto bus_attrs_fail;
-
-
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;
-
}
第6行:分配了一个bus_type_private对象,priv
第15行:设置priv->subsys.kobj的名字,由pci_bus_type变量可知,subsys.kobj的名字为“PCI”
第19,20行:设定了subsys.kobj.kset和ktype
bus_kset变量最初是用buses_init()函数初始化的,函数可以看下面的分析。
bus_ktype的定义如下
struct kobj_type bus_ktype = {
.sysfs_ops = &bus_sysfs_ops,
};
关于bus_sysfs_ops,也在后面描述
第23行调用了kset_register(&priv->subsys),这个函数的作用就是将kset结构放入设备模型中去(我应该画个图来说明注册后是个什么情况),另外这个函数会调用kobject_add_internal来在/sys/bus/目录下创建pci目录。
第27行调用bus_create_file(bus, &bus_attr_uevent)为属性bus_attr_uevent来创建文件,
bus_create_file就是调用下sysfs_create_file而已。这个创建出的文件就是/sys/bus/pci/下的uevent文件。
bus_attr_uevent的定义如下:
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
等价于
struct bus_attribute bus_attr_uevent {
.attr = {.mode = S_IWUSR },
.show = NULL,
.store = bus_uevent_store
};
使用ls -l /sys/bus/pci/uevent来查看文件属性的时候,会发现该文件只有写属性,没有读属性。
--w------- 1 root root 4096 2012-11-22 23:13 /sys/bus/pci/uevent
关于bus_register之后的就是注册device和driver之类的了,目前不关心。
3:bus_kset变量
该变量使用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;
-
}
第3行调用kset_create_and_add来注册并创建/sys/bus目录,因此bus_kset是bus目录下其他目录对应的kobject的父亲。
至于此函数是何时由何人调用,目前我不知道。
4:bus_ktype变量
-
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;
-
}
-
-
static struct sysfs_ops bus_sysfs_ops = {
-
.show = bus_attr_show,
-
.store = bus_attr_store,
-
};
-
-
static struct kobj_type bus_ktype = {
-
.sysfs_ops = &bus_sysfs_ops,
-
};
这样当对uevent文件有write操作时,会发生什么?
setp 1:write操作会引起对priv->subsys.kobj->ktype.store的调用,在这里也就是调用bus_ktype.bus_sysfs_ops.store
step 2:调用bus_attr_sore函数,该函数利用attr和函数to_bus_attr找到bus_attr_uevent变量
step 3:调用bus_attr_uevent.store函数,即bus_uevent_store函数。
4:相应的bus_unregister和bus_remove_file这里不描述。
这样便在/sys/bus目录下创建除了pci目录,以及pci目录下的uevent, 也知道要写文件uevent时最终会调用哪个函数了。
随便看下就知道/sys/bus/目录下全部都是关于总线的子目录,每个总线的子目录中总有uevent文件, devices目录和drivers目录。
阅读(1328) | 评论(0) | 转发(0) |