Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3170453
  • 博文数量: 685
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 5303
  • 用 户 组: 普通用户
  • 注册时间: 2014-04-19 14:17
个人简介

文章分类

全部博文(685)

文章存档

2015年(116)

2014年(569)

分类: LINUX

2014-09-05 10:26:22

原文地址: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变量。 

比较复杂,目前只需关注这些结构体中红色的域。

  1. struct bus_type {
  2.     const char        *name;
  3.     struct bus_attribute    *bus_attrs;
  4.     struct device_attribute    *dev_attrs;
  5.     struct driver_attribute    *drv_attrs;

  6.     int (*match)(struct device *dev, struct device_driver *drv);
  7.     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
  8.     int (*probe)(struct device *dev);
  9.     int (*remove)(struct device *dev);
  10.     void (*shutdown)(struct device *dev);

  11.     int (*suspend)(struct device *dev, pm_message_t state);
  12.     int (*resume)(struct device *dev);

  13.     const struct dev_pm_ops *pm;

  14.     struct bus_type_private *p;
  15. };

  16. struct bus_type_private {
  17.     struct kset subsys;
  18.     struct kset *drivers_kset;
  19.     struct kset *devices_kset;
  20.     struct klist klist_devices;
  21.     struct klist klist_drivers;
  22.     struct blocking_notifier_head bus_notifier;
  23.     unsigned int drivers_autoprobe:1;
  24.     struct bus_type *bus;
  25. };

  26. struct bus_type pci_bus_type = {
  27.     .name        = "pci",
  28.     .match        = pci_bus_match,
  29.     .uevent        = pci_uevent,
  30.     .probe        = pci_device_probe,
  31.     .remove        = pci_device_remove,
  32.     .shutdown    = pci_device_shutdown,
  33.     .dev_attrs    = pci_dev_attrs,
  34.     .bus_attrs    = pci_bus_attrs,
  35.     .pm        = PCI_PM_OPS_PTR,
  36. };
  37.  
  38. struct bus_attribute {
  39. struct attribute attr;
  40. ssize_t (*show)(struct bus_type *bus, char *buf);
  41. ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
  42. };

上面定义了变量pci_bus_type,下面就是用bus_register来注册总线了。

2:bus_register
又是一个大函数,只需关心前面的部分。

  1. int bus_register(struct bus_type *bus)
  2. {
  3.     int retval;
  4.     struct bus_type_private *priv;

  5.     priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
  6.     if (!priv)
  7.         return -ENOMEM;

  8.     priv->bus = bus;
  9.     bus->= priv;

  10.     BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

  11.     retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
  12.     if (retval)
  13.         goto out;

  14.     priv->subsys.kobj.kset = bus_kset;
  15.     priv->subsys.kobj.ktype = &bus_ktype;
  16.     priv->drivers_autoprobe = 1;

  17.     retval = kset_register(&priv->subsys);
  18.     if (retval)
  19.         goto out;

  20.     retval = bus_create_file(bus, &bus_attr_uevent);
  21.     if (retval)
  22.         goto bus_uevent_fail;

  23.     priv->devices_kset = kset_create_and_add("devices", NULL,
  24.                          &priv->subsys.kobj);
  25.     if (!priv->devices_kset) {
  26.         retval = -ENOMEM;
  27.         goto bus_devices_fail;
  28.     }

  29.     priv->drivers_kset = kset_create_and_add("drivers", NULL,
  30.                          &priv->subsys.kobj);
  31.     if (!priv->drivers_kset) {
  32.         retval = -ENOMEM;
  33.         goto bus_drivers_fail;
  34.     }

  35.     klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
  36.     klist_init(&priv->klist_drivers, NULL, NULL);
  37.      
  38.     /* 
  39. 此函数创建了两个属性文件 drivers_autoprobe和drivers_probe,相应变量的定义为:
  40. static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
  41. static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
  42. show_drivers_autoprobe, store_drivers_autoprobe);
  43. */
  1.     retval = add_probe_files(bus);
  2.     if (retval)
  3.         goto bus_probe_files_fail;
  4. /* 对struct bus_type中的bus_attrs数组中的属性,调用bus_create_file来创建属性文件,
  5. 对pci_bus_type,就是为pci_bus_attrs中的属性创建文件(rescan文件) */
  6.     retval = bus_add_attrs(bus);
  7.     if (retval)
  8.         goto bus_attrs_fail;

  9.     pr_debug("bus: '%s': registered\n", bus->name);
  10.     return 0;

  11. bus_attrs_fail:
  12.     remove_probe_files(bus);
  13. bus_probe_files_fail:
  14.     kset_unregister(bus->p->drivers_kset);
  15. bus_drivers_fail:
  16.     kset_unregister(bus->p->devices_kset);
  17. bus_devices_fail:
  18.     bus_remove_file(bus, &bus_attr_uevent);
  19. bus_uevent_fail:
  20.     kset_unregister(&bus->p->subsys);
  21.     kfree(bus->p);
  22. out:
  23.     bus->= NULL;
  24.     return retval;
  25. }
第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()来初始化。
  1. int __init buses_init(void)
  2. {
  3.     bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
  4.     if (!bus_kset)
  5.         return -ENOMEM;
  6.     return 0;
  7. }
第3行调用kset_create_and_add来注册并创建/sys/bus目录,因此bus_kset是bus目录下其他目录对应的kobject的父亲。
至于此函数是何时由何人调用,目前我不知道。

4:bus_ktype变量

  1. static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
  2.              char *buf)
  3. {
  4.     struct bus_attribute *bus_attr = to_bus_attr(attr);
  5.     struct bus_type_private *bus_priv = to_bus(kobj);
  6.     ssize_t ret = 0;

  7.     if (bus_attr->show)
  8.         ret = bus_attr->show(bus_priv->bus, buf);
  9.     return ret;
  10. }

  11. static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
  12.              const char *buf, size_t count)
  13. {
  14.     struct bus_attribute *bus_attr = to_bus_attr(attr);
  15.     struct bus_type_private *bus_priv = to_bus(kobj);
  16.     ssize_t ret = 0;

  17.     if (bus_attr->store)
  18.         ret = bus_attr->store(bus_priv->bus, buf, count);
  19.     return ret;
  20. }

  21. static struct sysfs_ops bus_sysfs_ops = {
  22.     .show    = bus_attr_show,
  23.     .store    = bus_attr_store,
  24. };

  25. static struct kobj_type bus_ktype = {
  26.     .sysfs_ops    = &bus_sysfs_ops,
  27. };
这样当对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) |
给主人留下些什么吧!~~