Linux设备驱动(6)模块之间关系详解
本文将对Linux系统中的sysfs进行简单的分析,要分析sysfs就必须分析内核的driver-model(驱动模型),两者是紧密联系的。在分析过程中,本文将以platform总线和spi主控制器的platform驱动为例来进行讲解。其实,platform机制是基于driver-model的,通过本文,也会对platform机制有个简单的了解。
1. What is sysfs?
个人理解:sysfs向用户空间展示了驱动设备的层次结构,即目录sys下的文件结构。我们都知道设备和对应的驱动都是由内核管理的,这些对于用户空间是不可见的。现在通过sysfs,可以在用户空间直观的了解设备驱动的层次结构。
我们来看看sysfs的文件结构:
[root@yj423 /sys]#ls block class devices fs module bus dev firmware kernel power
block:块设备
bus:系统中的总线
class: 设备类型,比如输入设备
dev:系统中已注册的设备节点的视图,有两个子目录char和block。
devices:系统中所有设备拓扑结构视图
fireware:固件
fs:文件系统
kernel:内核配置选项和状态信息
module:模块
power:系统的电源管理数据
2. kobject ,kset和ktype
要分析sysfs,首先就要分析kobject和kset,因为驱动设备的层次结构的构成就是由这两个来完成的。
2.1 kobject
kobject是一个对象的抽象,它用于管理对象。每个kobject对应着sysfs中的一个目录。
kobject用struct kobject来描述。
1 struct kobject { 2 const char *name; /*在sysfs建立目录的名字*/ 3 struct list_head entry; /*用于连接到所属kset的链表中*/ 4 struct kobject *parent; /*父对象*/ 5 struct kset *kset; /*属于哪个kset*/ 6 struct kobj_type *ktype; /*类型*/ 7 struct sysfs_dirent *sd; /*sysfs中与该对象对应的文件节点*/ 8 struct kref kref; /*对象的应用计数*/ 9 unsigned int state_initialized:1; 10 unsigned int state_in_sysfs:1; 11 unsigned int state_add_uevent_sent:1; 12 unsigned int state_remove_uevent_sent:1; 13 unsigned int uevent_suppress:1; 14 };
2.2 kset
kset是一些kobject的集合,这些kobject可以有相同的ktype,也可以不同。同时,kset自己也包含一个kobject。在sysfs中,kset也是对应这一个目录,但是目录下面包含着其他的kojbect。
kset使用struct kset来描述。
1 /** 2 * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. 3 * 4 * A kset defines a group of kobjects. They can be individually 5 * different "types" but overall these kobjects all want to be grouped 6 * together and operated on in the same manner. ksets are used to 7 * define the attribute callbacks and other common events that happen to 8 * a kobject. 9 * 10 * @list: the list of all kobjects for this kset 11 * @list_lock: a lock for iterating over the kobjects 12 * @kobj: the embedded kobject for this kset (recursion, isn't it fun...) 13 * @uevent_ops: the set of uevent operations for this kset. These are 14 * called whenever a kobject has something happen to it so that the kset 15 * can add new environment variables, or filter out the uevents if so 16 * desired. 17 */ 18 struct kset { 19 struct list_head list; 20 spinlock_t list_lock; 21 struct kobject kobj; 22 const struct kset_uevent_ops *uevent_ops; 23 };
2.3 ktype
每个kobject对象都内嵌有一个ktype,该结构定义了kobject在创建和删除时所采取的行为。
1 struct kobj_type { 2 void (*release)(struct kobject *kobj); 3 const struct sysfs_ops *sysfs_ops; 4 struct attribute **default_attrs; 5 const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); 6 const void *(*namespace)(struct kobject *kobj); 7 }; 8 9 /*以下两个结构体定义位于include\linux\sysfs.h*/ 10 struct sysfs_ops { 11 ssize_t (*show)(struct kobject *, struct attribute *,char *); 12 ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); 13 const void *(*namespace)(struct kobject *, const struct attribute *); 14 }; 15 16 struct attribute { 17 const char *name; 18 umode_t mode; 19 #ifdef CONFIG_DEBUG_LOCK_ALLOC 20 bool ignore_lockdep:1; 21 struct lock_class_key *key; 22 struct lock_class_key skey; 23 #endif
当kobject的引用计数为0时,通过release方法来释放相关的资源。
attribute为属性,每个属性在sysfs中都有对应的属性文件。
sysfs_op的两个方法用于实现读取和写入属性文件时应该采取的行为。
2.4 kobject与kset的关系
下面这张图非常经典。最下面的kobj都属于一个kset,同时这些kobj的父对象就是kset内嵌的kobj。通过链表,kset可以获取所有属于它的kobj。
从sysfs角度而言,kset代表一个文件夹,而下面的kobj就是这个文件夹里面的内容,而内容有可能是文件也有可能是文件夹。
3.举例
在上一节中,我们知道sys下有一个bus目录,这一将分析如何通过kobject创建bus目录,以bus的创建过程分析kobject和kset。
下面代码位于drivers/base/bus.c。
3.1从函数buses_init()开始分析,直接调用kset_create_and_add,第一个参数为要创建的目录的名字,而第三个参数为空表示没有父对象,因为bus在sys目录下,没有父对象。
bus是一个kset,其下面有很多kobject。
1 int __init buses_init(void) 2 {/*这里直接调用kset_create_and_add,第一个参数为要创建的目录的名字,而第三个参数表示没有父对象。*/ 3 bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL); 4 if (!bus_kset) 5 return -ENOMEM; 6 /*这个函数中,动态分配了kset结构,调用kobject_set_name设置kset->kobj->name为bus,也就是我们要创建的目录bus。同时这里kset->kobj.parent为NULL*/ 7 system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);//也就是没有父对象。因为要创建的bus目录是在sysfs所在的根目录创建的,自然没有父对象。 8 if (!system_kset) 9 return -ENOMEM; 10 11 return 0; 12 } 13 14 static const struct kset_uevent_ops bus_uevent_ops = { 15 .filter = bus_uevent_filter, 16 }; 17 18 static int bus_uevent_filter(struct kset *kset, struct kobject *kobj) 19 { 20 struct kobj_type *ktype = get_ktype(kobj); 21 22 if (ktype == &bus_ktype) 23 return 1; 24 return 0; 25 }
1 /** 2 * kset_create_and_add - create a struct kset dynamically and add it to sysfs 3 * 4 * @name: the name for the kset 5 * @uevent_ops: a struct kset_uevent_ops for the kset 6 * @parent_kobj: the parent kobject of this kset, if any. 7 * 8 * This function creates a kset structure dynamically and registers it 9 * with sysfs. When you are finished with this structure, call 10 * kset_unregister() and the structure will be dynamically freed when it 11 * is no longer being used. 12 * 13 * If the kset was not able to be created, NULL will be returned. 14 */ 15 struct kset *kset_create_and_add(const char *name, 16 struct kset_uevent_ops *uevent_ops, 17 struct kobject *parent_kobj) 18 { 19 struct kset *kset; 20 int error; 21 22 kset = kset_create(name, uevent_ops, parent_kobj); /*建立kset,设置某些字段*/ 23 if (!kset) 24 return NULL; 25 error = kset_register(kset); /*添加kset到sysfs*/ 26 if (error) { 27 kfree(kset); 28 return NULL; 29 } 30 return kset; 31 }
接着函数kset_create中,动态分配了kset结构,初始化kset结构体成员部分变量。
1 /** 2 * kset_create - create a struct kset dynamically 3 * 4 * @name: the name for the kset 5 * @uevent_ops: a struct kset_uevent_ops for the kset 6 * @parent_kobj: the parent kobject of this kset, if any. 7 * 8 * This function creates a kset structure dynamically. This structure can 9 * then be registered with the system and show up in sysfs with a call to 10 * kset_register(). When you are finished with this structure, if 11 * kset_register() has been called, call kset_unregister() and the 12 * structure will be dynamically freed when it is no longer being used. 13 * 14 * If the kset was not able to be created, NULL will be returned. 15 */ 16 static struct kset *kset_create(const char *name, 17 struct kset_uevent_ops *uevent_ops, 18 struct kobject *parent_kobj) 19 { 20 struct kset *kset; 21 22 kset = kzalloc(sizeof(*kset), GFP_KERNEL);/*分配kset*/ 23 if (!kset) 24 return NULL; 25 kobject_set_name(&kset->kobj, name);/*设置kobj->name*/ 26 kset->uevent_ops = uevent_ops; 27 kset->kobj.parent = parent_kobj; /*设置父对象*/ 28 29 /* 30 * The kobject of this kset will have a type of kset_ktype and belong to 31 * no kset itself. That way we can properly free it when it is 32 * finished being used. 33 */ 34 kset->kobj.ktype = &kset_ktype; 35 kset->kobj.kset = NULL; /*本keset不属于任何kset*/ 36 37 return kset; 38 }
接着调用kobject_set_name 采用标准化格式设置kset->kobj->name为bus,也就是我们要创建的目录bus。同时这里kset->kobj.parent为NULL,也就是没有父对象。因为要创建的bus目录是在sysfs所在的根目录sys创建的,自然没有父对象。
/** * kobject_set_name - Set the name of a kobject * @kobj: struct kobject to set the name of * @fmt: format string used to build the name * * This sets the name of the kobject. If you have already added the * kobject to the system, you must call kobject_rename() in order to * change the name of the kobject. */ int kobject_set_name(struct kobject *kobj, const char *fmt, ...) { va_list vargs; int retval; va_start(vargs, fmt); retval = kobject_set_name_vargs(kobj, fmt, vargs); va_end(vargs); return retval; } /** * kobject_set_name_vargs - Set the name of an kobject * @kobj: struct kobject to set the name of * @fmt: format string used to build the name * @vargs: vargs to format the string. */ int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, va_list vargs) { const char *old_name = kobj->name; char *s; if (kobj->name && !fmt) return 0; kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs); if (!kobj->name) return -ENOMEM; /* ewww... some of these buggers have '/' in the name ... */ while ((s = strchr(kobj->name, '/'))) s[0] = '!'; kfree(old_name); return 0; } /* Simplified asprintf. */ char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap) { unsigned int len; char *p; va_list aq; va_copy(aq, ap); len = vsnprintf(NULL, 0, fmt, aq); va_end(aq); p = kmalloc(len+1, gfp); if (!p) return NULL; vsnprintf(p, len+1, fmt, ap); return p; }
3.2kset_create_and_add函数中调用函数kset_create的流程分析完,然后紧接着调用函数kset_register
1 /** 2 * kset_register - initialize and add a kset. 3 * @k: kset. 4 */ 5 int kset_register(struct kset *k) 6 { 7 int err; 8 9 if (!k) 10 return -EINVAL; 11 12 kset_init(k); /*初始化kset*/ 13 err = kobject_add_internal(&k->kobj); /*在sysfs中建立目录*/ 14 if (err) 15 return err; 16 kobject_uevent(&k->kobj, KOBJ_ADD); 17 return 0; 18 }
函数kset_init接着初始化kset中的其他成员变量
1 /** 2 * kset_init - initialize a kset for use 3 * @k: kset 4 */ 5 void kset_init(struct kset *k) 6 { 7 kobject_init_internal(&k->kobj);/*初始化kobject的某些字段*/ 8 INIT_LIST_HEAD(&k->list); /*初始化链表头*/ 9 spin_lock_init(&k->list_lock); /*初始化自旋锁*/ 10 } 11 12 static void kobject_init_internal(struct kobject *kobj) 13 { 14 if (!kobj) 15 return; 16 kref_init(&kobj->kref); /*初始化引用基计数*/ 17 INIT_LIST_HEAD(&kobj->entry); /*初始化链表头*/ 18 kobj->state_in_sysfs = 0; 19 kobj->state_add_uevent_sent = 0; 20 kobj->state_remove_uevent_sent = 0; 21 kobj->state_initialized = 1; 22 }
函数kobject_add_internal,将kobj加入内核,在sysfs中将建立目录。
1 static int kobject_add_internal(struct kobject *kobj) 2 { 3 int error = 0; 4 struct kobject *parent; 5 6 if (!kobj) 7 return -ENOENT; 8 /*检查name字段是否存在*/ 9 if (!kobj->name || !kobj->name[0]) { 10 WARN(1, "kobject: (%p): attempted to be registered with empty " 11 "name!\n", kobj); 12 return -EINVAL; 13 } 14 15 parent = kobject_get(kobj->parent); /*有父对象则增加父对象引用计数*/ 16 17 /* join kset if set, use it as parent if we do not already have one */ 18 if (kobj->kset) { 19 if (!parent) 20 /*kobj属于某个kset,但是该kobj没有父对象,则以kset的kobj作为父对象*/ 21 parent = kobject_get(&kobj->kset->kobj); 22 kobj_kset_join(kobj); /*将kojbect添加到kset结构中的链表当中*/ 23 kobj->parent = parent; 24 } 25 26 pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n", 27 kobject_name(kobj), kobj, __func__, 28 parent ? kobject_name(parent) : "<NULL>", 29 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>"); 30 31 error = create_dir(kobj); /*根据kobj->name在sys中建立目录*/ 32 if (error) { 33 kobj_kset_leave(kobj); /*删除链表项*/ 34 kobject_put(parent); /*减少引用计数*/ 35 kobj->parent = NULL; 36 37 /* be noisy on error issues */ 38 if (error == -EEXIST) 39 printk(KERN_ERR "%s failed for %s with " 40 "-EEXIST, don't try to register things with " 41 "the same name in the same directory.\n", 42 __func__, kobject_name(kobj)); 43 else 44 printk(KERN_ERR "%s failed for %s (%d)\n", 45 __func__, kobject_name(kobj), error); 46 dump_stack(); 47 } else 48 kobj->state_in_sysfs = 1; 49 50 return error; 51 }
在上面的kset_create中有kset->kobj.kset = NULL,因此if (kobj->kset)条件不满足。因此在这个函数中,对name进行了必要的检查之后,调用了create_dir在sysfs中创建目录。
在create_dir执行完成以后会在sysfs的根目录(/sys/)建立文件夹bus。该函数的详细分析将在后面给出。
至此,对bus目录的建立有了简单而直观的了解。我们可以看出kset其实就是表示一个文件夹,而kset本身也含有一个kobject,而该kobject的name字段即为该目录的名字,本例中为bus。
4. 驱动模型回顾
前2节所介绍的是最底层,最核心的内容。下面开始将描述较为高层的内容。Linux设备模型使用了bus/device/device_driver三个数据结构分别来描述总线、设备和驱动。所有的设备和对应的驱动都必须挂载在某一个总线上,通过总线,可以绑定设备和驱动。这个属于分离的思想,将设备和驱动分开管理。同时驱动程序可以了解到所有它所支持的设备,同样的,设备也能知道它对应驱动程序。
下面分析bus/device/device_driver三者之间的关系,三个结构体不再重复。
4.1 bus
总线是处理器与一个设备或者多个设备之间的通道。在设备模型中,所有的设备都挂载在某一个总线上。总线使用struct bus_type来表述。结构体定义代码位于include/linux/device.h和driver/base/base.h。
1 /** 2 * struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure. 3 * 4 * @subsys - the struct kset that defines this subsystem 5 * @devices_kset - the subsystem's 'devices' directory 6 * @interfaces - list of subsystem interfaces associated 7 * @mutex - protect the devices, and interfaces lists. 8 * 9 * @drivers_kset - the list of drivers associated 10 * @klist_devices - the klist to iterate over the @devices_kset 11 * @klist_drivers - the klist to iterate over the @drivers_kset 12 * @bus_notifier - the bus notifier list for anything that cares about things 13 * on this bus. 14 * @bus - pointer back to the struct bus_type that this structure is associated 15 * with. 16 * 17 * @glue_dirs - "glue" directory to put in-between the parent device to 18 * avoid namespace conflicts 19 * @class - pointer back to the struct class that this structure is associated 20 * with. 21 * 22 * This structure is the one that is the actual kobject allowing struct 23 * bus_type/class to be statically allocated safely. Nothing outside of the 24 * driver core should ever touch these fields. 25 */ 26 struct subsys_private { 27 struct kset subsys; 28 struct kset *devices_kset;//负责连接它下面的所有device的目录 29 struct list_head interfaces; 30 struct mutex mutex; 31 32 struct kset *drivers_kset;//负责连接它下面的所有driver的目录 33 struct klist klist_devices;//负责连接它下面的所有device的链表,device->p->knode_bus的表头 34 struct klist klist_drivers;//负责连接它下面的所有driver的链表,driver->p->knode_bus的表头 35 struct blocking_notifier_head bus_notifier; 36 unsigned int drivers_autoprobe:1; 37 struct bus_type *bus; 38 39 struct kset glue_dirs; 40 struct class *class; 41 };
每个bus_type都包含一个kset对象subsys,该kset在/sys/bus/目录下有着对应的一个目录,目录名即为字段name。后面我们将看到platform总线的建立。
drivers_kset和devices_kset对应着两个目录,该两个目录下将包含该总线上的设备和相应的驱动程序。
总线有个私有数据subsys_private *p,只有该总线可以访问它,其他不可访问。同时总线上的设备和驱动将分别保存在两个链表中:bus_type->p->klist_devices和bus_type->p->klist_drivers。
4.2 device
设备对象在driver-model中使用struct device来表示。代码位于include/linux/device.h。不再重复。
device本身包含一个kobject,也就是说这个device在sysfs的某个地方有着一个对应的目录。
device包含私有指针变量,用于连接bus和device_driver,只有device能访问,其他不可访问。
1 /** 2 * struct device_private - structure to hold the private to the driver core portions of the device structure. 3 * 4 * @klist_children - klist containing all children of this device 5 * @knode_parent - node in sibling list 6 * @knode_driver - node in driver list 7 * @knode_bus - node in bus list 8 * @deferred_probe - entry in deferred_probe_list which is used to retry the 9 * binding of drivers which were unable to get all the resources needed by 10 * the device; typically because it depends on another driver getting 11 * probed first. 12 * @driver_data - private pointer for driver specific info. Will turn into a 13 * list soon. 14 * @device - pointer back to the struct class that this structure is 15 * associated with. 16 * 17 * Nothing outside of the driver core should ever touch these fields. 18 */ 19 struct device_private { 20 struct klist klist_children; 21 struct klist_node knode_parent; 22 struct klist_node knode_driver;//一个driver可以支持多个device,该device就是靠这个节点加入到匹配到的driver的链表的 23 struct klist_node knode_bus;//所属的bus,bus下所有device组成一个链表,表头是bus的klist_devices 24 struct list_head deferred_probe; 25 void *driver_data; 26 struct device *device; 27 };
该device所挂载的bus由device->p->knode_bus指定。
该device所对应的设备驱动由device->p->knode_driver指定。
4.3 device_driver
设备设备对象在driver-model中使用struct device_driver来表示。结构体定义位于include/linux/device.h。device_driver本身包含一个kobject,也就是说这个device_driver在sysfs的某个地方也有着一个对应的目录。
1 struct driver_private { 2 struct kobject kobj; 3 struct klist klist_devices;//一个driver可以支持多个device,用链表连接它所支持的device 4 struct klist_node knode_bus;//所属的bus,bus下所有driver组成一个链表,表头是bus的klist_drivers 5 struct module_kobject *mkobj; 6 struct device_driver *driver; 7 };
device_driver也有私有指针数据driver_private,同样只有该driver可以访问,其他不可访问。
该设备驱动所支持的设备由driver->p->klist_devices指定,该设备驱动所挂载的总线由driver->p->knode_bus制定。
小总:这样的话,bus、device和device_driver都有自己的私有指针数据,它们的功能就是负责连接对方。通过klist_node 强化版的链表,互相连接起来的。
5. platform bus实例
接下来来分析/sys/bus/platform是如何建立的。定义位于drivers\base\platform.c
platform总线的注册是由platform_bus_init函数完成的。该函数在内核启动阶段被调用,我们来简单看下调用过程:
start_kernel() -> rest_init() ->kernel_init() -> do_basic_setup() -> driver_init() -> platform_bus_init()。
注:kernel_init()是在rest_init函数中创建内核线程来执行的。
1 int __init platform_bus_init(void) 2 { 3 int error; 4 5 early_platform_cleanup(); 6 7 error = device_register(&platform_bus); 8 if (error) 9 return error; 10 error = bus_register(&platform_bus_type); 11 if (error) 12 device_unregister(&platform_bus); 13 return error; 14 } 15 16 struct device platform_bus = { 17 .init_name = "platform", 18 }; 19 20 struct bus_type platform_bus_type = { 21 .name = "platform", 22 .dev_attrs = platform_dev_attrs, 23 .match = platform_match, 24 .uevent = platform_uevent, 25 .pm = &platform_dev_pm_ops, 26 };
从bus_type,我们看到该总线的名字为platform。
调用了device_register和bus_register两个函数,我们只关注bus_register函数。
1 int bus_register(struct bus_type *bus) 2 { 3 int retval; 4 struct subsys_private *priv; 5 struct lock_class_key *key = &bus->lock_key; 6 7 priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); 8 if (!priv) 9 return -ENOMEM; 10 11 priv->bus = bus; 12 bus->p = priv; 13 14 BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); 15 16 retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); 17 if (retval) 18 goto out; 19 20 priv->subsys.kobj.kset = bus_kset; 21 priv->subsys.kobj.ktype = &bus_ktype; 22 priv->drivers_autoprobe = 1; 23 24 retval = kset_register(&priv->subsys); 25 if (retval) 26 goto out; 27 28 retval = bus_create_file(bus, &bus_attr_uevent); 29 if (retval) 30 goto bus_uevent_fail; 31 32 priv->devices_kset = kset_create_and_add("devices", NULL, 33 &priv->subsys.kobj); 34 if (!priv->devices_kset) { 35 retval = -ENOMEM; 36 goto bus_devices_fail; 37 } 38 39 priv->drivers_kset = kset_create_and_add("drivers", NULL, 40 &priv->subsys.kobj); 41 if (!priv->drivers_kset) { 42 retval = -ENOMEM; 43 goto bus_drivers_fail; 44 } 45 46 INIT_LIST_HEAD(&priv->interfaces); 47 __mutex_init(&priv->mutex, "subsys mutex", key); 48 klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); 49 klist_init(&priv->klist_drivers, NULL, NULL); 50 51 retval = add_probe_files(bus); 52 if (retval) 53 goto bus_probe_files_fail; 54 55 retval = bus_add_attrs(bus); 56 if (retval) 57 goto bus_attrs_fail; 58 59 pr_debug("bus: '%s': registered\n", bus->name); 60 return 0; 61 62 bus_attrs_fail: 63 remove_probe_files(bus); 64 bus_probe_files_fail: 65 kset_unregister(bus->p->drivers_kset); 66 bus_drivers_fail: 67 kset_unregister(bus->p->devices_kset); 68 bus_devices_fail: 69 bus_remove_file(bus, &bus_attr_uevent); 70 bus_uevent_fail: 71 kset_unregister(&bus->p->subsys); 72 out: 73 kfree(bus->p); 74 bus->p = NULL; 75 return retval; 76 }
函数中,首先调用kobject_set_name设置了bus对象的subsys.kobject->name 为 platform,也就是说会建立一个名为platform的目录。kobject_set_name函数在3.1小节中已经给出。
在这里还用到了bus_kset这个变量,这个变量就是在第3节buses_init函数中建立bus目录所对应的kset对象。
接着,priv->subsys.kobj.kset = bus_kset,设置subsys的kobj在bus_kset对象包含的集合中,也就是说bus目录下将包含subsys对象所对应的目录,即platform。
紧接着调用了kset_register,参数为&priv->subsys。该函数在3.2节中以给出。在该函数的调用过程中,将调用kobj_kset_join函数,该函数将kobject添加到kobject->kset的链表中。
1 /** 2 * kset_register - initialize and add a kset. 3 * @k: kset. 4 */ 5 int kset_register(struct kset *k) 6 { 7 int err; 8 9 if (!k) 10 return -EINVAL; 11 12 kset_init(k); 13 err = kobject_add_internal(&k->kobj); 14 if (err) 15 return err; 16 kobject_uevent(&k->kobj, KOBJ_ADD); 17 return 0; 18 }
kset_register函数执行完成后,将在/sys/bus/下建立目录platform。此刻,我们先来看下kset和kobject之间的关系。
然后,调用了bus_create_file函数在/sys/bus/platform/下建立文件uevent。
1 int bus_create_file(struct bus_type *bus, struct bus_attribute *attr) 2 { 3 int error; 4 if (bus_get(bus)) { 5 error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr); 6 bus_put(bus); 7 } else 8 error = -EINVAL; 9 return error; 10 }
有关底层的sysfs将在后面叙述,这里只要关注参数&bus->p->subsys.kobj,表示在该kset下建立文件,也就是platform下建立。
接着调用了2次kset_create_and_add,分别在/sys/bus/platform/下建立了文件夹devices和drivers。该函数位于第3节开始处。
这里和第3节调用kset_create_and_add时的最主要一个区别就是:此时的parent参数不为NULL,而是&priv->subsys.kobj。
也就是说,将要创建的kset的kobject->parent = &priv->subsys.kobj,也即新建的kset被包含在platform文件夹对应的kset中。
我们来看下关系图:
随后,bus_register接着调用了add_probe_files创建了属性文件drivers_autoprobe和drivers_probe。
1 static int add_probe_files(struct bus_type *bus) 2 { 3 int retval; 4 5 retval = bus_create_file(bus, &bus_attr_drivers_probe); 6 if (retval) 7 goto out; 8 9 retval = bus_create_file(bus, &bus_attr_drivers_autoprobe); 10 if (retval) 11 bus_remove_file(bus, &bus_attr_drivers_probe); 12 out: 13 return retval; 14 }
该函数只是简单的调用了两次bus_create_file,该函数已在前面叙述过。
最后调用bus_add_attrs创建总线相关的属性文件。
1 /** 2 * bus_add_attrs - Add default attributes for this bus. 3 * @bus: Bus that has just been registered. 4 */ 5 6 static int bus_add_attrs(struct bus_type *bus) 7 { 8 int error = 0; 9 int i; 10 11 if (bus->bus_attrs) { 12 for (i = 0; attr_name(bus->bus_attrs[i]); i++) { 13 error = bus_create_file(bus, &bus->bus_attrs[i]); 14 if (error) 15 goto err; 16 } 17 } 18 done: 19 return error; 20 err: 21 while (--i >= 0) 22 bus_remove_file(bus, &bus->bus_attrs[i]); 23 goto done; 24 }
我们可以看到这个函数将根据bus_type->bus_arrts来创建属性文件。不过,在本例中,bus_arrts从未给出定义,因此次函数不做任何工作。
好了,整个bus_register调用完成了,我们来看下sysfs中实际的情况。
[root1423 platform]#pwd /sys/bus/platform [root123 platform]#ls devices drivers drivers_autoprobe drivers_probe uevent
最后,我们对整个bus_register的过程进行一个小结。
6. device举例
本节将首先讲述如何在/sys/devices下建立虚拟的platform设备,然后再讲述如何在/sys/devices/platform/下建立子设备。
之所以叫虚拟是因为这个platform并不代表任何实际存在的设备,但是platform将是所有具体设备的父设备。
6.1虚拟的platform设备
在第5节,platform_bus_init函数中还调用了device_register,
1 int device_register(struct device *dev) 2 { 3 device_initialize(dev); /*初始化dev的某些字段*/ 4 return device_add(dev); /*将设备添加到系统中*/ 5 } 6 7 void device_initialize(struct device *dev) 8 { 9 dev->kobj.kset = devices_kset; /*设置kobj属于哪个kset,/sys/devices/*/ 10 kobject_init(&dev->kobj, &device_ktype);/*初始化dev->kobj*/ 11 INIT_LIST_HEAD(&dev->dma_pools); /*初始化链表头*/ 12 init_MUTEX(&dev->sem); /*初始化互斥体*/ 13 spin_lock_init(&dev->devres_lock); /*初始化自旋锁*/ 14 INIT_LIST_HEAD(&dev->devres_head); /*初始化链表头*/ 15 device_init_wakeup(dev, 0); /*设置该device不能唤醒*/ 16 device_pm_init(dev); /*设置该device的电源状态*/ 17 set_dev_node(dev, -1); /*如果使用NUMA,则设置NUMA节点*/ 18 }
6.1.1
首先其中用到了devices_kset对象,这个对象和第3节当中的bus_kset是同样的性质,也就是说该对象表示一个目录。
该对象的建立是在devices_init函数中完成的。
1 int __init devices_init(void) 2 { 3 devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); 4 if (!devices_kset) 5 return -ENOMEM; 6 dev_kobj = kobject_create_and_add("dev", NULL); 7 if (!dev_kobj) 8 goto dev_kobj_err; 9 sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj); 10 if (!sysfs_dev_block_kobj) 11 goto block_kobj_err; 12 sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj); 13 if (!sysfs_dev_char_kobj) 14 goto char_kobj_err; 15 16 return 0; 17 18 char_kobj_err: 19 kobject_put(sysfs_dev_block_kobj); 20 block_kobj_err: 21 kobject_put(dev_kobj); 22 dev_kobj_err: 23 kset_unregister(devices_kset); 24 return -ENOMEM; 25 }
由此可见,devices_kset对象表示的目录为/sys下的devices目录。
6.1.2kobject_init
1 void kobject_init(struct kobject *kobj, struct kobj_type *ktype) 2 { 3 char *err_str; 4 5 if (!kobj) { 6 err_str = "invalid kobject pointer!"; 7 goto error; 8 } 9 if (!ktype) { 10 err_str = "must have a ktype to be initialized properly!\n"; 11 goto error; 12 } 13 if (kobj->state_initialized) { 14 /* do not error out as sometimes we can recover */ 15 printk(KERN_ERR "kobject (%p): tried to init an initialized " 16 "object, something is seriously wrong.\n", kobj); 17 dump_stack(); 18 } 19 20 kobject_init_internal(kobj); 21 kobj->ktype = ktype; 22 return; 23 24 error: 25 printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str); 26 dump_stack(); 27 } 28 EXPORT_SYMBOL(kobject_init); 29 30 static void kobject_init_internal(struct kobject *kobj) 31 { 32 if (!kobj) 33 return; 34 kref_init(&kobj->kref); /*初始化引用基计数*/ 35 INIT_LIST_HEAD(&kobj->entry); /*初始化链表头*/ 36 kobj->state_in_sysfs = 0; 37 kobj->state_add_uevent_sent = 0; 38 kobj->state_remove_uevent_sent = 0; 39 kobj->state_initialized = 1; 40 }
该函数在做了一系列的必要检查后,调用kobject_init_internal初始化了kobject的某些字段。
6.1.3device_init_wakeup
参数val为0,设置该device不能够唤醒。
1 #ifdef CONFIG_PM 2 3 /* changes to device_may_wakeup take effect on the next pm state change. 4 * by default, devices should wakeup if they can. 5 */ 6 static inline void device_init_wakeup(struct device *dev, int val) 7 { 8 dev->power.can_wakeup = dev->power.should_wakeup = !!val; 9 } 10 #else /* !CONFIG_PM */ 11 12 /* For some reason the next two routines work even without CONFIG_PM */ 13 static inline void device_init_wakeup(struct device *dev, int val) 14 { 15 dev->power.can_wakeup = !!val; 16 } 17 #endif
6.2device_add
接下来是注册的第二步:调用device_add。
1 int device_add(struct device *dev) 2 { 3 struct device *parent = NULL; 4 struct class_interface *class_intf; 5 int error = -EINVAL; 6 7 dev = get_device(dev); /*增加引用计数*/ 8 if (!dev) 9 goto done; 10 11 dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL); /*分配device_private结构*/ 12 if (!dev->p) { 13 error = -ENOMEM; 14 goto done; 15 } 16 dev->p->device = dev; /*保存dev*/ 17 klist_init(&dev->p->klist_children, klist_children_get, /*初始化内核链表*/ 18 klist_children_put); 19 20 /* 21 * for statically allocated devices, which should all be converted 22 * some day, we need to initialize the name. We prevent reading back 23 * the name, and force the use of dev_name() 24 */ 25 if (dev->init_name) { 26 dev_set_name(dev, dev->init_name); /*dev->kobject->name = dev->init_name*/ 27 dev->init_name = NULL; 28 } 29 30 if (!dev_name(dev)) /*检查dev->kobject->name*/ 31 goto name_error; 32 33 pr_debug("device: '%s': %s\n", dev_name(dev), __func__); 34 35 parent = get_device(dev->parent); /*增加父设备引用计数*/ 36 setup_parent(dev, parent); /*设置dev->kobject->parent*/ 37 38 /* use parent numa_node */ 39 if (parent) 40 set_dev_node(dev, dev_to_node(parent)); 41 42 /* first, register with generic layer. */ 43 /* we require the name to be set before, and pass NULL */ 44 /* 执行完以后,将在/sys/devices/下建立目录XXX,目录名XXX为dev->kobj->name*/ 45 error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); 46 if (error) 47 goto Error; 48 49 /* notify platform of device entry */ 50 if (platform_notify) 51 platform_notify(dev); 52 53 /*在XXX下建立文件uevent*/ 54 error = device_create_file(dev, &uevent_attr); 55 if (error) 56 goto attrError; 57 58 if (MAJOR(dev->devt)) {/*主设备号不为0*/ 59 error = device_create_file(dev, &devt_attr);/*创建属性文件dev*/ 60 if (error) 61 goto ueventattrError; 62 63 /* 在sys/dev/char/下建立symlink,名字为主设备号:次设备号,该链接指向XXX */ 64 error = device_create_sys_dev_entry(dev); 65 if (error) 66 goto devtattrError; 67 } 68 69 error = device_add_class_symlinks(dev); 70 if (error) 71 goto SymlinkError; 72 error = device_add_attrs(dev); /*添加类设备属型文件和属性组*/ 73 if (error) 74 goto AttrsError; 75 error = bus_add_device(dev); /*添加3个symlink*/ 76 if (error) 77 goto BusError; 78 error = dpm_sysfs_add(dev); /*创建power子目录,并在其下添加电源管理的属性组文件*/ 79 if (error) 80 goto DPMError; 81 device_pm_add(dev); /*将该device添加到电源管理链表中*/ 82 83 /* Notify clients of device addition. This call must come 84 * after dpm_sysf_add() and before kobject_uevent(). 85 */ 86 if (dev->bus) 87 blocking_notifier_call_chain(&dev->bus->p->bus_notifier, 88 BUS_NOTIFY_ADD_DEVICE, dev); 89 90 kobject_uevent(&dev->kobj, KOBJ_ADD); /*通知用户层*/ 91 bus_attach_device(dev); /*将设备添加到总线的设备链表中,并尝试获取驱动*/ 92 if (parent) 93 klist_add_tail(&dev->p->knode_parent, /*有父设备,则将该设备添加到父设备的儿子链表中*/ 94 &parent->p->klist_children); 95 96 if (dev->class) { /*该设备属于某个设备类*/ 97 mutex_lock(&dev->class->p->class_mutex); 98 /* tie the class to the device */ 99 klist_add_tail(&dev->knode_class, /*将device添加到class的类设备链表中*/ 100 &dev->class->p->class_devices); 101 102 /* notify any interfaces that the device is here */ 103 list_for_each_entry(class_intf, 104 &dev->class->p->class_interfaces, node) 105 if (class_intf->add_dev) 106 class_intf->add_dev(dev, class_intf); 107 mutex_unlock(&dev->class->p->class_mutex); 108 } 109 done: 110 put_device(dev); 111 return error; 112 DPMError: 113 bus_remove_device(dev); 114 BusError: 115 device_remove_attrs(dev); 116 AttrsError: 117 device_remove_class_symlinks(dev); 118 SymlinkError: 119 if (MAJOR(dev->devt)) 120 device_remove_sys_dev_entry(dev); 121 devtattrError: 122 if (MAJOR(dev->devt)) 123 device_remove_file(dev, &devt_attr); 124 ueventattrError: 125 device_remove_file(dev, &uevent_attr); 126 attrError: 127 kobject_uevent(&dev->kobj, KOBJ_REMOVE); 128 kobject_del(&dev->kobj); 129 Error: 130 cleanup_device_parent(dev); 131 if (parent) 132 put_device(parent); 133 name_error: 134 kfree(dev->p); 135 dev->p = NULL; 136 goto done; 137 }
该函数是device的重要API,接下来以此设备为例,逐步分析
6.2.1setup_parent
下列代码位于drivers/base/core.c。
1 static void setup_parent(struct device *dev, struct device *parent) 2 { 3 struct kobject *kobj; 4 kobj = get_device_parent(dev, parent); 5 if (kobj) 6 dev->kobj.parent = kobj; 7 } 8 9 static struct kobject *get_device_parent(struct device *dev, 10 struct device *parent) 11 { 12 /* class devices without a parent live in /sys/class/<classname>/ */ 13 if (dev->class && (!parent || parent->class != dev->class)) 14 return &dev->class->p->class_subsys.kobj; 15 /* all other devices keep their parent */ 16 else if (parent) 17 return &parent->kobj; 18 19 return NULL; 20 }
该函数将设置dev对象的parent。在这里实际传入的parent为NULL,同时dev->class也没有定义过。因此这个函数什么都没有做。
6.2.2 kobject_add
1 int kobject_add(struct kobject *kobj, struct kobject *parent, 2 const char *fmt, ...) 3 { 4 va_list args; 5 int retval; 6 7 if (!kobj) 8 return -EINVAL; 9 10 if (!kobj->state_initialized) { 11 printk(KERN_ERR "kobject '%s' (%p): tried to add an " 12 "uninitialized object, something is seriously wrong.\n", 13 kobject_name(kobj), kobj); 14 dump_stack(); 15 return -EINVAL; 16 } 17 va_start(args, fmt); 18 retval = kobject_add_varg(kobj, parent, fmt, args); 19 va_end(args); 20 21 return retval; 22 } 23 EXPORT_SYMBOL(kobject_add); 24 25 static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, 26 const char *fmt, va_list vargs) 27 { 28 int retval; 29 30 retval = kobject_set_name_vargs(kobj, fmt, vargs); 31 if (retval) { 32 printk(KERN_ERR "kobject: can not set name properly!\n"); 33 return retval; 34 } 35 kobj->parent = parent; 36 return kobject_add_internal(kobj); 37 } 38 39 static int kobject_add_internal(struct kobject *kobj) 40 { 41 int error = 0; 42 struct kobject *parent; 43 44 if (!kobj) 45 return -ENOENT; 46 /*检查name字段是否存在*/ 47 if (!kobj->name || !kobj->name[0]) { 48 WARN(1, "kobject: (%p): attempted to be registered with empty " 49 "name!\n", kobj); 50 return -EINVAL; 51 } 52 53 parent = kobject_get(kobj->parent); /*有父对象则增加父对象引用计数*/ 54 55 /* join kset if set, use it as parent if we do not already have one */ 56 if (kobj->kset) { 57 if (!parent) 58 /*kobj属于某个kset,但是该kobj没有父对象,则以kset的kobj作为父对象*/ 59 parent = kobject_get(&kobj->kset->kobj); 60 kobj_kset_join(kobj); /*将kojbect添加到kset结构中的链表当中*/ 61 kobj->parent = parent; 62 } 63 64 pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n", 65 kobject_name(kobj), kobj, __func__, 66 parent ? kobject_name(parent) : "<NULL>", 67 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>"); 68 69 error = create_dir(kobj); /*根据kobj->name在sys中建立目录*/ 70 if (error) { 71 kobj_kset_leave(kobj); /*删除链表项*/ 72 kobject_put(parent); /*减少引用计数*/ 73 kobj->parent = NULL; 74 75 /* be noisy on error issues */ 76 if (error == -EEXIST) 77 printk(KERN_ERR "%s failed for %s with " 78 "-EEXIST, don't try to register things with " 79 "the same name in the same directory.\n", 80 __func__, kobject_name(kobj)); 81 else 82 printk(KERN_ERR "%s failed for %s (%d)\n", 83 __func__, kobject_name(kobj), error); 84 dump_stack(); 85 } else 86 kobj->state_in_sysfs = 1; 87 88 return error; 89 }
在调用时,参数parent为NULL,且dev->kobj.kset在6.1节device_initialize函数中设置为devices_kset。
而devices_kset对应着/sys/devices目录,因此该函数调用完成后将在/sys/devices目录下生成目录platform。
但是这里比较奇怪的是,为什么platform目录没有对应的kset对象?
6.2.3device_create_sys_dev_entry
在调用该函数之前,会在/sys/devices/platform/下生成属性文件。接着如果该device的设备号不为0,则创建属性文件dev,并调用本函数。
但是,在本例中设备号devt从未设置过,显然为0,那么本函数实际并未执行。
1 static int device_create_sys_dev_entry(struct device *dev) 2 { 3 struct kobject *kobj = device_to_dev_kobj(dev); 4 int error = 0; 5 char devt_str[15]; 6 7 if (kobj) { 8 format_dev_t(devt_str, dev->devt); 9 error = sysfs_create_link(kobj, &dev->kobj, devt_str); 10 } 11 12 return error; 13 } 14 /** 15 * device_to_dev_kobj - select a /sys/dev/ directory for the device 16 * @dev: device 17 * 18 * By default we select char/ for new entries. Setting class->dev_obj 19 * to NULL prevents an entry from being created. class->dev_kobj must 20 * be set (or cleared) before any devices are registered to the class 21 * otherwise device_create_sys_dev_entry() and 22 * device_remove_sys_dev_entry() will disagree about the the presence 23 * of the link. 24 */ 25 static struct kobject *device_to_dev_kobj(struct device *dev) 26 { 27 struct kobject *kobj; 28 29 if (dev->class) 30 kobj = dev->class->dev_kobj; 31 else 32 kobj = sysfs_dev_char_kobj; 33 34 return kobj; 35 }
6.2.4device_add_class_symlinks
由于dev->class为NULL,本函数其实没做任何工作。
下列代码位于drivers/base/core.c。
1 static int device_add_class_symlinks(struct device *dev) 2 { 3 int error; 4 5 if (!dev->class) 6 return 0; 7 8 error = sysfs_create_link(&dev->kobj, 9 &dev->class->p->class_subsys.kobj, 10 "subsystem"); 11 if (error) 12 goto out; 13 14 #ifdef CONFIG_SYSFS_DEPRECATED 15 /* stacked class devices need a symlink in the class directory */ 16 if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && 17 device_is_not_partition(dev)) { 18 error = sysfs_create_link(&dev->class->p->class_subsys.kobj, 19 &dev->kobj, dev_name(dev)); 20 if (error) 21 goto out_subsys; 22 } 23 24 if (dev->parent && device_is_not_partition(dev)) { 25 struct device *parent = dev->parent; 26 char *class_name; 27 28 /* 29 * stacked class devices have the 'device' link 30 * pointing to the bus device instead of the parent 31 */ 32 while (parent->class && !parent->bus && parent->parent) 33 parent = parent->parent; 34 35 error = sysfs_create_link(&dev->kobj, 36 &parent->kobj, 37 "device"); 38 if (error) 39 goto out_busid; 40 41 class_name = make_class_name(dev->class->name, 42 &dev->kobj); 43 if (class_name) 44 error = sysfs_create_link(&dev->parent->kobj, 45 &dev->kobj, class_name); 46 kfree(class_name); 47 if (error) 48 goto out_device; 49 } 50 return 0; 51 52 out_device: 53 if (dev->parent && device_is_not_partition(dev)) 54 sysfs_remove_link(&dev->kobj, "device"); 55 out_busid: 56 if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && 57 device_is_not_partition(dev)) 58 sysfs_remove_link(&dev->class->p->class_subsys.kobj, 59 dev_name(dev)); 60 #else 61 /* link in the class directory pointing to the device */ 62 error = sysfs_create_link(&dev->class->p->class_subsys.kobj, 63 &dev->kobj, dev_name(dev)); 64 if (error) 65 goto out_subsys; 66 67 if (dev->parent && device_is_not_partition(dev)) { 68 error = sysfs_create_link(&dev->kobj, &dev->parent->kobj, 69 "device"); 70 if (error) 71 goto out_busid; 72 } 73 return 0; 74 75 out_busid: 76 sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev)); 77 #endif 78 79 out_subsys: 80 sysfs_remove_link(&dev->kobj, "subsystem"); 81 out: 82 return error; 83 }
6.2.5
同样dev->class为空,什么都没干。
下列代码位于drivers/base/core.c。
1 static int device_add_attrs(struct device *dev) 2 { 3 struct class *class = dev->class; 4 struct device_type *type = dev->type; 5 int error; 6 7 if (class) { 8 error = device_add_attributes(dev, class->dev_attrs); 9 if (error) 10 return error; 11 } 12 13 if (type) { 14 error = device_add_groups(dev, type->groups); 15 if (error) 16 goto err_remove_class_attrs; 17 } 18 19 error = device_add_groups(dev, dev->groups); 20 if (error) 21 goto err_remove_type_groups; 22 23 return 0; 24 25 err_remove_type_groups: 26 if (type) 27 device_remove_groups(dev, type->groups); 28 err_remove_class_attrs: 29 if (class) 30 device_remove_attributes(dev, class->dev_attrs); 31 32 return error; 33 }
6.2.6bus_add_device
由于dev->bus未指定,因此这个函数什么都没干。
该函数将创建三个symlink,在sysfs中建立总线和设备间的关系。
下列代码位于drivers/base/bus.c。
1 int bus_add_device(struct device *dev) 2 { 3 struct bus_type *bus = bus_get(dev->bus); 4 int error = 0; 5 6 if (bus) { 7 pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev)); 8 error = device_add_attrs(bus, dev); 9 if (error) 10 goto out_put; 11 12 /*在sys/bus/XXX/devices下建立symlink,名字为设备名,该链接指向/sys/devices/下的某个目录*/ 13 error = sysfs_create_link(&bus->p->devices_kset->kobj, 14 &dev->kobj, dev_name(dev)); 15 if (error) 16 goto out_id; 17 18 /*在sys/devices/的某个目录下建立symlink,名字为subsystem,该链接指向/sys/bus/下的某个目录*/ 19 error = sysfs_create_link(&dev->kobj, 20 &dev->bus->p->subsys.kobj, "subsystem"); 21 if (error) 22 goto out_subsys; 23 24 /*在sys/devices/的某个目录下建立symlink,名字为bus,该链接指向/sys/bus/下的某个目录*/ 25 error = make_deprecated_bus_links(dev); 26 if (error) 27 goto out_deprecated; 28 } 29 return 0; 30 31 out_deprecated: 32 sysfs_remove_link(&dev->kobj, "subsystem"); 33 out_subsys: 34 sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev)); 35 out_id: 36 device_remove_attrs(bus, dev); 37 out_put: 38 bus_put(dev->bus); 39 return error; 40 }
6.2.7
下列代码位于drivers/base/power/sysfs.c。
1 int dpm_sysfs_add(struct device * dev) 2 { 3 return sysfs_create_group(&dev->kobj, &pm_attr_group); 4 } 5 6 static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); 7 8 9 static struct attribute * power_attrs[] = { 10 &dev_attr_wakeup.attr, 11 NULL, 12 }; 13 static struct attribute_group pm_attr_group = { 14 .name = "power", 15 .attrs = power_attrs, 16 };
该函数将在XXX目录下建立power子目录,并在该子目录下建立属性文件wakeup。
在本例中,将在/sys/bus/platform下建立子目录power并在子目录下建立wakeup文件。
6.2.8
下列代码位于drivers/base/power/main.c。该函数只是将设备添加到电源管理链表中。
1 /** 2 * device_pm_add - add a device to the list of active devices 3 * @dev: Device to be added to the list 4 */ 5 void device_pm_add(struct device *dev) 6 { 7 pr_debug("PM: Adding info for %s:%s\n", 8 dev->bus ? dev->bus->name : "No Bus", 9 kobject_name(&dev->kobj)); 10 mutex_lock(&dpm_list_mtx); 11 if (dev->parent) { 12 if (dev->parent->power.status >= DPM_SUSPENDING) 13 dev_warn(dev, "parent %s should not be sleeping\n", 14 dev_name(dev->parent)); 15 } else if (transition_started) { 16 /* 17 * We refuse to register parentless devices while a PM 18 * transition is in progress in order to avoid leaving them 19 * unhandled down the road 20 */ 21 dev_WARN(dev, "Parentless device registered during a PM transaction\n"); 22 } 23 24 list_add_tail(&dev->power.entry, &dpm_list); /*将该设备添加到链表中*/ 25 mutex_unlock(&dpm_list_mtx); 26 }
6.2.9
1 void bus_attach_device(struct device *dev) 2 { 3 struct bus_type *bus = dev->bus; 4 int ret = 0; 5 6 if (bus) { 7 if (bus->p->drivers_autoprobe) 8 ret = device_attach(dev); /*尝试获取驱动*/ 9 WARN_ON(ret < 0); 10 if (ret >= 0) /*将设备挂在到总线中*/ 11 klist_add_tail(&dev->p->knode_bus, 12 &bus->p->klist_devices); 13 } 14 } 15 16 /** 17 * device_attach - try to attach device to a driver. 18 * @dev: device. 19 * 20 * Walk the list of drivers that the bus has and call 21 * driver_probe_device() for each pair. If a compatible 22 * pair is found, break out and return. 23 * 24 * Returns 1 if the device was bound to a driver; 25 * 0 if no matching device was found; 26 * -ENODEV if the device is not registered. 27 * 28 * When called for a USB interface, @dev->parent->sem must be held. 29 */ 30 int device_attach(struct device *dev) 31 { 32 int ret = 0; 33 34 down(&dev->sem); 35 if (dev->driver) { /*如果已指定驱动,即已绑定*/ 36 ret = device_bind_driver(dev); /*在sysfs中建立链接关系*/ 37 if (ret == 0) 38 ret = 1; 39 else { 40 dev->driver = NULL; 41 ret = 0; 42 } 43 } else { /*尚未绑定,尝试绑定,遍历该总线上的所有驱动*/ 44 ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); 45 } 46 up(&dev->sem); 47 return ret; 48 }
如果bus存在的话,将会调用device_attach函数进行绑定工作。该函数首先判断dev->driver,如果非0,表示该设备已经绑定了驱动,只要在sysfs中建立链接关系即可。
为0表示没有绑定,接着调用bus_for_each_drv,注意作为参数传入的__device_attach,这是个函数,后面会调用它。
我们来看下bus_for_each_drv:
1 int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, 2 void *data, int (*fn)(struct device_driver *, void *)) 3 { 4 struct klist_iter i; 5 struct device_driver *drv; 6 int error = 0; 7 8 if (!bus) 9 return -EINVAL; 10 11 klist_iter_init_node(&bus->p->klist_drivers, &i, 12 start ? &start->p->knode_bus : NULL); 13 while ((drv = next_driver(&i)) && !error) 14 error = fn(drv, data); 15 klist_iter_exit(&i); 16 return error; 17 }
该函数将遍历总线的drivers目录下的所有驱动,也就是/sys/bus/XXX/drivers/下的目录,为该driver调用fn函数,也就是__device_attach。我们来看下:
1 static int __device_attach(struct device_driver *drv, void *data) 2 { 3 struct device *dev = data; 4 5 if (!driver_match_device(drv, dev)) /*进行匹配工作*/ 6 return 0; 7 8 return driver_probe_device(drv, dev); 9 } 10 11 static inline int driver_match_device(struct device_driver *drv, 12 struct device *dev) 13 { 14 return drv->bus->match ? drv->bus->match(dev, drv) : 1; 15 } 16 17 /** 18 * driver_probe_device - attempt to bind device & driver together 19 * @drv: driver to bind a device to 20 * @dev: device to try to bind to the driver 21 * 22 * This function returns -ENODEV if the device is not registered, 23 * 1 if the device is bound sucessfully and 0 otherwise. 24 * 25 * This function must be called with @dev->sem held. When called for a 26 * USB interface, @dev->parent->sem must be held as well. 27 */ 28 int driver_probe_device(struct device_driver *drv, struct device *dev) 29 { 30 int ret = 0; 31 32 if (!device_is_registered(dev)) /*该device是否已在sysfs中*/ 33 return -ENODEV; 34 35 pr_debug("bus: '%s': %s: matched device %s with driver %s\n", 36 drv->bus->name, __func__, dev_name(dev), drv->name); 37 38 ret = really_probe(dev, drv);/*device已在sysfs,调用really_probe*/ 39 40 return ret; 41 }
该函数首先调用driver_match_device函数,后者将会调用总线的match方法,如果有的话,来进行匹配工作。如果没有该方法,则返回1,表示匹配成功。
我们这里是针对platform总线,该总线的方法将在7.6.2节中看到。
随后,又调用了driver_probe_device函数。该函数将首先判断该device是否已在sysfs中,如果在则调用really_probe,否则返回出错。
really_probe将会调用驱动的probe并完成绑定的工作。该函数将在7.6.2节中分析。
6.3.10总结
在本例中,当device_register调用完成以后,将在/sys/devices/下建立目录platform,并在platfrom下建立属性文件uevent和子目录power,最后在power子目录下建立wakeup属性文件。
整个调用流程如下:
6.3 spi主控制器的平台设备
本节对一个特定的platform设备进行讲解,那就是spi主控制器的平台设备。
在内核的启动阶段,platform设备将被注册进内核。我们来看下。
下列代码位于arch/arm/mach-s3c2440/mach-smdk2440.c
1 static struct resource s3c_spi0_resource[] = { 2 [0] = { 3 .start = S3C24XX_PA_SPI, 4 .end = S3C24XX_PA_SPI + 0x1f, 5 .flags = IORESOURCE_MEM, 6 }, 7 [1] = { 8 .start = IRQ_SPI0, 9 .end = IRQ_SPI0, 10 .flags = IORESOURCE_IRQ, 11 } 12 13 }; 14 15 static u64 s3c_device_spi0_dmamask = 0xffffffffUL; 16 17 struct platform_device s3c_device_spi0 = { 18 .name = "s3c2410-spi", 19 .id = 0, 20 .num_resources = ARRAY_SIZE(s3c_spi0_resource), 21 .resource = s3c_spi0_resource, 22 .dev = { 23 .dma_mask = &s3c_device_spi0_dmamask, 24 .coherent_dma_mask = 0xffffffffUL 25 } 26 }; 27 28 static struct platform_device *smdk2440_devices[] __initdata = { 29 &s3c_device_usb, 30 &s3c_device_lcd, 31 &s3c_device_wdt, 32 &s3c_device_i2c0, 33 &s3c_device_iis, 34 &s3c_device_spi0, 35 }; 36 37 static void __init smdk2440_machine_init(void) 38 { 39 s3c24xx_fb_set_platdata(&smdk2440_fb_info); 40 s3c_i2c0_set_platdata(NULL); 41 42 platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices)); 43 smdk_machine_init(); 44 }
在smdk2440_machine_init函数中,通过调用platform_add_devices将设备注册到内核中。接着来看下该函数。
6.3.1 platform_add_devices
1 /** 2 * platform_add_devices - add a numbers of platform devices 3 * @devs: array of platform devices to add 4 * @num: number of platform devices in array 5 */ 6 int platform_add_devices(struct platform_device **devs, int num) 7 { 8 int i, ret = 0; 9 10 for (i = 0; i < num; i++) { 11 ret = platform_device_register(devs[i]); 12 if (ret) { 13 while (--i >= 0) 14 platform_device_unregister(devs[i]); 15 break; 16 } 17 } 18 19 return ret; 20 }
该函数将根据devs指针数组,调用platform_device_register将platform设备逐一注册进内核。
6.3.2 platform_device_register
1 /** 2 * platform_device_register - add a platform-level device 3 * @pdev: platform device we're adding 4 */ 5 int platform_device_register(struct platform_device *pdev) 6 { 7 device_initialize(&pdev->dev); 8 return platform_device_add(pdev); 9 }
调用了两个函数,第一个函数在6.1节已经分析过。我们来看下第二个函数。
6.3.2 platform_device_register
1 /** 2 * platform_device_add - add a platform device to device hierarchy 3 * @pdev: platform device we're adding 4 * 5 * This is part 2 of platform_device_register(), though may be called 6 * separately _iff_ pdev was allocated by platform_device_alloc(). 7 */ 8 int platform_device_add(struct platform_device *pdev) 9 { 10 int i, ret = 0; 11 12 if (!pdev) 13 return -EINVAL; 14 15 if (!pdev->dev.parent) 16 pdev->dev.parent = &platform_bus; /*该设备的父设备是platform设备,/sys/devices/platform*/ 17 18 pdev->dev.bus = &platform_bus_type; /*设备挂载到platform总线上*/ 19 20 if (pdev->id != -1) 21 dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); 22 else 23 dev_set_name(&pdev->dev, pdev->name);/*pdev->dev->kobj->name = pdev->name*/ 24 25 /*遍历平台设备的资源,并将资源添加到资源树中*/ 26 for (i = 0; i < pdev->num_resources; i++) { 27 struct resource *p, *r = &pdev->resource[i]; 28 29 if (r->name == NULL) 30 r->name = dev_name(&pdev->dev); /*获取dev->kobject->name*/ 31 32 p = r->parent; 33 if (!p) { /*p空*/ 34 if (resource_type(r) == IORESOURCE_MEM) 35 p = &iomem_resource; 36 else if (resource_type(r) == IORESOURCE_IO) 37 p = &ioport_resource; 38 } 39 40 if (p && insert_resource(p, r)) { /*将资源添加到资源树中*/ 41 printk(KERN_ERR 42 "%s: failed to claim resource %d\n", 43 dev_name(&pdev->dev), i); 44 ret = -EBUSY; 45 goto failed; 46 } 47 } 48 49 pr_debug("Registering platform device '%s'. Parent at %s\n", 50 dev_name(&pdev->dev), dev_name(pdev->dev.parent)); 51 52 ret = device_add(&pdev->dev); /*添加设备*/ 53 if (ret == 0) 54 return ret; 55 56 failed: 57 while (--i >= 0) { 58 struct resource *r = &pdev->resource[i]; 59 unsigned long type = resource_type(r); 60 61 if (type == IORESOURCE_MEM || type == IORESOURCE_IO) 62 release_resource(r); 63 } 64 65 return ret; 66 }
在这个函数的最后赫然出现了device_add函数。我们回忆下在6.1节中device_register的注册过程,该函数只调用了两个函数,一个是device_initialize函数,另一个就是device_add。
本节的platform_device_register函数,首先也是调用了device_initialize,但是随后他做了一些其他的工作,最后调用了device_add。
那么这个"其他的工作"干了些什么呢?
首先,它将该SPI主控制对应的平台设备的父设备设为虚拟的platform设备(platform_bus),然后将该平台设备挂在至platform总线(platform_bus_type)上,这两步尤为重要,后面我们将看到。
然后,调用了dev_set_name设置了pdev->dev-kobj.name,也就是该设备对象的名字,这里的名字为s3c2410-spi.0,这个名字将被用来建立一个目录。
最后,将平台的相关资源添加到资源树中。这不是本篇文章讨论的重点所在,所以不做过多说明。
在"其他的工作""干完之后,调用了device_add函数。那么后面的函数调用过程将和6.2小结的一致。
由于“其他的工作”的原因,实际执行的过程和结果将有所区别。我们来分析下。
6.3.3 不一样device_add调用结果
首先,在device_add被调用之前,有若干个非常重要的条件已经被设置了。如下:
pdev->dev->kobj.kset = devices_kset
pdev->dev-.parent = &platform_bus
pdev->dev.bus = &platform_bus_type
set_up函数执行时,由于参数parent为&platform_bus,因此最后将设置pdev->dev->kobj.parent = platform_bus.kobj。平台设备对象的父对象为虚拟的platform设备。
kobject_add函数执行时,由于参数parent的存在,将在parent对象所对应的目录下创建另一个目录。parent对象代表目录/sys/devices/下的platform,因此将在/sys/devices/platform下建立目录s3c2410-spi.0。
device_create_file建立属性文件uevent。
bus_add_device函数执行时,由于dev.bus 为&platform_bus_type,因此将建立三个symlink。
/sys/devices/platform/s3c2410-spi.0下建立链接subsystem和bus,他们指向/sys/bus/platform。
/sys/bus/platform/devices/下建立链接s3c2410-spi.0,指向/sys/devices/platform/s3c2410-spi.0。
dpm_sysfs_add函数在/sys/devices/platform/s3c2410-spi.0下建立子目录power,并在该子目录下建立属性文件wakeup。
执行到这里时,sysfs已将内核中新添加的SPI主控制器平台设备呈现出来了,我们来验证下。
[root@yj423 s3c2410-spi.0]#pwd /sys/devices/platform/s3c2410-spi.0 [root@yj423 s3c2410-spi.0]#ll lrwxrwxrwx 1 root root 0 Jan 1 00:29 bus -> ../../../bus/platform lrwxrwxrwx 1 root root 0 Jan 1 00:29 driver -> ../../../bus/platform/drivers/s3c2410-spi -r--r--r-- 1 root root 4096 Jan 1 00:29 modalias drwxr-xr-x 2 root root 0 Jan 1 00:29 power drwxr-xr-x 3 root root 0 Jan 1 00:00 spi0.0 drwxr-xr-x 3 root root 0 Jan 1 00:00 spi0.1 lrwxrwxrwx 1 root root 0 Jan 1 00:29 spi_master:spi0 -> ../../../class/spi_master/spi0 lrwxrwxrwx 1 root root 0 Jan 1 00:29 subsystem -> ../../../bus/platform -rw-r--r-- 1 root root 4096 Jan 1 00:29 uevent [root@yj423 devices]#pwd /sys/bus/platform/devices [root@yj423 devices]#ll s3c2410-spi.0 lrwxrwxrwx 1 root root 0 Jan 1 00:44 s3c2410-spi.0 -> ../../../devices/platform/s3c2410-spi.0
通过sysfs将设备驱动的模型层次呈现在用户空间以后,将更新内核的设备模型之间的关系,这是通过修改链表的指向来完成的。
bus_attach_device函数执行时,将设备添加到总线的设备链表中,同时也会尝试绑定驱动,不过会失败。
接着,由于dev->parent的存在,将SPI主控制器设备添加到父设备platform虚拟设备的儿子链表中。
7. driver举例
我们已经介绍过platform总线的注册,也讲述了SPI主控制器设备作为平台设备的注册过程,在本节,将描述SPI主控制器的platform驱动是如何注册的。
7.1 s3c24xx_spi_init
下列代码位于drivers/spi/spi_s3c24xx.c。
1 MODULE_ALIAS("platform:s3c2410-spi"); 2 static struct platform_driver s3c24xx_spi_driver = { 3 .remove = __exit_p(s3c24xx_spi_remove), 4 .suspend = s3c24xx_spi_suspend, 5 .resume = s3c24xx_spi_resume, 6 .driver = { 7 .name = "s3c2410-spi", 8 .owner = THIS_MODULE, 9 }, 10 }; 11 12 static int __init s3c24xx_spi_init(void) 13 { 14 return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);//设备不可热插拔,所以使用该函数,而不是platform_driver_register 15 )
驱动注册通过调用platform_driver_probe来完成。
注意:driver.name字段使用来匹配设备的,该字段必须和6.3节一开始给出的pdev.name字段相同。
7.2 platform_driver_probe
下列代码位于drivers/base/platform.c。
1 /** 2 * platform_driver_probe - register driver for non-hotpluggable device 3 * @drv: platform driver structure 4 * @probe: the driver probe routine, probably from an __init section 5 * 6 * Use this instead of platform_driver_register() when you know the device 7 * is not hotpluggable and has already been registered, and you want to 8 * remove its run-once probe() infrastructure from memory after the driver 9 * has bound to the device. 10 * 11 * One typical use for this would be with drivers for controllers integrated 12 * into system-on-chip processors, where the controller devices have been 13 * configured as part of board setup. 14 * 15 * Returns zero if the driver registered and bound to a device, else returns 16 * a negative error code and with the driver not registered. 17 */ 18 int __init_or_module platform_driver_probe(struct platform_driver *drv, 19 int (*probe)(struct platform_device *)) 20 { 21 int retval, code; 22 23 /* temporary section violation during probe() */ 24 drv->probe = probe; 25 retval = code = platform_driver_register(drv); /*注册platform驱动*/ 26 27 /* Fixup that section violation, being paranoid about code scanning 28 * the list of drivers in order to probe new devices. Check to see 29 * if the probe was successful, and make sure any forced probes of 30 * new devices fail. 31 */ 32 spin_lock(&platform_bus_type.p->klist_drivers.k_lock); 33 drv->probe = NULL; 34 if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list)) 35 retval = -ENODEV; 36 drv->driver.probe = platform_drv_probe_fail; 37 spin_unlock(&platform_bus_type.p->klist_drivers.k_lock); 38 39 if (code != retval) 40 platform_driver_unregister(drv); 41 return retval; 42 }
这里的重点是platform_driver_register,由它来完成了platform驱动的注册。
7.3 platform_driver_register
1 /** 2 * platform_driver_register 3 * @drv: platform driver structure 4 */ 5 int platform_driver_register(struct platform_driver *drv) 6 { 7 drv->driver.bus = &platform_bus_type;//驱动挂载的总线 8 if (drv->probe) 9 drv->driver.probe = platform_drv_probe; 10 if (drv->remove) 11 drv->driver.remove = platform_drv_remove; 12 if (drv->shutdown) 13 drv->driver.shutdown = platform_drv_shutdown; 14 if (drv->suspend) 15 drv->driver.suspend = platform_drv_suspend; 16 if (drv->resume) 17 drv->driver.resume = platform_drv_resume; 18 return driver_register(&drv->driver); /*驱动注册*/ 19 }
7.4driver_register
driver_register以及其他driver的api详解:linux设备驱动(3)devive_driver 详解
driver_register函数就是driver注册的核心函数。需要注意的是,在调用函数之前,将该驱动所挂载的总线设置为platform总线(platform_bus_type)。
1 /** 2 * driver_register - register driver with bus 3 * @drv: driver to register 4 * 5 * We pass off most of the work to the bus_add_driver() call, 6 * since most of the things we have to do deal with the bus 7 * structures. 8 */ 9 int driver_register(struct device_driver *drv) 10 { 11 int ret; 12 struct device_driver *other; 13 14 BUG_ON(!drv->bus->p); 15 16 if ((drv->bus->probe && drv->probe) || 17 (drv->bus->remove && drv->remove) || 18 (drv->bus->shutdown && drv->shutdown)) 19 printk(KERN_WARNING "Driver '%s' needs updating - please use " 20 "bus_type methods\n", drv->name); 21 22 other = driver_find(drv->name, drv->bus);/*用驱动名字来搜索在该总线上驱动是否已经存在*/ 23 if (other) { /*存在则报错*/ 24 put_driver(other); 25 printk(KERN_ERR "Error: Driver '%s' is already registered, " 26 "aborting...\n", drv->name); 27 return -EEXIST; 28 } 29 30 ret = bus_add_driver(drv); /*将驱动添加到一个总线中*/ 31 if (ret) 32 return ret; 33 ret = driver_add_groups(drv, drv->groups); /*建立属性组文件*/ 34 if (ret) 35 bus_remove_driver(drv); 36 return ret; 37 }
这里主要调用两个函数driver_find和bus_add_driver。前者将通过总线来搜索该驱动是否存在,后者将添加驱动到总线中。
接下来就分析这两个函数。
7.5driver_find
1 /** 2 * driver_find - locate driver on a bus by its name. 3 * @name: name of the driver. 4 * @bus: bus to scan for the driver. 5 * 6 * Call kset_find_obj() to iterate over list of drivers on 7 * a bus to find driver by name. Return driver if found. 8 * 9 * Note that kset_find_obj increments driver's reference count. 10 */ 11 struct device_driver *driver_find(const char *name, struct bus_type *bus) 12 { 13 struct kobject *k = kset_find_obj(bus->p->drivers_kset, name); 14 struct driver_private *priv; 15 16 if (k) { 17 priv = to_driver(k); 18 return priv->driver; 19 } 20 return NULL; 21 } 22 23 24 /** 25 * kset_find_obj - search for object in kset. 26 * @kset: kset we're looking in. 27 * @name: object's name. 28 * 29 * Lock kset via @kset->subsys, and iterate over @kset->list, 30 * looking for a matching kobject. If matching object is found 31 * take a reference and return the object. 32 */ 33 struct kobject *kset_find_obj(struct kset *kset, const char *name) 34 { 35 struct kobject *k; 36 struct kobject *ret = NULL; 37 38 spin_lock(&kset->list_lock); 39 list_for_each_entry(k, &kset->list, entry) { 40 if (kobject_name(k) && !strcmp(kobject_name(k), name)) { 41 ret = kobject_get(k); 42 break; 43 } 44 } 45 spin_unlock(&kset->list_lock); 46 return ret; 47 }
这里调用了kset_find_obj函数,传入的实参bus->p->drivers_kset,它对应的就是/sys/bus/platform/下的drivers目录,然后通过链表,它将搜索该目录下的所有文件,来寻找是否有名为s3c2410-spi的文件。还记得吗? kobject就是一个文件对象。如果没有找到将返回NULL,接着将调用bus_add_driver把驱动注册进内核。
7.6bus_add_driver
1 /** 2 * bus_add_driver - Add a driver to the bus. 3 * @drv: driver. 4 */ 5 int bus_add_driver(struct device_driver *drv) 6 { 7 struct bus_type *bus; 8 struct driver_private *priv; 9 int error = 0; 10 11 bus = bus_get(drv->bus); /*增加引用计数获取bus_type*/ 12 if (!bus) 13 return -EINVAL; 14 15 pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name); 16 17 priv = kzalloc(sizeof(*priv), GFP_KERNEL); /*分配driver_private结构体*/ 18 if (!priv) { 19 error = -ENOMEM; 20 goto out_put_bus; 21 } 22 /*初始化内核链表*/ 23 klist_init(&priv->klist_devices, NULL, NULL); 24 /*相互保存*/ 25 priv->driver = drv; 26 drv->p = priv; 27 /*设置该kobj属于那个kset*/ 28 priv->kobj.kset = bus->p->drivers_kset; 29 error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, /*parent=NULL*/ 30 "%s", drv->name); /*执行完以后,会在bus/总线名/drivers/下建立名为drv->name的目录*/ 31 if (error) 32 goto out_unregister; 33 34 if (drv->bus->p->drivers_autoprobe) { 35 error = driver_attach(drv); /*尝试绑定驱动和设备*/ 36 if (error) 37 goto out_unregister; 38 } 39 /*添加该驱动到bus的内核链表中*/ 40 klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); 41 module_add_driver(drv->owner, drv);/*?????????*/ 42 43 /*创建属性,在bus/总线名/drivers/驱动名/下建立文件uevent*/ 44 error = driver_create_file(drv, &driver_attr_uevent); 45 if (error) { 46 printk(KERN_ERR "%s: uevent attr (%s) failed\n", 47 __func__, drv->name); 48 } 49 /*利用bus->drv_attrs创建属性,位于bus/总线名/drivers/驱动名/*/ 50 error = driver_add_attrs(bus, drv); 51 if (error) { 52 /* How the hell do we get out of this pickle? Give up */ 53 printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", 54 __func__, drv->name); 55 } 56 /*创建属性,在bus/总线名/drivers/驱动名/下建立文件bind和unbind*/ 57 error = add_bind_files(drv); 58 if (error) { 59 /* Ditto */ 60 printk(KERN_ERR "%s: add_bind_files(%s) failed\n", 61 __func__, drv->name); 62 } 63 /*通知用户空间???*/ 64 kobject_uevent(&priv->kobj, KOBJ_ADD); 65 return 0; 66 out_unregister: 67 kfree(drv->p); 68 drv->p = NULL; 69 kobject_put(&priv->kobj); 70 out_put_bus: 71 bus_put(bus); 72 return error; 73 }
7.6.1
下列代码位于lib/kobject.c。
1 /** 2 * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy 3 * @kobj: pointer to the kobject to initialize 4 * @ktype: pointer to the ktype for this kobject. 5 * @parent: pointer to the parent of this kobject. 6 * @fmt: the name of the kobject. 7 * 8 * This function combines the call to kobject_init() and 9 * kobject_add(). The same type of error handling after a call to 10 * kobject_add() and kobject lifetime rules are the same here. 11 */ 12 int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, 13 struct kobject *parent, const char *fmt, ...) 14 { 15 va_list args; 16 int retval; 17 18 kobject_init(kobj, ktype); 19 20 va_start(args, fmt); 21 retval = kobject_add_varg(kobj, parent, fmt, args); 22 va_end(args); 23 24 return retval; 25 }
该函数中调用了两个函数,这两个函数分别在6.1.2和6.2.2中讲述过,这里不再赘述。
调用该函数时由于parent为NULL,但kobj.kset为drivers目录,所以将在/sys/bus/platform/drivers/下建立目录,名为s3c2410-spi。
我们来验证下:
[root@yj423 s3c2410-spi]#pwd
/sys/bus/platform/drivers/s3c2410-spi
接着由于drivers_autoprobe在bus_register执行的时候已经置1,将调用driver_attach。
7.6.2
下列代码位于drivers/base/dd.c。
1 /** 2 * driver_attach - try to bind driver to devices. 3 * @drv: driver. 4 * 5 * Walk the list of devices that the bus has on it and try to 6 * match the driver with each one. If driver_probe_device() 7 * returns 0 and the @dev->driver is set, we've found a 8 * compatible pair. 9 */ 10 int driver_attach(struct device_driver *drv) 11 { 12 return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); 13 }
该函数将调用bus_for_each_dev来寻找总线上的每个设备,这里的总线即为platform总线,然后尝试绑定设备。
这里需要注意的是最后一个参数__driver_attach,这是一个函数名,后面将会调用它。
1 int bus_for_each_dev(struct bus_type *bus, struct device *start, 2 void *data, int (*fn)(struct device *, void *)) 3 { 4 struct klist_iter i; 5 struct device *dev; 6 int error = 0; 7 8 if (!bus) 9 return -EINVAL; 10 11 klist_iter_init_node(&bus->p->klist_devices, &i, 12 (start ? &start->p->knode_bus : NULL)); 13 while ((dev = next_device(&i)) && !error) 14 error = fn(dev, data); 15 klist_iter_exit(&i); 16 return error; 17 }
通过klist将遍历该总线上的所有设备,并为其调用__driver_attach函数。
1 static int __driver_attach(struct device *dev, void *data) 2 { 3 struct device_driver *drv = data; 4 5 /* 6 * Lock device and try to bind to it. We drop the error 7 * here and always return 0, because we need to keep trying 8 * to bind to devices and some drivers will return an error 9 * simply if it didn't support the device. 10 * 11 * driver_probe_device() will spit a warning if there 12 * is an error. 13 */ 14 15 if (!driver_match_device(drv, dev)) 16 return 0; 17 18 if (dev->parent) /* Needed for USB */ 19 down(&dev->parent->sem); 20 down(&dev->sem); 21 if (!dev->driver) 22 driver_probe_device(drv, dev); 23 up(&dev->sem); 24 if (dev->parent) 25 up(&dev->parent->sem); 26 27 return 0; 28 }
首先调用了driver_match_device函数,该函数进会进行匹配,如果匹配成功将返回1。我们看下这个函数:
1 static inline int driver_match_device(struct device_driver *drv, 2 struct device *dev) 3 { 4 return drv->bus->match ? drv->bus->match(dev, drv) : 1; 5 }
这里直接调用了platform总线的match方法,我们来看下这个方法。
1 /** 2 * platform_match - bind platform device to platform driver. 3 * @dev: device. 4 * @drv: driver. 5 * 6 * Platform device IDs are assumed to be encoded like this: 7 * "<name><instance>", where <name> is a short description of the type of 8 * device, like "pci" or "floppy", and <instance> is the enumerated 9 * instance of the device, like '0' or '42'. Driver IDs are simply 10 * "<name>". So, extract the <name> from the platform_device structure, 11 * and compare it against the name of the driver. Return whether they match 12 * or not. 13 */ 14 static int platform_match(struct device *dev, struct device_driver *drv) 15 { 16 struct platform_device *pdev = to_platform_device(dev); 17 struct platform_driver *pdrv = to_platform_driver(drv); 18 19 /* match against the id table first */ 20 if (pdrv->id_table) 21 return platform_match_id(pdrv->id_table, pdev) != NULL; 22 23 /* fall-back to driver name match */ 24 return (strcmp(pdev->name, drv->name) == 0); 25 }
该方法的核心其实就是使用stcmp进行字符匹配,判断pdev->name和drv->name是否相等。
在本例中两者同为s3c2410-spi。因此匹配完成,返回1。
返回后,由于dev->driver为NULL,将调用driver_probe_device函数。我们来看下:
1 int driver_probe_device(struct device_driver *drv, struct device *dev) 2 { 3 int ret = 0; 4 5 if (!device_is_registered(dev)) 6 return -ENODEV; 7 8 pr_debug("bus: '%s': %s: matched device %s with driver %s\n", 9 drv->bus->name, __func__, dev_name(dev), drv->name); 10 11 ret = really_probe(dev, drv); 12 13 return ret; 14 } 15 static inline int device_is_registered(struct device *dev) 16 { 17 return dev->kobj.state_in_sysfs; 18 }
该函数将调用really_probe来绑定设备和它的驱动。
1 static int really_probe(struct device *dev, struct device_driver *drv) 2 { 3 int ret = 0; 4 5 atomic_inc(&probe_count); 6 pr_debug("bus: '%s': %s: probing driver %s with device %s\n", 7 drv->bus->name, __func__, drv->name, dev_name(dev)); 8 WARN_ON(!list_empty(&dev->devres_head)); 9 10 dev->driver = drv; 11 if (driver_sysfs_add(dev)) { /*创建两个symlink,更新sysfs*/ 12 printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", 13 __func__, dev_name(dev)); 14 goto probe_failed; 15 } 16 17 if (dev->bus->probe) { 18 ret = dev->bus->probe(dev);/*调用总线的probe方法*/ 19 if (ret) 20 goto probe_failed; 21 } else if (drv->probe) { 22 ret = drv->probe(dev); /*调用驱动的probe方法*/ 23 if (ret) 24 goto probe_failed; 25 } 26 27 driver_bound(dev); /*绑定设备和驱动*/ 28 ret = 1; 29 pr_debug("bus: '%s': %s: bound device %s to driver %s\n", 30 drv->bus->name, __func__, dev_name(dev), drv->name); 31 goto done; 32 33 probe_failed: 34 devres_release_all(dev); 35 driver_sysfs_remove(dev); 36 dev->driver = NULL; 37 38 if (ret != -ENODEV && ret != -ENXIO) { 39 /* driver matched but the probe failed */ 40 printk(KERN_WARNING 41 "%s: probe of %s failed with error %d\n", 42 drv->name, dev_name(dev), ret); 43 } 44 /* 45 * Ignore errors returned by ->probe so that the next driver can try 46 * its luck. 47 */ 48 ret = 0; 49 done: 50 atomic_dec(&probe_count); 51 wake_up(&probe_waitqueue); 52 return ret; 53 }
在这个函数中调用4个函数。
第一个函数driver_sysfs_add将更新sysfs。
1 static int driver_sysfs_add(struct device *dev) 2 { 3 int ret; 4 /* 在/sys/bus/XXX/drivers/XXX目录下建立symlink,链接名为kobj->name, 5 链接指向/sys/devices/platform/XXX */ 6 ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj, 7 kobject_name(&dev->kobj)); 8 if (ret == 0) { 9 /* 在/sys/devices/platform/XXX/下建立symlink,链接名为driver, 10 指向/sys/bus/xxx/drivers目录下的某个目录*/ 11 ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj, 12 "driver"); 13 if (ret) 14 sysfs_remove_link(&dev->driver->p->kobj, 15 kobject_name(&dev->kobj)); 16 } 17 return ret; 18 }
执行完以后,建立了两个链接。
在/sys/bus/platform/drivers/s3c2410-spi下建立链接,指向/sys/devices/platform/s3c2410-spi.0
在/sys/devices/platform/s3c2410-spi.0下建立链接,指向/sys/devices/platform/s3c2410-spi.0。
这样就在用户空间呈现出驱动和设备的关系了。我们来验证下。
[root@yj423 s3c2410-spi]#pwd /sys/bus/platform/drivers/s3c2410-spi [root@yj423 s3c2410-spi]#ll s3c2410-spi.0 lrwxrwxrwx 1 root root 0 Jan 1 02:28 s3c2410-spi.0 -> ../../../../devices/platform/s3c2410-spi.0 [root@yj423 s3c2410-spi.0]#pwd /sys/devices/platform/s3c2410-spi.0 [root@yj423 s3c2410-spi.0]#ll driver lrwxrwxrwx 1 root root 0 Jan 1 02:26 driver -> ../../../bus/platform/drivers/s3c2410-spi
第2个函数执行总线的probe方法,由于platform总线没有提供probe方法,因此不执行。
第3个函数执行驱动的probe方法,驱动提供了probe,因此调用它,该函数的细节超过了本文的讨论内容,所以略过。
第4个函数执行driver_bound,用来绑定设备和驱动,来看下这个函数。
1 static void driver_bound(struct device *dev) 2 { 3 if (klist_node_attached(&dev->p->knode_driver)) { 4 printk(KERN_WARNING "%s: device %s already bound\n", 5 __func__, kobject_name(&dev->kobj)); 6 return; 7 } 8 9 pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev), 10 __func__, dev->driver->name); 11 12 if (dev->bus) 13 blocking_notifier_call_chain(&dev->bus->p->bus_notifier, 14 BUS_NOTIFY_BOUND_DRIVER, dev); 15 16 klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); 17 }
其实,所谓的绑定,就是将设备的驱动节点添加到驱动支持的设备链表中。
至此,通过内核链表,这个platform device 和platform driver 已经绑定完成,将继续遍历内核链表尝试匹配和绑定,直到链表结束。
在driver_attach执行完毕以后,bus_add_driver函数还有些剩余工作要完成。
首先,将驱动添加到总线的驱动列表中。
接着,如果定义了驱动属性文件,则创建。
最后,在/sys/bus/platform/drivers/s3c2410-spi/下建立属性文件uevent,并在同一目录下建立文件bind和unbind。
我们来验证下:
[root@yj423 s3c2410-spi]#pwd /sys/bus/platform/drivers/s3c2410-spi [root@yj423 s3c2410-spi]#ls bind s3c2410-spi.0 uevent unbind
7.7小结
在本节中,我们看到了platform driver是如何注册到内核中,在注册过程中,通过更新了sysfs,向用户空间展示总线,设备和驱动之间的关系。
同时,还更新了链表的指向,在内核中体现了同样的关系。
最后以platform driver的注册过程总结如下:
参考博文:https://blog.csdn.net/yj4231/java/article/details/7799245