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 }
下面着重分析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 }
- 确定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; }
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);
首先进入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; }
绑定设备和驱动的第一步就是分别在各自目录下创建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上去。