在Linux系统中,设备驱动模型是一种非常重要的模型。它负责管理设备驱动程序并向用户空间提供接口。在本文中,我们将深入探讨Linux设备驱动模型-Device的实现原理和相关技术。
前言
Linux将所有的设备统一抽象为struct device结构, 同时将所有的驱动统一抽象为struct device_driver结构。这样设计之后就方便驱动开发工程师编写驱动,只需要将具体的设备包含struct device结构,具体的驱动包含struct device_driver结构。最终会调用device_register和driver_register将驱动和设备注册到系统,表现出来就是在sys目录的device和driver目录下。本小节先分析device结构,以及相关API,以及如何注册到系统中,以及提供给上层的sys接口。
数据结构
Linux将所有的设备统一抽象为struct device结构。定义在
1. struct device {
2. struct device *parent;
3.
4. struct device_private *p;
5.
6. struct kobject kobj;
7. const char *init_name; /* initial name of the device */
8. const struct device_type *type;
9.
10. struct mutex mutex; /* mutex to synchronize calls to
11. \* its driver.
12. */
13.
14. struct bus_type *bus; /* type of bus device is on */
15. struct device_driver *driver; /* which driver has allocated this
16. device */
17. void *platform_data; /* Platform specific data, device
18. core doesn't touch it */
19. void *driver_data; /* Driver data, set and get with
20. dev_set/get_drvdata */
21. struct dev_pm_info power;
22. struct dev_pm_domain *pm_domain;
23.
24. \#ifdef CONFIG_PINCTRL
25. struct dev_pin_info *pins;
26. \#endif
27.
28. \#ifdef CONFIG_NUMA
29. int numa_node; /* NUMA node this device is close to */
30. \#endif
31. u64 *dma_mask; /* dma mask (if dma'able device) */
32. u64 coherent_dma_mask;/* Like dma_mask, but for
33. alloc_coherent mappings as
34. not all hardware supports
35. 64 bit addresses for consistent
36. allocations such descriptors. */
37. unsigned long dma_pfn_offset;
38.
39. struct device_dma_parameters *dma_parms;
40.
41. struct list_head dma_pools; /* dma pools (if dma'ble) */
42.
43. struct dma_coherent_mem *dma_mem; /* internal for coherent mem
44. override */
45. \#ifdef CONFIG_DMA_CMA
46. struct cma *cma_area; /* contiguous memory area for dma
47. allocations */
48. \#endif
49. /* arch specific additions */
50. struct dev_archdata archdata;
51.
52. struct device_node *of_node; /* associated device tree node */
53. struct acpi_dev_node acpi_node; /* associated ACPI device node */
54.
55. dev_t devt; /* dev_t, creates the sysfs "dev" */
56. u32 id; /* device instance */
57.
58. spinlock_t devres_lock;
59. struct list_head devres_head;
60.
61. struct klist_node knode_class;
62. struct class *class;
63. const struct attribute_group **groups; /* optional groups */
64.
65. void (*release)(struct device *dev);
66. struct iommu_group *iommu_group;
67.
68. bool offline_disabled:1;
69. bool offline:1;
70. };
device结构体比较复杂,同时其中还包含了dma, numa, pintrl, pm相关的知识,本小节不做过多谈论。不过该结构体的注释是相当的详细,可以参考。
parent: 代表设备的parent节点,通常parent是bus或者controller。 如果此parent为NULL,则此设备就是顶层设备。
p: 代表device的私有数据的指针,指针类型为device_private。
kobj: kobject结构,用于层级关系。
init_name: 设备对象的名称,出现在sys目录下。
type: 指向device_type结构,代表了设备的特殊的信息。
mutex: 同步操作。
bus: 设备所属的总线。
driver: 设备所对应的驱动。
platfrorm_data: 设备所对应的平台数据。
driver_data: 保存设备所对于驱动的数据,一般使用get/set函数。
power/pm_domain: 电源管理相关的内容。
of_node: 该设备对应的设备树结构。
devt: 设备号,由主设备号和次设备号组成。
id: 设备索引号。
devres_head: 设备资源管理的链表。用于将设备使用的资源用链表管理。
class: 设备所属的class。
group: 设备默认的属性。
设备相关函数
-
devices_init
此函数主要是初始化devices_kset,以及初始化所有的dev_kset
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. }
内核中每一个设备都是struct device结构,同时内核将所有的设备使用devices_kset管理。同时为了统一方便管理又将设备分为block和char设备,生成的内核对象分别为sysfs_dev_block_kobj和sysfs_dev_char_kobj。当然这两个内核模块都是在dev内核模块之下的。
这个函数的操作实现最后表现到sys文件目录下,分别为/sys/devices, /sys/dev, /sys/dev/char, /sys/dev/block。
-
device_initialize(用于初始化一个device)
1. void device_initialize(struct device *dev)
2. {
3. dev->kobj.kset = devices_kset;
4. kobject_init(&dev->kobj, &device_ktype);
5. INIT_LIST_HEAD(&dev->dma_pools);
6. mutex_init(&dev->mutex);
7. lockdep_set_novalidate_class(&dev->mutex);
8. spin_lock_init(&dev->devres_lock);
9. INIT_LIST_HEAD(&dev->devres_head);
10. device_pm_init(dev);
11. set_dev_node(dev, -1);
12. }
主要是设置设备所属的kset,设置设备所属kset的ktype,初始化设备资源管理的链表,以及设备电源管理初始化。
-
device_add(添加一个设备到系统中)
1. int device_add(struct device *dev)
2. {
3. struct device *parent = NULL;
4. struct kobject *kobj;
5. struct class_interface *class_intf;
6. int error = -EINVAL;
7.
8. dev = get_device(dev); //增加此设备的引用计数
9. if (!dev)
10. goto done;
11.
12. if (!dev->p) {
13. error = device_private_init(dev); ---------------A
14. if (error)
15. goto done;
16. }
17.
18. /*
19. \* for statically allocated devices, which should all be converted
20. \* some day, we need to initialize the name. We prevent reading back
21. \* the name, and force the use of dev_name()
22. */
23. if (dev->init_name) {
24. dev_set_name(dev, "%s", dev->init_name);
25. dev->init_name = NULL;
26. }
27.
28. /* subsystems can specify simple device enumeration */
29. if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
30. dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
31.
32. if (!dev_name(dev)) { //设置device的name域,如果设置失败退出。
33. error = -EINVAL;
34. goto name_error;
35. }
36.
37. pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
38.
39. parent = get_device(dev->parent); //增加设备parent的引用计数
40. kobj = get_device_parent(dev, parent); //设置设备parent的内核对象,建立设备在sys下的层次结构。
41. if (kobj)
42. dev->kobj.parent = kobj;
43.
44. /* use parent numa_node */
45. if (parent)
46. set_dev_node(dev, dev_to_node(parent));
47.
48. /* first, register with generic layer. */
49. /* we require the name to be set before, and pass NULL */
50. error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); //调用kobject_add将设备添加到sys中
51. if (error)
52. goto Error;
53.
54. /* notify platform of device entry */
55. if (platform_notify)
56. platform_notify(dev);
57.
58. error = device_create_file(dev, &dev_attr_uevent); //创建设备的uevent属性
59. if (error)
60. goto attrError;
61.
62. if (MAJOR(dev->devt)) {
63. error = device_create_file(dev, &dev_attr_dev); //如果主设备号存在,创建dev属性
64. if (error)
65. goto ueventattrError;
66.
67. error = device_create_sys_dev_entry(dev);
68. if (error)
69. goto devtattrError;
70.
71. devtmpfs_create_node(dev); //调用devtmpfs,自动创建设备节点
72. }
73.
74. error = device_add_class_symlinks(dev); //创建链接文件
75. if (error)
76. goto SymlinkError;
77. error = device_add_attrs(dev); //添加设备的属性
78. if (error)
79. goto AttrsError;
80. error = bus_add_device(dev); //将此设备添加到一条总线上
81. if (error)
82. goto BusError;
83. error = dpm_sysfs_add(dev);
84. if (error)
85. goto DPMError;
86. device_pm_add(dev); //将设备添加到pm core链表中,会在suspend中使用到
87.
88. /* Notify clients of device addition. This call must come
89. \* after dpm_sysfs_add() and before kobject_uevent().
90. */
91. if (dev->bus)
92. blocking_notifier_call_chain(&dev->bus->p->bus_notifier, //使用通知链同时有新设备添加到bus中
93. BUS_NOTIFY_ADD_DEVICE, dev);
94.
95. kobject_uevent(&dev->kobj, KOBJ_ADD); //使用uevent通知上层,这时候就会使用到device_uevent_ops中的函数
96. bus_probe_device(dev); //匹配bus下的设备与驱动
97. if (parent)
98. klist_add_tail(&dev->p->knode_parent, //如果parent存在,将dev添加到parent链表中
99. &parent->p->klist_children);
100.
101. if (dev->class) { //如果class存在,则将dev添加到class链表中
102. mutex_lock(&dev->class->p->mutex);
103. /* tie the class to the device */
104. klist_add_tail(&dev->knode_class,
105. &dev->class->p->klist_devices);
106.
107. /* notify any interfaces that the device is here */
108. list_for_each_entry(class_intf,
109. &dev->class->p->interfaces, node)
110. if (class_intf->add_dev)
111. class_intf->add_dev(dev, class_intf);
112. mutex_unlock(&dev->class->p->mutex);
113. }
114. }
A: 如果设备的device_private不存在,就重现分配一个
-
1. int device_private_init(struct device *dev) 2. { 3. dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL); //分配一个device_private结构 4. if (!dev->p) 5. return -ENOMEM; 6. dev->p->device = dev; //设置device_private的device结构 7. klist_init(&dev->p->klist_children, klist_children_get, 8. klist_children_put); //初始化设备下所有children链表 9. INIT_LIST_HEAD(&dev->p->deferred_probe); 10. return 0; 11. }
B: 主要分析下如何将设备添加到一条总线上,这是核心。
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. error = device_add_groups(dev, bus->dev_groups);
12. if (error)
13. goto out_groups;
14. error = sysfs_create_link(&bus->p->devices_kset->kobj,
15. &dev->kobj, dev_name(dev));
16. if (error)
17. goto out_id;
18. error = sysfs_create_link(&dev->kobj,
19. &dev->bus->p->subsys.kobj, "subsystem");
20. if (error)
21. goto out_subsys;
22. klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
23. }
24. return 0;
25. }
如果此设备不属于任何总线,直接就返回,否则添加设备属性,创建链接文件,将此bus下的设备添加到klist_devices链表中。
C: 关于总线先设备与驱动的匹配是设备驱动模型的核心
1. void bus_probe_device(struct device *dev)
2. {
3. struct bus_type *bus = dev->bus;
4. struct subsys_interface *sif;
5. int ret;
6.
7. if (!bus) //如果不存在总线,直接返回
8. return;
9.
10. if (bus->p->drivers_autoprobe) { //是否总线支持自动匹配
11. ret = device_attach(dev);
12. WARN_ON(ret p->mutex);
16. list_for_each_entry(sif, &bus->p->interfaces, node)
17. if (sif->add_dev)
18. sif->add_dev(dev, sif);
19. mutex_unlock(&bus->p->mutex);
20. }
一般总线都会支持自动匹配,当然可以修改driver_autoprobe的值,然后调用device_attach进行匹配。
1. int device_attach(struct device *dev)
2. {
3. int ret = 0;
4.
5. device_lock(dev); //因为此操作只能运行一个dev进行,需要互斥保护。
6. if (dev->driver) { //如果设备存在对应的driver,说明已经进行了匹配,只需要调用device_bind_driver在sys中建立关系。
7. if (klist_node_attached(&dev->p->knode_driver)) {
8. ret = 1;
9. goto out_unlock;
10. }
11. ret = device_bind_driver(dev);
12. if (ret == 0)
13. ret = 1;
14. else {
15. dev->driver = NULL;
16. ret = 0;
17. }
18. } else {
19. ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); //如果driver不存在,则需要遍历dev所在bus下的所有driver
20. pm_request_idle(dev);
21. }
22. out_unlock:
23. device_unlock(dev);
24. return ret;
25.
}
当遍历driver下过程中,最终会调用到__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. }
函数match最终使用的匹配是

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. }
根据bus是否存在match函数,如果存在调用bus的match函数,如果不存在,直接返回1。如果匹配成功之后就会调用driver_probe_device进行绑定,最终会调用really_probe函数。
1. static int really_probe(struct device *dev, struct device_driver *drv)
2. {
3. int ret = 0;
4. int local_trigger_count = atomic_read(&deferred_trigger_count);
5.
6. atomic_inc(&probe_count);
7. pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
8. drv->bus->name, __func__, drv->name, dev_name(dev));
9. WARN_ON(!list_empty(&dev->devres_head));
10.
11. dev->driver = drv; //设置设备的driver变量
12.
13. /* If using pinctrl, bind pins now before probing */
14. ret = pinctrl_bind_pins(dev);
15. if (ret)
16. goto probe_failed;
17.
18. if (driver_sysfs_add(dev)) {
19. printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
20. __func__, dev_name(dev));
21. goto probe_failed;
22. }
23.
24. if (dev->bus->probe) {
25. ret = dev->bus->probe(dev); //如果bus的probe存在,则调用bus的probe函数
26. if (ret)
27. goto probe_failed;
28. } else if (drv->probe) { //否则调用驱动的probe,这下知道驱动的probe函数是如何调用的。
29. ret = drv->probe(dev);
30. if (ret)
31. goto probe_failed;
32. }
33.
34. driver_bound(dev); //将设备所属的驱动加入到驱动链表中,使用通知链发出BOUND信息。
35. ret = 1;
36. pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
37. drv->bus->name, __func__, dev_name(dev), drv->name);
38. goto done;
39.
40. probe_failed: //probe失败之后,就会清空设备资源,设备driver设置为NULL,从sys中移除dev建立的driver链接。
41. devres_release_all(dev);
42. driver_sysfs_remove(dev);
43. dev->driver = NULL;
44. dev_set_drvdata(dev, NULL);
45.
46. if (ret == -EPROBE_DEFER) {
47. /* Driver requested deferred probing */
48. dev_info(dev, "Driver %s requests probe deferral\n", drv->name);
49. driver_deferred_probe_add(dev);
50. /* Did a trigger occur while probing? Need to re-trigger if yes */
51. if (local_trigger_count != atomic_read(&deferred_trigger_count))
52. driver_deferred_probe_trigger();
53. } else if (ret != -ENODEV && ret != -ENXIO) {
54. /* driver matched but the probe failed */
55. printk(KERN_WARNING
56. "%s: probe of %s failed with error %d\n",
57. drv->name, dev_name(dev), ret);
58. } else {
59. pr_debug("%s: probe of %s rejects match %d\n",
60. drv->name, dev_name(dev), ret);
61. }
62. /*
63. \* Ignore errors returned by ->probe so that the next driver can try
64. \* its luck.
65. */
66. ret = 0;
67. }
-
device_register(注册一个设备到系统中是上述两个函数的结合体) -
device_unregister(将一个设备从系统中注销)
1. void device_unregister(struct device *dev)
2. {
3. pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
4. device_del(dev);
5. put_device(dev);
6. }
此函数分为两步走,第一步从系统中移除此设备,第二步将设备的引用计数减去1。主要的注销操作在device_del中实现,也就是register的反操作。
1. void device_del(struct device *dev)
2. {
3. struct device *parent = dev->parent;
4. struct class_interface *class_intf;
5.
6. /* Notify clients of device removal. This call must come
7. \* before dpm_sysfs_remove().
8. */
9. if (dev->bus) //如果bus总线存在,调用通知链删除设备
10. blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
11. BUS_NOTIFY_DEL_DEVICE, dev);
12. dpm_sysfs_remove(dev); //电源管理函数,关掉设备电源
13. if (parent) //如果有父设备,将当前设备从父设备所属链表删除
14. klist_del(&dev->p->knode_parent);
15. if (MAJOR(dev->devt)) { //如果主设备号存在, 动态删除设备节点,移除设备的属性
16. devtmpfs_delete_node(dev);
17. device_remove_sys_dev_entry(dev);
18. device_remove_file(dev, &dev_attr_dev);
19. }
20. if (dev->class) { //如果设备所属的class存在,移除设备的链接,调用remove_dev做移除,从class链表中删除该设备
21. device_remove_class_symlinks(dev);
22.
23. mutex_lock(&dev->class->p->mutex);
24. /* notify any interfaces that the device is now gone */
25. list_for_each_entry(class_intf,
26. &dev->class->p->interfaces, node)
27. if (class_intf->remove_dev)
28. class_intf->remove_dev(dev, class_intf);
29. /* remove the device from the class list */
30. klist_del(&dev->knode_class);
31. mutex_unlock(&dev->class->p->mutex);
32. }
33. device_remove_file(dev, &dev_attr_uevent); //移除设备的uevent属性
34. device_remove_attrs(dev);
35. bus_remove_device(dev); //从总线中移除设备,以及电源管理等。
36. device_pm_remove(dev);
37. driver_deferred_probe_del(dev);
38.
39. /* Notify the platform of the removal, in case they
40. \* need to do anything...
41. */
42. if (platform_notify_remove)
43. platform_notify_remove(dev);
44. if (dev->bus)
45. blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
46. BUS_NOTIFY_REMOVED_DEVICE, dev);
47. kobject_uevent(&dev->kobj, KOBJ_REMOVE);
48. cleanup_device_parent(dev);
49. kobject_del(&dev->kobj); //删除设备的内核模块
50. put_device(parent);
51. }
设备属性
linux中使用device_attribute结构体表示一个设备的属性
1. struct device_attribute {
2. struct attribute attr;
3. ssize_t (*show)(struct device *dev, struct device_attribute *attr,
4. char *buf);
5. ssize_t (*store)(struct device *dev, struct device_attribute *attr,
6. const char *buf, size_t count);
7. };
通常使用to_dev_attr宏定义得到device_attribute对象
\#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
同时为了方便定义设备的属性,内核提供了一系列相关的宏定义,用于初始化设备的属性。
1. \#define DEVICE_ATTR(_name, _mode, _show, _store) \
2. struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
3. \#define DEVICE_ATTR_RW(_name) \
4. struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
5. \#define DEVICE_ATTR_RO(_name) \
6. struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
7. \#define DEVICE_ATTR_WO(_name) \
8. struct device_attribute dev_attr_##_name = __ATTR_WO(_name)
关于设备属性的调用过程,最终会调用到设备的show和store函数中,具体的流程分析可见Linux设备驱动模型-Ktype
-
1. static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, 2. char *buf) 3. { 4. struct device_attribute *dev_attr = to_dev_attr(attr); 5. struct device *dev = kobj_to_dev(kobj); 6. ssize_t ret = -EIO; 7. 8. if (dev_attr->show) 9. ret = dev_attr->show(dev, dev_attr, buf); 10. if (ret >= (ssize_t)PAGE_SIZE) { 11. print_symbol("dev_attr_show: %s returned bad count\n", 12. (unsigned long)dev_attr->show); 13. } 14. return ret; 15. } 16. 17. static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr, 18. const char *buf, size_t count) 19. { 20. struct device_attribute *dev_attr = to_dev_attr(attr); 21. struct device *dev = kobj_to_dev(kobj); 22. ssize_t ret = -EIO; 23. 24. if (dev_attr->store) 25. ret = dev_attr->store(dev, dev_attr, buf, count); 26. return ret; 27. } 28. 29. static const struct sysfs_ops dev_sysfs_ops = { 30. .show = dev_attr_show, 31. .store = dev_attr_store, 32. };
设备类型
在include/linux/device.h文件存在这样的结构体:
1. struct device_type {
2. const char *name;
3. const struct attribute_group **groups;
4. int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
5. char *(*devnode)(struct device *dev, umode_t *mode,
6. kuid_t *uid, kgid_t *gid);
7. void (*release)(struct device *dev);
8.
9. const struct dev_pm_ops *pm;
10. };
此结构代表设备类型。通常一个Bus下会存在各种设备的,比如:disks, mouse, event等。而此结构就表明此设备是何种类型的设备。
name: 代表设备的名称,在上报event的时候,会通过DEVTYPE设置设备的类型名称。
groups: 代码设备的属性,在添加设备的属性的时候,如果存在设备类型,也会添加设备类型的属性。
uevent: 在新增一个设备时,通过该函数上报设备类型的uevent。
devnode: 在创建一个设备节点的时候会使用到,获取设备的信息。
release: 在设备释放时候,如果存在设备类型会调用到。
总之,设备驱动模型是Linux系统中不可或缺的一部分。它负责管理设备驱动程序并向用户空间提供接口。希望本文能够帮助读者更好地理解Linux设备驱动模型-Device的实现原理和相关技术。
以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !