如何创建总线设备?
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目录。
阅读(155) | 评论(0) | 转发(0) |