platform_device_register

  调用platform_device_register和platform_driver_register分别注册platform_device和platform_driver,两者的名字一样(xxx_device),名字不一样调用device_attach会匹配不上,也就不会执行probe函数,在/sys目录下的文件顺序如下:

  /sys/devices/platform/xxx_device/

    uevent subsystem power driver modalias(bus->dev_attrs) XXX(class->dev_attrs) XXX(device_type->groups) XXX(dev->groups)

     /sys/bus/platform/drivers

    xxx_device

      xxx_device(link)  bind unbind uevent module(link)

  /sys/bus/platform/devices

      xxx_device(link)

  这些文件以及它们的链接关系都是在这两个函数执行过程中生成的,下面分析一下device_add函数:

  1 /*
  2     1.初始化设备私有成员和设备名字,设置父kobj并在/sys下创建设备目录;
  3     2.在设备目录下创建uevent文件,添加device自身的属性文件,如果是类设备还要添加
  4     类属性文件,将设备挂载到总线上,添加总线属性文件并创建link文件和总线互相链接,
  5     为电源管理创建power目录和相关文件;
  6     3.将设备和对应的驱动互相绑定,执行probe
  7     4.发送kobject_uevent事件给应用,创建设备文件
  8     5.将设备挂载到总线上,挂到父设备上,挂到类上
  9 */
 10 int device_add(struct device *dev)
 11 {
 12     struct device *parent = NULL;
 13     struct class_interface *class_intf;
 14     int error = -EINVAL;
 15 
 16     /* 添加设备,该设备引用计数加1 */
 17     dev = get_device(dev);
 18     if (!dev)
 19         goto done;
 20 
 21     /* 如果device的私有成员为空,则初始化它 */
 22     if (!dev->p) {
 23         error = device_private_init(dev);
 24         if (error)
 25             goto done;
 26     }
 27 
 28     /*
 29      * for statically allocated devices, which should all be converted
 30      * some day, we need to initialize the name. We prevent reading back
 31      * the name, and force the use of dev_name()
 32      */
 33     /* 如果设备的初始名字不为空,则以它为准 ,设置为kobj->name */
 34     if (dev->init_name) {
 35         dev_set_name(dev, "%s", dev->init_name);
 36         dev->init_name = NULL;
 37     }
 38     
 39     if (!dev_name(dev)) {
 40         error = -EINVAL;
 41         goto name_error;
 42     }
 43 
 44     pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
 45 
 46     parent = get_device(dev->parent);
 47     /* 设置dev->kobj的parent */
 48     setup_parent(dev, parent);
 49 
 50     /* use parent numa_node */
 51     if (parent)
 52         set_dev_node(dev, dev_to_node(parent));
 53 
 54     /* first, register with generic layer. */
 55     /* we require the name to be set before, and pass NULL */
 56     /* setup_parent确定dev的父kobj,在这里创建device目录到父目录下*/
 57     error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
 58     if (error)
 59         goto Error;
 60 
 61     /* notify platform of device entry */
 62     if (platform_notify)
 63         platform_notify(dev);
 64 
 65     /*
 66         在device目录下创建uevent文件,如果注册的是platform_device,那么parent是
 67         /sys/bus/platform/devices,那么device目录就是/sys/bus/platform/devices/XXX
 68     */
 69     error = device_create_file(dev, &uevent_attr);
 70     if (error)
 71         goto attrError;
 72 
 73     /*SOLVE_ME devt*/
 74     if (MAJOR(dev->devt)) 
 75     {        
 76         /* SOLVE_ME :在device目录下创建dev文件,比如 /sys/bus/platform/devices/XXX/dev */
 77         error = device_create_file(dev, &devt_attr);
 78         if (error)
 79             goto ueventattrError;
 80 
 81         /* 创建link文件,链接到device目录 */
 82         error = device_create_sys_dev_entry(dev);
 83         if (error)
 84             goto devtattrError;
 85         
 86         /* 创建设备文件 SOLVE_ME */
 87         devtmpfs_create_node(dev);
 88     }
 89 
 90     error = device_add_class_symlinks(dev);
 91     if (error)
 92         goto SymlinkError;
 93 
 94     /* 添加device自身的一些设备属性文件 */
 95     error = device_add_attrs(dev);
 96     if (error)
 97         goto AttrsError;
 98 
 99     /* 设备挂到总线上,使设备和总线互相链接 */
100     error = bus_add_device(dev);
101     if (error)
102         goto BusError;
103     
104     /* 在devices目录下创建power目录,并在其下创建属性文件 */
105     error = dpm_sysfs_add(dev);
106     if (error)
107         goto DPMError;
108     device_pm_add(dev);
109 
110     /* Notify clients of device addition.  This call must come
111      * after dpm_sysf_add() and before kobject_uevent().
112      * SOLVE_ME 
113      */
114     if (dev->bus)
115         blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
116                          BUS_NOTIFY_ADD_DEVICE, dev);
117 
118     kobject_uevent(&dev->kobj, KOBJ_ADD);
119 
120     /* 探测,寻找设备对应的驱动并互相链接,若找到执行probe */
121     bus_probe_device(dev);
122 
123     /* 挂到父设备的链表上,父设备不一定存在,但是一定能分配的父kobj */
124     if (parent)
125         klist_add_tail(&dev->p->knode_parent,
126                    &parent->p->klist_children);
127 
128     if (dev->class) 
129     {
130         mutex_lock(&dev->class->p->class_mutex);
131         /* tie the class to the device */
132         klist_add_tail(&dev->knode_class,
133                    &dev->class->p->class_devices);
134 
135         /* notify any interfaces that the device is here */
136         list_for_each_entry(class_intf,
137                     &dev->class->p->class_interfaces, node)
138             if (class_intf->add_dev)
139                 class_intf->add_dev(dev, class_intf);
140         mutex_unlock(&dev->class->p->class_mutex);
141     }
142 done:
143     put_device(dev);
144     return error;
145  DPMError:
146     bus_remove_device(dev);
147  BusError:
148     device_remove_attrs(dev);
149  AttrsError:
150     device_remove_class_symlinks(dev);
151  SymlinkError:
152     if (MAJOR(dev->devt))
153         devtmpfs_delete_node(dev);
154     if (MAJOR(dev->devt))
155         device_remove_sys_dev_entry(dev);
156  devtattrError:
157     if (MAJOR(dev->devt))
158         device_remove_file(dev, &devt_attr);
159  ueventattrError:
160     device_remove_file(dev, &uevent_attr);
161  attrError:
162     kobject_uevent(&dev->kobj, KOBJ_REMOVE);
163     kobject_del(&dev->kobj);
164  Error:
165     cleanup_device_parent(dev);
166     if (parent)
167         put_device(parent);
168 name_error:
169     kfree(dev->p);
170     dev->p = NULL;
171     goto done;
172 }
View Code

 

  下面着重分析setup_parent函数,由这个函数来决定设备的parent kobj,在kobject_add中传进去,使得设备/sys目录创建在其下,parent kobj不一定就是parent device的kobj。

  setup_parent->get_device_parent

  

 1 /*
 2     -------------------
 3     该函数的作用是返回dev的父kobj
 4     
 5     该设备是个类设备
 6         1.如果没有父亲,就将/sys/devices/virtual下创建中间目录,放到该中间目录下
 7         2.父亲也是类设备,则就放在父目录下
 8         3.父设备不是类设备,则创建一层中间目录,放到该中间目录下
 9         
10     该设备不是类设备
11         1.直接放到其父目录下
12     
13 */
14 static struct kobject *get_device_parent(struct device *dev,
15                      struct device *parent)
16 {
17     if (dev->class)
18     {
19         static DEFINE_MUTEX(gdp_mutex);
20         struct kobject *kobj = NULL;
21         struct kobject *parent_kobj;
22         struct kobject *k;
23 
24 #ifdef CONFIG_BLOCK
25         /* block disks show up in /sys/block */
26         if (sysfs_deprecated && dev->class == &block_class) {
27             if (parent && parent->class == &block_class)
28                 return &parent->kobj;
29             return &block_class.p->class_subsys.kobj;
30         }
31 #endif
32 
33         /*
34          * If we have no parent, we live in "virtual".
35          * Class-devices with a non class-device as parent, live
36          * in a "glue" directory to prevent namespace collisions.
37          */
38         if (parent == NULL)
39         {
40             /* class,no parent -> class dir
41                此时parent_kobj is /sys/devices/virtual dir kobj
42             */
43             parent_kobj = virtual_device_parent(dev);
44         }
45         /* SOLVE_ME:ns_type*/
46         else if (parent->class && !dev->class->ns_type)
47         {            
48             /* class,parent,parent->class -> parent dir*/
49             return &parent->kobj;
50         }
51         else
52         {
53             /* class,parent,no parent->class -> class dir */
54             parent_kobj = &parent->kobj;
55         }
56         mutex_lock(&gdp_mutex);
57 
58         /* find our class-directory at the parent and reference it
59            SOLVE_ME :class_dirs主要是类目录,该目录主要是为了隔离非类设备的父目录
60            和为类设备的子设备的目录
61         */
62         spin_lock(&dev->class->p->class_dirs.list_lock);
63         list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)
64             if (k->parent == parent_kobj) 
65             {
66                 kobj = kobject_get(k);
67                 break;
68             }
69         spin_unlock(&dev->class->p->class_dirs.list_lock);
70         if (kobj) 
71         {
72             mutex_unlock(&gdp_mutex);
73             return kobj;
74         }
75 
76         /* or create a new class-directory at the parent device
77            在parent目录和device目录之间创建一层class目录,device目录就放到该class目录下
78         */
79         k = class_dir_create_and_add(dev->class, parent_kobj);
80         /* do not emit an uevent for this simple "glue" directory */
81         mutex_unlock(&gdp_mutex);
82         return k;
83     }
84 
85     /* no class -> parent dir */
86     if (parent)
87         return &parent->kobj;
88     
89     return NULL;
90 }
View Code

 

  • 确定parent kobj以确定将device目录创建到/sys下的哪个目录下

 1.  如果该设备是类设备

      1.如果不存在父设备,那么该设备属于“虚拟”设备:调用virtual_device_parent在全局变量devices_kset(/sys/devices)下面创建一个virtual目录,parent kobj  为/sys/devices/virtual所代表的kobj,设备有可能放到该目录下;

       2.如果父设备存在且也是类设备:那么就直接返回父设备的Kobj,创建的父目录下;

     3.如果父设备存在却不是类设备,相当于一个为类设备的子设备拥有一个不是类设备的父亲,我们将这样的设备所在的目录称为“胶水”目录,因其存在冲突,需要隔离,即父设备和子设备中间要隔一层类目录,取这层类目录的Kobj为parent kobj;

      上述中只有2已经确定了parent kobj,可以说已经决定了设备目录的存放位置,1,3两种只是取得了parent kobj,接下来就要创建那层用来隔离的类目录。首先遍历设备的类目录(dev->class->p->class_dirs),如果有类目录的parent kobj就是前面获得的parent kobj,那么就不用创建类目录了,直接将该目录kobj作为parent kobj返回,也就是说设备目录将创建在其下;如果在设备的类目录中没有找到合适的类目录,那么就调用class_dir_create_and_add在parent kobj目录下创建一个类目录,目录名字为设备的类的名字(dev->class->name),将该类目录kobj作为parent kobj返回。

 2.  如果该设备不是类设备

  2.1 该设备有父设备

    以父设备的kobj作为父kobj(dev->kobj->parent)

    2.2 该设备没有父设备

          那么此种情况下setup_parent返回NULL,没有为其确定父Kobj,就用dev->kobj所处容器kset的基类作为父kobj,并把dev->kobj加入到该容器中,一般dev->kobj都处于一个容器中,因为device_initialize已经把该kobj的容器设置为devices_kset。

      在确定了dev->kobj的parent kobj之后,就可以把设备创建到其目录下,在这里我们以注册platform_device为例,那么父设备就是platform_bus,父目录就是/sys/devices/platform,相应的platform_device就创建为目录/sys/devices/platform/xxx_device,即在代码中看到dev->kobj,我们就要知道代表的是那个目录。

  • 在device目录下创建相关文件(往往是为了生成/dev下的设备文件)

      device_create_file(dev, &uevent_attr); 在device目录下创建uevent文件;

      如果设备含有设备号,就会执行以下函数,在平时写的驱动中,要创建一个设备文件的流程往往是先alloc_chrdev_region注册设备号,然后调用class_create创建类,调用device_create->device_add创建设备,此时创建的设备是一个类设备,并且含有设备号,所以此文其他地方讨论的设备是platform_device->dev,而这里的设备应该是为了创建设备文件而调用device_create所创建的类设备:

      device_create_file(dev, &devt_attr); 在device目录下创建dev属性文件,通过cat该dev文件可以获取设备号;

                device_create_sys_dev_entry(dev);创建link文件链接到该设备目录,该link文件的名字是“主设备号:次设备号”,如果该设备class->dev_kobj不为空,就把link文件创建到该目录下,否则创建到/sys/dev/char目录下;

                devtmpfs_create_node(dev);在/dev目录下创建设备文件;

     device_add_class_symlinks(dev);如果该设备是类设备,此函数就会起作用,就创建一些文件;

     device_add_attrs(dev);如果该设备是类设备,就在device目录下创建类的设备属性文件(class->dev_attrs),如果该设备的device_type成员不为空,就创建设备类型的属性组文件(device_type->groups),如果该设备自身含有属性组(dev->groups),就创建;

  dpm_sysfs_add(dev);在设备目录下创建power目录,并在其下创建相关属性文件,主要用于电源管理;

  •       调用bus_add_device将设备挂载到总线上

  该函数的内容可以总结为“ 总线的属性文件,总线上的每个设备都要创建;设备要能链接到总线,总线也要能链接到设备”。

/*
    总体而言,可以概括为:
    总线的属性文件,总线上的每个设备都要创建
    设备要能链接到总线,总线也要能链接到设备
*/
int bus_add_device(struct device *dev)
{
    struct bus_type *bus = bus_get(dev->bus);
    int error = 0;

    if (bus) 
    {
        pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
        /* 添加bus->dev_attrs指定的属性文件到/sys/devices/platform/XXX_devices目录 */
        error = device_add_attrs(bus, dev);
        if (error)
            goto out_put;

        /* /sys/bus/xxx_bus/devices/目录下创建link文件,
          链接到/sys/devices/platform/XXX_devices目录
        */
        error = sysfs_create_link(&bus->p->devices_kset->kobj,
                        &dev->kobj, dev_name(dev));
        if (error)
            goto out_id;
        
        /*
            /sys/devices/platform/XXX_devices目录下创建subsystem文件,链接到
            /sys/bus/XXX_bus(platform)目录
        */
        error = sysfs_create_link(&dev->kobj,
                &dev->bus->p->subsys.kobj, "subsystem");
        if (error)
            goto out_subsys;

        /* 把设备挂到总线设备链表上去 */
        klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
    }
    return 0;

out_subsys:
    sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
out_id:
    device_remove_attrs(bus, dev);
out_put:
    bus_put(dev->bus);
    return error;
}
View Code

  device_add_attrs把总线的设备属性文件(bus->dev_attrs)创建到设备目录下,比如是总线是platform_bus_type,那么其dev_attrs就是modalias文件,也就是说总线有的属性,该总线上的所有设备也必须有;

  接下来创建了两个link:

    一个让总线链接到设备,在总线的设备目录下/sys/bus/platform/devices/(bus->p->devices_kset->kobj)创建xxx_device链接文件,链接到设备目录/sys/device/platform/xxx_device;

    一个让设备链接到总线,在设备目录下创建了一个名为subsystem的link文件,链接到总线目录/sys/bus/platform(dev->bus->p->subsys.kobj),在前面有调用函数device_add_class_symlinks,如果该设备是类设备,也会创建一个subsystem,链接到对应的类目录,所以一个设备如果即是类设备又是总线设备,那么就会出错,因为先创建的subsystem用来链接类目录,接下来再创建它想要链接到总线目录内核就会报错

  sysfs: cannot create duplicate filename '/devices/platform/module_test/module_test/subsystem'

  所以一个设备不能同时是类设备又是总线设备,只能class_create之后再device_create创建一个设备作为区别。

  /sys/device/virtual/misc/network_throughput,其中/sys/device/virtual/是parent kobj,network_throughput就是一个无父设备的类设备,所在的目录就是“胶水”目录,可知“胶水”设备有可能是那些为了创建设备节点而创建的设备,中间隔了一层类目录misc,network_throughput的subsystem就是链接到/sys/class/misc目录,也就是链接到类目录,类目录是通过class_create创建的。

      接下来把设备作为一个节点(knode_bus),挂载到总线的设备链表上去(klist_devices)。

  • 在总线上寻找与之匹配的driver

  1. 调用bus_probe_device->device_attach,如果设备已经有了对应的驱动,就调用device_bind_driver将设备和驱动绑定,

  

int device_bind_driver(struct device *dev)
{
    int ret;

    /* 分别在设备目录和驱动目录下创建链接文件,链接对方 */
    ret = driver_sysfs_add(dev);
    
    /* 把设备挂载到驱动链表上去 */
    if (!ret)
        driver_bound(dev);
    return ret;
}
EXPORT_SYMBOL_GPL(device_bind_driver);
View Code

  首先进入driver_sysfs_add

/* 分别在设备目录和驱动目录下创建链接文件,链接对方 */
static int driver_sysfs_add(struct device *dev)
{
    int ret;

    if (dev->bus)
        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                         BUS_NOTIFY_BIND_DRIVER, dev);
    
    /* 在驱动目录下创建link链接到设备目录,比如
       /sys/bus/platform/drivers/XXX_device -> /sys/devices/platform/XXX_device
    */
    ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
              kobject_name(&dev->kobj));
    if (ret == 0) 
    {    /*
            在设备目录下创建link文件链接到驱动目录
        */
        ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
                    "driver");
        if (ret)
            sysfs_remove_link(&dev->driver->p->kobj,
                    kobject_name(&dev->kobj));
    }
    return ret;
}
View Code

  绑定设备和驱动的第一步就是分别在各自目录下创建Link文件链接对方,在驱动目录下创建名为xxx_device的链接文件链接到设备目录(驱动目录与设备目录不同,一般说来,平台设备目录是处于/sys/devices/platform下,在/sys/bus/platform/devices/下的目录只是一个Link文件,链接到前者,而驱动目录就是处于/sys/bus/platform/drivers下,不是一个链接);在设备目录下创建名为"driver"的link文件链接到驱动目录。

  driver_bound的主要作用是把设备作为节点(knode_driver)挂载到驱动链表(klist_devices)上去,因为一个驱动可以对应多个设备。

  2.如果设备还未有对应的驱动,那么调用bus_for_each_drv在总线上查找已挂载上去的驱动,通过while循环遍历总线的驱动挂载链表klist_driver,正如在注册总线设备的时候,会挂载设备到klist_devices一样,注册总线驱动的时候,也会挂载驱动到klist_drivers;每遍历一个驱动就调用__device_attach去匹配,匹配成功执行driver_probe_device去探测,driver_probe_device执行成功仍旧返回1,跳出while,匹配不成功或者匹配成功但是探测失败返回0。

  driver_probe_device中所做的部分内容是绑定驱动,因为进入该函数之前,设备已经匹配到合适的驱动,接下来就是绑定驱动,和上面第一步做的差不多,只不过多执行了probe函数,优先执行总线(bus_type)的probe函数,如果它为空,则执行绑定驱动(device_driver)的probe。对于平台总线而已,平台总线(platform_bus_type)并没有定义probe函数,那么注册platform_driver的时候就要定义probe函数,可能是借由驱动来探测设备之意,所以是执行驱动的probe函数。

  仍旧回到device_add,如果父设备存在,就将子设备的knode_parent节点挂载到父设备的klist_children上去。如果该类是类设备,就把子设备的knode_class挂载到class_devices上去。

  

 

     

    

 

posted @ 2013-05-24 14:17  IrisZhou  阅读(2786)  评论(0编辑  收藏  举报