上篇主要对总线进行了一些梳理,现在来看看内核对于设备的处理过程。
- struct device {
- struct device *parent; /*指向父节点的指针*/
- struct device_private *p;
- struct kobject kobj;/*用它联系到sysfs中*/
- const char *init_name; /*初始化设备的名字 */
- const struct device_type *type;/*device属性文件*/
- struct mutex mutex; /* mutex to synchronize calls to
- * its driver.
- */
- struct bus_type *bus; /* 属于那条总线 */
- struct device_driver *driver; /*那个驱动能匹配该设备*/
- void *platform_data;
- struct dev_pm_info power;
- struct dev_pm_domain *pm_domain;
- #ifdef CONFIG_NUMA
- int numa_node; /* NUMA node this device is close to */
- #endif
- u64 *dma_mask; /* dma mask (if dma'able device) */
- u64 coherent_dma_mask;/* Like dma_mask, but for
- alloc_coherent mappings as
- not all hardware supports
- 64 bit addresses for consistent
- allocations such descriptors. */
- struct device_dma_parameters *dma_parms;
- struct list_head dma_pools; /* dma pools (if dma'ble) */
- struct dma_coherent_mem *dma_mem; /* internal for coherent mem
- override */
- /* arch specific additions */
- struct dev_archdata archdata;
- struct device_node *of_node; /* associated device tree node */
- dev_t devt; /* dev_t, creates the sysfs "dev" */
- spinlock_t devres_lock;
- struct list_head devres_head;
- struct klist_node knode_class;
- struct class *class;
- const struct attribute_group **groups; /* optional groups */
- void (*release)(struct device *dev);
- };
来看看strcut device的结构变量,首先是指向父节点的指针parent,kobj是内嵌在device中的kbojcet,匹配的驱动,属于哪条总线,其中比较重要的是release函数,当这个设备被删去时,内核调用该方法,所有的注册到内核的设备结构必须有一个release方法。和总线一样
,device也有一个联系bus、device、driver的数据结构。- struct device_private {
- struct klist klist_children;//子设备的链表
- struct klist_node knode_parent;//连接到父设备的klist_childern时所用的节点
- struct klist_node knode_driver;//连接到驱动的设备时用到得节点
- struct klist_node knode_bus;//连接到总线时所用的节点
- void *driver_data; //用于在设备结构中存放相关的驱动信息
- struct device *device; //指向属于有的device
- };
- #define to_device_private_parent(obj) \
- container_of(obj, struct device_private, knode_parent)//从父设备的klist_children上节点,获取相应的private
- #define to_device_private_driver(obj) \
- container_of(obj, struct device_private, knode_driver)//从驱动链表上获得private
- #define to_device_private_bus(obj) \
- container_of(obj, struct device_private, knode_bus) //从总线上获得private
从数据结构上看出,device也有和bus总线一样的。
- /* interface for exporting device attributes */
- struct device_attribute {
- struct attribute attr;
- ssize_t (*show)(struct device *dev, struct device_attribute *attr,
- char *buf);
- ssize_t (*store)(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count);
- };
- #define DEVICE_ATTR(_name, _mode, _show, _store) \
- struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
sysf中的设备入口的属性结构
- struct device_type {
- const char *name;
- const struct attribute_group **groups;
- int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
- char *(*devnode)(struct device *dev, mode_t *mode);
- void (*release)(struct device *dev);
- const struct dev_pm_ops *pm;
- };
同bus过程一样,device的初始化也是在driver_init完成初始化的
- int __init devices_init(void)
- {
- devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
- if (!devices_kset)
- return -ENOMEM;
- dev_kobj = kobject_create_and_add("dev", NULL);
- if (!dev_kobj)
- goto dev_kobj_err;
- sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
- if (!sysfs_dev_block_kobj)
- goto block_kobj_err;
- sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
- if (!sysfs_dev_char_kobj)
- goto char_kobj_err;
- return 0;
- char_kobj_err:
- kobject_put(sysfs_dev_block_kobj);
- block_kobj_err:
- kobject_put(dev_kobj);
- dev_kobj_err:
- kset_unregister(devices_kset);
- return -ENOMEM;
- }
这个函数干的事情很明显,就是创建在sysfs中的devices目录和dev目录,还在dev目录下创建了block和char两个子目录。
- static const struct kset_uevent_ops device_uevent_ops = {
- .filter = dev_uevent_filter,
- .name = dev_uevent_name,
- .uevent = dev_uevent,
- };
这部分用于向用户控件传递event事件。
下面来分析下设备的注册过程
- int device_register(struct device *dev)
- {
- device_initialize(dev);
- return device_add(dev);
- }
device_register是提供给外界注册设备的接口,主要调用device_initialize完成dev结构的初始化,然后调用device_add将其加入到系统中。
- void device_initialize(struct device *dev)
- {
- dev->kobj.kset = devices_kset;
- kobject_init(&dev->kobj, &device_ktype);
- INIT_LIST_HEAD(&dev->dma_pools);
- mutex_init(&dev->mutex);
- lockdep_set_novalidate_class(&dev->mutex);
- spin_lock_init(&dev->devres_lock);
- INIT_LIST_HEAD(&dev->devres_head);
- device_pm_init(dev);
- set_dev_node(dev, -1);
- }
主要是完成device结构的初始化,把device中能初始化的部分全部初始化。主要是将device_ktype加入到kobject中。
- int device_add(struct device *dev)
- {
- struct device *parent = NULL;
- struct class_interface *class_intf;
- int error = -EINVAL;
- dev = get_device(dev); //增加dev的引用计数
- if (!dev)
- goto done;
- if (!dev->p) {
- error = device_private_init(dev);//分配和初始化dev->p
- if (error)
- goto done;
- }
- /*
- * for statically allocated devices, which should all be converted
- * some day, we need to initialize the name. We prevent reading back
- * the name, and force the use of dev_name()
- */
- if (dev->init_name) {
- dev_set_name(dev, "%s", dev->init_name);//设置dev名字
- dev->init_name = NULL;
- }
- if (!dev_name(dev)) {
- error = -EINVAL;
- goto name_error;
- }
- pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
- parent = get_device(dev->parent);
- setup_parent(dev, parent);
- /* use parent numa_node */
- if (parent)
- set_dev_node(dev, dev_to_node(parent));
- /* first, register with generic layer. */
- /* we require the name to be set before, and pass NULL */
- error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); //将dev加入sysfs
- if (error)
- goto Error;
- /* notify platform of device entry */
- if (platform_notify)
- platform_notify(dev);
- error = device_create_file(dev, &uevent_attr);//创建uevent属性文件
- if (error)
- goto attrError;
- if (MAJOR(dev->devt)) {
- error = device_create_file(dev, &devt_attr);//如果没有设备号,就创建dev属性
- if (error)
- goto ueventattrError;
- error = device_create_sys_dev_entry(dev); //创建软链接
- if (error)
- goto devtattrError;
- devtmpfs_create_node(dev);//在dev下创建设备文件
- }
- error = device_add_class_symlinks(dev);//dev与class创建软链接
- if (error)
- goto SymlinkError;
- error = device_add_attrs(dev); //添加device/type/class定义的属性集合
- if (error)
- goto AttrsError;
- error = bus_add_device(dev);
- if (error)
- goto BusError;
- error = dpm_sysfs_add(dev);
- if (error)
- goto DPMError;
- device_pm_add(dev);
- /* Notify clients of device addition. This call must come
- * after dpm_sysf_add() and before kobject_uevent().
- */
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_ADD_DEVICE, dev);
- kobject_uevent(&dev->kobj, KOBJ_ADD);//
- bus_probe_device(dev);
- if (parent)
- klist_add_tail(&dev->p->knode_parent,
- &parent->p->klist_children);
- if (dev->class) {
- mutex_lock(&dev->class->p->class_mutex);
- /* tie the class to the device */
- klist_add_tail(&dev->knode_class,
- &dev->class->p->klist_devices);
- /* notify any interfaces that the device is here */
- list_for_each_entry(class_intf,
- &dev->class->p->class_interfaces, node)
- if (class_intf->add_dev)
- class_intf->add_dev(dev, class_intf);
- mutex_unlock(&dev->class->p->class_mutex);
- }
- done:
- put_device(dev);
- return error;
- DPMError:
- bus_remove_device(dev);
- BusError:
- device_remove_attrs(dev);
- AttrsError:
- device_remove_class_symlinks(dev);
- SymlinkError:
- if (MAJOR(dev->devt))
- devtmpfs_delete_node(dev);
- if (MAJOR(dev->devt))
- device_remove_sys_dev_entry(dev);
- devtattrError:
- if (MAJOR(dev->devt))
- device_remove_file(dev, &devt_attr);
- ueventattrError:
- device_remove_file(dev, &uevent_attr);
- attrError:
- kobject_uevent(&dev->kobj, KOBJ_REMOVE);
- kobject_del(&dev->kobj);
- Error:
- cleanup_device_parent(dev);
- if (parent)
- put_device(parent);
- name_error:
- kfree(dev->p);
- dev->p = NULL;
- goto done;
- }
创建属性文件
- int device_create_file(struct device *dev,
- const struct device_attribute *attr)
- {
- int error = 0;
- if (dev)
- error = sysfs_create_file(&dev->kobj, &attr->attr);
- return error;
- }
从上面可以看出
error = device_create_file(dev, &uevent_attr)创建了uevent的属性。调用error = device_create_file(dev, &devt_attr);如果dev没有被分配设备号,添加dev属性,之后会调用device_create_sys_dev_entry在/sys/dev下添加相应的软链接,最后在调用devtmpfs_create_node在/dev下创建相应的设备文件。
- static int device_add_class_symlinks(struct device *dev)
- {
- int error;
- if (!dev->class)
- return 0;
- error = sysfs_create_link(&dev->kobj,
- &dev->class->p->subsys.kobj,
- "subsystem");
- if (error)
- goto out;
- if (dev->parent && device_is_not_partition(dev)) {
- error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
- "device");
- if (error)
- goto out_subsys;
- }
- #ifdef CONFIG_BLOCK
- /* /sys/block has directories and does not need symlinks */
- if (sysfs_deprecated && dev->class == &block_class)
- return 0;
- #endif
- /* link in the class directory pointing to the device */
- error = sysfs_create_link(&dev->class->p->subsys.kobj,
- &dev->kobj, dev_name(dev));
- if (error)
- goto out_device;
- return 0;
- out_device:
- sysfs_remove_link(&dev->kobj, "device");
- out_subsys:
- sysfs_remove_link(&dev->kobj, "subsystem");
- out:
- return error;
- }
这个函数主要是添加dev与class间的软链接,下面来看看重点的,将dev挂入到bus的设备链表中。
- 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));
- error = device_add_attrs(bus, dev);
- if (error)
- goto out_put;
- error = sysfs_create_link(&bus->p->devices_kset->kobj,
- &dev->kobj, dev_name(dev));
- if (error)
- goto out_id;
- error = sysfs_create_link(&dev->kobj,
- &dev->bus->p->subsys.kobj, "subsystem");//创建bus与dev的软链接
- 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;
- }
可以看出,其只做了一个添加dev与bus间的软链接,并将dev挂到bus的设备链表中。之后dpm_sysfs_add增加dev下的power属性,
在将dev加入到dpm_list的链表中。
- void device_pm_add(struct device *dev)
- {
- pr_debug("PM: Adding info for %s:%s\n",
- dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
- mutex_lock(&dpm_list_mtx);
- if (dev->parent && dev->parent->power.is_prepared)
- dev_warn(dev, "parent %s should not be sleeping\n",
- dev_name(dev->parent));
- list_add_tail(&dev->power.entry, &dpm_list);
- dev_pm_qos_constraints_init(dev);
- mutex_unlock(&dpm_list_mtx);
- }
kobject_uevent(&dev->kobj, KOBJ_ADD)会做kobject_uevent发布KOBJ_ADD消息,最后会进入中要的函数bus_probe_device(dev),为dev寻找合适的驱动。
- void bus_probe_device(struct device *dev)
- {
- struct bus_type *bus = dev->bus;
- int ret;
- if (bus && bus->p->drivers_autoprobe) {
- ret = device_attach(dev);
- WARN_ON(ret < 0);
- }
- }
- int device_attach(struct device *dev)
- {
- int ret = 0;
- device_lock(dev);
- if (dev->driver) {
- if (klist_node_attached(&dev->p->knode_driver)) {
- ret = 1;
- goto out_unlock;
- }
- ret = device_bind_driver(dev);
- if (ret == 0)
- ret = 1;
- else {
- dev->driver = NULL;
- ret = 0;
- }
- } else {
- pm_runtime_get_noresume(dev);
- ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
- pm_runtime_put_sync(dev);
- }
- out_unlock:
- device_unlock(dev);
- return ret;
- }
上面主要的做的是,当device被注册时候,会匹配drv是否有匹配的项,其匹配过程主要是调用__device_attach。
- static int __device_attach(struct device_driver *drv, void *data)
- {
- struct device *dev = data;
- if (!driver_match_device(drv, dev))
- return 0;
- return driver_probe_device(drv, dev);
- }
如果匹配成功,就会调用probe函数。先来看看匹配的过程,主要是调用bus的match函数。
- static inline int driver_match_device(struct device_driver *drv,
- struct device *dev)
- {
- return drv->bus->match ? drv->bus->match(dev, drv) : 1;
- }
匹配成功后,调用probe函数,从下面可以看出如果bus的probe函数存在,调用bus的,如果不存在,则调用dev的probe函数。
- static int really_probe(struct device *dev, struct device_driver *drv)
- {
- int ret = 0;
- atomic_inc(&probe_count);
- pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
- drv->bus->name, __func__, drv->name, dev_name(dev));
- WARN_ON(!list_empty(&dev->devres_head));
- dev->driver = drv;
- if (driver_sysfs_add(dev)) {
- printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
- __func__, dev_name(dev));
- goto probe_failed;
- }
- if (dev->bus->probe) {
- ret = dev->bus->probe(dev);
- if (ret)
- goto probe_failed;
- } else if (drv->probe) {
- ret = drv->probe(dev);
- if (ret)
- goto probe_failed;
- }
- driver_bound(dev);
- ret = 1;
- pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
- goto done;
- probe_failed:
- devres_release_all(dev);
- driver_sysfs_remove(dev);
- dev->driver = NULL;
- if (ret != -ENODEV && ret != -ENXIO) {
- /* driver matched but the probe failed */
- printk(KERN_WARNING
- "%s: probe of %s failed with error %d\n",
- drv->name, dev_name(dev), ret);
- } else {
- pr_debug("%s: probe of %s rejects match %d\n",
- drv->name, dev_name(dev), ret);
- }
- /*
- * Ignore errors returned by ->probe so that the next driver can try
- * its luck.
- */
- ret = 0;
- done:
- atomic_dec(&probe_count);
- wake_up(&probe_waitqueue);
- return ret;
- }
寻找到合适的驱动后,下面做的主要是如果有parent节点,把knode_parent挂入klist_children链表。如果dev有属于的class,也挂入到链表中,上面对device的分析也就完了,下面来看一个例子。
- extern struct device my_bus;
- extern struct bus_type my_bus_type;
- /* Why need this ?*/
- static void my_dev_release(struct device *dev)
- {
-
- }
- struct device my_dev = {
- .bus = &my_bus_type,
- .parent = &my_bus,
- .release = my_dev_release,
- };
- /*
- * Export a simple attribute.
- */
- static ssize_t mydev_show(struct device *dev, char *buf)
- {
- return sprintf(buf, "%s\n", "This is my device!");
- }
- static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL);
- static int __init my_device_init(void)
- {
- int ret = 0;
-
- /* 初始化设备 */
- strncpy(my_dev.bus_id, "my_dev", BUS_ID_SIZE);
-
- /*注册设备*/
- device_register(&my_dev);
-
- /*创建属性文件*/
- device_create_file(&my_dev, &dev_attr_dev);
-
- return ret;
- }
- static void my_device_exit(void)
- {
- device_unregister(&my_dev);
- }
- module_init(my_device_init);
- module_exit(my_device_exit);
阅读(279) | 评论(0) | 转发(0) |