Chinaunix首页 | 论坛 | 博客
  • 博客访问: 145310
  • 博文数量: 19
  • 博客积分: 1746
  • 博客等级: 上尉
  • 技术积分: 443
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-22 22:54
文章分类
文章存档

2011年(3)

2010年(16)

分类: LINUX

2010-03-04 22:10:11

------------------------------------------------
#纯属个人理解,如有问题敬请谅解!
#kernel version: 2.6.26
#Author: andy wang
-------------------------------------------------
linux设备模型中,所有注册在内核中的总线都可以在/sys/bus 目录下找到 .:12c ,pci ,usb,platform总线等.
 
初始化总线
在分析注册总线之前, 先来看看/sys下目录bus是如何建立起来的.
linux初始化的阶段会调用buses_init()初始化bus总线.
先来看看这段代码:
985 int __init buses_init(void)
986 {
987         bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
988         if (!bus_kset)
989                 return -ENOMEM;
990         return 0;
991 }
调用kset_create_and_add()注册一个ket ,这个函数在上篇文章已分析过了, 因为传入parent_kobj参数为NULL,所以会在/sys顶层目录建立一个”bus”目录. 最后返回bus_ket .
 
注册总线
在分析注册总线前,先看看两个结构:
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 (*suspend_late)(struct device *dev, pm_message_t state);
         int (*resume_early)(struct device *dev);
         int (*resume)(struct device *dev);
 
         struct bus_type_private *p;
};
 
 
图1-9是在注册serio总线后的sys目录结构图.
我们就根据这个图来分析总线的注册过程,假设注册的是serio总线.
先列出软件流程:
bus_register()->kset_register()->bus_create_file()->kset_create_and_add()->kset_create_and_add()->add_probe_files()->bus_add_attrs()
对照上面的图片然后看软件流程就会很清晰了.
867 int bus_register(struct bus_type *bus)
868 {
869         int retval;
870         struct bus_type_private *priv;
871
872         priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
873         if (!priv)
874                 return -ENOMEM;
875
876         priv->bus = bus;
877         bus->p = priv;
878
879         BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
880
881         retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
882         if (retval)
883                 goto out;
884
885         priv->subsys.kobj.kset = bus_kset;
886         priv->subsys.kobj.ktype = &bus_ktype;
887         priv->drivers_autoprobe = 1;
888
889         retval = kset_register(&priv->subsys);
890         if (retval)
891                 goto out;
892
893         retval = bus_create_file(bus, &bus_attr_uevent);
894         if (retval)
895                 goto bus_uevent_fail;
896
897         priv->devices_kset = kset_create_and_add("devices", NULL,
898                                                  &priv->subsys.kobj);
899         if (!priv->devices_kset) {
900                 retval = -ENOMEM;
901                 goto bus_devices_fail;
902         }
903
904         priv->drivers_kset = kset_create_and_add("drivers", NULL,
905                                                  &priv->subsys.kobj);
906         if (!priv->drivers_kset) {
907                 retval = -ENOMEM;
908                 goto bus_drivers_fail;
909         }
910
911         klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
912         klist_init(&priv->klist_drivers, NULL, NULL);
913
914         retval = add_probe_files(bus);
915         if (retval)
916                 goto bus_probe_files_fail;
917
918         retval = bus_add_attrs(bus);
919         if (retval)
920                 goto bus_attrs_fail;
921
922         pr_debug("bus: '%s': registered\n", bus->name);
923         return 0;
 
                     …………………..
}
我们来逐一分析每行代码.
第872行代码, 在内存中分配一个bus_type_private结构, 也就是我们上面图中的结构体 ,然后与bus_type关联.
第879行, 初始化总线通知链bus_notifier.
第881行, 初始化priv->subsys.kobj 这个kobj名字, 在图1-9就是”serio”
第885-886行, 初始化”serio” kobj的kset和ktype,  这里的ket= bus_kset ,那么我们的serio目录将会建立在/sys/bus目录下 ,ktype= bus_ktype这个很重要了,因为我们在serio目录下面建立的三个属性文件uevent,drivers_prob,drivers_autoprobe的读写方法都定义在bus_ktype中了.
第887行, priv->drivers_autoprobe = 1;那么在这个serio注册了设备或者驱动时都会自动匹配设备或者驱动 ,这个值可以通过写文件drivers_autoprobe来修改它.
第889行,向内核注册一个ket, 注册完后就会在/sys/bus目录下建立一个”serio”目录.
接下来893行代码bus_create_file()就是在serio目录下建立一个 名为uevent的属性文件,在后面分析它的读写操作.
897-990行代码, 以serio kobj为父对象注册一个名为” devices’的ket和名为” drivers”的ket , 也就是在serio目录下建立devices目录和drivers目录,如图1-9.
913-917行代码就是在serio目录下建立属性文件drivers_probe和drivers_autoprobe
 
读写总线属性文件
几乎 Linux 设备模型中的每一层都提供添加属性的函数,总线层也是一样的
这里就拿uevent属性文件来分析一下.先找到uevent总线属性文件的定义:
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
#define BUS_ATTR(_name, _mode, _show, _store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
这里就是创建一个 bus_attr_uevent的全局结构 ,定义其名字,模式,操作方法,,,显然这里只定义了store方法.
 
那么如何来读写它呢?
在以前已经介绍读写sysfs下面的属性文件,就是要找到其父目录kobj关联的ktype ,在ktype定义了属性文件的操作方法.
在bus_register() 中有这么一段代码priv->subsys.kobj.ktype = &bus_ktype;
找到bus_ktype的定义:
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,
};
我们已经找到了写属性uevent文件调用的函数为bus_attr_store(),继续跟踪它的代码:
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);   //由attr找到bus_attr
         struct bus_type_private *bus_priv = to_bus(kobj);  //由kobj找到bus_priv
         ssize_t ret = 0;
 
         if (bus_attr->store)
                   ret = bus_attr->store(bus_priv->bus, buf, count);
         return ret;
}
这段代码bus_attr->store(bus_priv->bus, buf, count);就是调用定义在bus_attr中的store方法.
在上面看到总线属性文件uevent的store方法定义是bus_uevent_store()
看看代码是如何实现的:
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 /sys/bus/serio/uevent 这个文件,最终调用的就是这个函数了. 看看它做了啥子操作.
首先是解析写进来的字符串,如add,,,将会被解析成KOBJ_ADD动作 ,然后调用函数kobject_uevent()
向用户空间的udev发送相关的消息.
 
阅读(3129) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~