https://cloud.tencent.com/developer/article/1603916
前言
Linux将所有的设备统一抽象为struct device结构, 同时将所有的驱动统一抽象为struct device_driver结构。这样设计之后就方便驱动开发工程师编写驱动,只需要将具体的设备包含struct device结构,具体的驱动包含struct device_driver结构。最终会调用device_register和driver_register将驱动和设备注册到系统,表现出来就是在sys目录的device和driver目录下。本小节先分析device结构,以及相关API,以及如何注册到系统中,以及提供给上层的sys接口。
数据结构
Linux将所有的设备统一抽象为struct device结构。定义在<linux/device.h>
struct device { struct device *parent; struct device_private *p; struct kobject kobj; const char *init_name; /* initial name of the device */ const struct device_type *type; struct mutex mutex; /* mutex to synchronize calls to * its driver. */ struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ void *platform_data; /* Platform specific data, device core doesn't touch it */ void *driver_data; /* Driver data, set and get with dev_set/get_drvdata */ struct dev_pm_info power; struct dev_pm_domain *pm_domain; #ifdef CONFIG_PINCTRL struct dev_pin_info *pins; #endif #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. */ unsigned long dma_pfn_offset; 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 */ #ifdef CONFIG_DMA_CMA struct cma *cma_area; /* contiguous memory area for dma allocations */ #endif /* arch specific additions */ struct dev_archdata archdata; struct device_node *of_node; /* associated device tree node */ struct acpi_dev_node acpi_node; /* associated ACPI device node */ dev_t devt; /* dev_t, creates the sysfs "dev" */ u32 id; /* device instance */ 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); struct iommu_group *iommu_group; bool offline_disabled:1; bool offline:1; };
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
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; }
内核中每一个设备都是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)
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); }
主要是设置设备所属的kset,设置设备所属kset的ktype,初始化设备资源管理的链表,以及设备电源管理初始化。
- device_add(添加一个设备到系统中)
int device_add(struct device *dev) { struct device *parent = NULL; struct kobject *kobj; struct class_interface *class_intf; int error = -EINVAL; dev = get_device(dev); //增加此设备的引用计数 if (!dev) goto done; if (!dev->p) { error = device_private_init(dev); ---------------A 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->init_name = NULL; } /* subsystems can specify simple device enumeration */ if (!dev_name(dev) && dev->bus && dev->bus->dev_name) dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); if (!dev_name(dev)) { //设置device的name域,如果设置失败退出。 error = -EINVAL; goto name_error; } pr_debug("device: '%s': %s\n", dev_name(dev), __func__); parent = get_device(dev->parent); //增加设备parent的引用计数 kobj = get_device_parent(dev, parent); //设置设备parent的内核对象,建立设备在sys下的层次结构。 if (kobj) dev->kobj.parent = kobj; /* 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); //调用kobject_add将设备添加到sys中 if (error) goto Error; /* notify platform of device entry */ if (platform_notify) platform_notify(dev); error = device_create_file(dev, &dev_attr_uevent); //创建设备的uevent属性 if (error) goto attrError; if (MAJOR(dev->devt)) { error = device_create_file(dev, &dev_attr_dev); //如果主设备号存在,创建dev属性 if (error) goto ueventattrError; error = device_create_sys_dev_entry(dev); if (error) goto devtattrError; devtmpfs_create_node(dev); //调用devtmpfs,自动创建设备节点 } error = device_add_class_symlinks(dev); //创建链接文件 if (error) goto SymlinkError; error = device_add_attrs(dev); //添加设备的属性 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); //将设备添加到pm core链表中,会在suspend中使用到 /* Notify clients of device addition. This call must come * after dpm_sysfs_add() and before kobject_uevent(). */ if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, //使用通知链同时有新设备添加到bus中 BUS_NOTIFY_ADD_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_ADD); //使用uevent通知上层,这时候就会使用到device_uevent_ops中的函数 bus_probe_device(dev); //匹配bus下的设备与驱动 if (parent) klist_add_tail(&dev->p->knode_parent, //如果parent存在,将dev添加到parent链表中 &parent->p->klist_children); if (dev->class) { //如果class存在,则将dev添加到class链表中 mutex_lock(&dev->class->p->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->interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); mutex_unlock(&dev->class->p->mutex); } }
A: 如果设备的device_private不存在,就重现分配一个
int device_private_init(struct device *dev) { dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL); //分配一个device_private结构 if (!dev->p) return -ENOMEM; dev->p->device = dev; //设置device_private的device结构 klist_init(&dev->p->klist_children, klist_children_get, klist_children_put); //初始化设备下所有children链表 INIT_LIST_HEAD(&dev->p->deferred_probe); return 0; }
B: 主要分析下如何将设备添加到一条总线上,这是核心。
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 = device_add_groups(dev, bus->dev_groups); if (error) goto out_groups; 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"); if (error) goto out_subsys; klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); } return 0; }
如果此设备不属于任何总线,直接就返回,否则添加设备属性,创建链接文件,将此bus下的设备添加到klist_devices链表中。
C: 关于总线先设备与驱动的匹配是设备驱动模型的核心
void bus_probe_device(struct device *dev) { struct bus_type *bus = dev->bus; struct subsys_interface *sif; int ret; if (!bus) //如果不存在总线,直接返回 return; if (bus->p->drivers_autoprobe) { //是否总线支持自动匹配 ret = device_attach(dev); WARN_ON(ret < 0); } mutex_lock(&bus->p->mutex); list_for_each_entry(sif, &bus->p->interfaces, node) if (sif->add_dev) sif->add_dev(dev, sif); mutex_unlock(&bus->p->mutex); }
一般总线都会支持自动匹配,当然可以修改driver_autoprobe的值,然后调用device_attach进行匹配。
int device_attach(struct device *dev) { int ret = 0; device_lock(dev); //因为此操作只能运行一个dev进行,需要互斥保护。 if (dev->driver) { //如果设备存在对应的driver,说明已经进行了匹配,只需要调用device_bind_driver在sys中建立关系。 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 { ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); //如果driver不存在,则需要遍历dev所在bus下的所有driver pm_request_idle(dev); } out_unlock: device_unlock(dev); return ret; }
当遍历driver下过程中,最终会调用到__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); }
函数match最终使用的匹配是
static inline int driver_match_device(struct device_driver *drv, struct device *dev) { return drv->bus->match ? drv->bus->match(dev, drv) : 1; }
根据bus是否存在match函数,如果存在调用bus的match函数,如果不存在,直接返回1。如果匹配成功之后就会调用driver_probe_device进行绑定,最终会调用really_probe函数。
static int really_probe(struct device *dev, struct device_driver *drv) { int ret = 0; int local_trigger_count = atomic_read(&deferred_trigger_count); 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; //设置设备的driver变量 /* If using pinctrl, bind pins now before probing */ ret = pinctrl_bind_pins(dev); if (ret) goto probe_failed; 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); //如果bus的probe存在,则调用bus的probe函数 if (ret) goto probe_failed; } else if (drv->probe) { //否则调用驱动的probe,这下知道驱动的probe函数是如何调用的。 ret = drv->probe(dev); if (ret) goto probe_failed; } driver_bound(dev); //将设备所属的驱动加入到驱动链表中,使用通知链发出BOUND信息。 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: //probe失败之后,就会清空设备资源,设备driver设置为NULL,从sys中移除dev建立的driver链接。 devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL; dev_set_drvdata(dev, NULL); if (ret == -EPROBE_DEFER) { /* Driver requested deferred probing */ dev_info(dev, "Driver %s requests probe deferral\n", drv->name); driver_deferred_probe_add(dev); /* Did a trigger occur while probing? Need to re-trigger if yes */ if (local_trigger_count != atomic_read(&deferred_trigger_count)) driver_deferred_probe_trigger(); } else 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; }
- device_register(注册一个设备到系统中是上述两个函数的结合体)
- device_unregister(将一个设备从系统中注销)
void device_unregister(struct device *dev) { pr_debug("device: '%s': %s\n", dev_name(dev), __func__); device_del(dev); put_device(dev); }
此函数分为两步走,第一步从系统中移除此设备,第二步将设备的引用计数减去1。主要的注销操作在device_del中实现,也就是register的反操作。
void device_del(struct device *dev) { struct device *parent = dev->parent; struct class_interface *class_intf; /* Notify clients of device removal. This call must come * before dpm_sysfs_remove(). */ if (dev->bus) //如果bus总线存在,调用通知链删除设备 blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_DEL_DEVICE, dev); dpm_sysfs_remove(dev); //电源管理函数,关掉设备电源 if (parent) //如果有父设备,将当前设备从父设备所属链表删除 klist_del(&dev->p->knode_parent); if (MAJOR(dev->devt)) { //如果主设备号存在, 动态删除设备节点,移除设备的属性 devtmpfs_delete_node(dev); device_remove_sys_dev_entry(dev); device_remove_file(dev, &dev_attr_dev); } if (dev->class) { //如果设备所属的class存在,移除设备的链接,调用remove_dev做移除,从class链表中删除该设备 device_remove_class_symlinks(dev); mutex_lock(&dev->class->p->mutex); /* notify any interfaces that the device is now gone */ list_for_each_entry(class_intf, &dev->class->p->interfaces, node) if (class_intf->remove_dev) class_intf->remove_dev(dev, class_intf); /* remove the device from the class list */ klist_del(&dev->knode_class); mutex_unlock(&dev->class->p->mutex); } device_remove_file(dev, &dev_attr_uevent); //移除设备的uevent属性 device_remove_attrs(dev); bus_remove_device(dev); //从总线中移除设备,以及电源管理等。 device_pm_remove(dev); driver_deferred_probe_del(dev); /* Notify the platform of the removal, in case they * need to do anything... */ if (platform_notify_remove) platform_notify_remove(dev); if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_REMOVED_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_REMOVE); cleanup_device_parent(dev); kobject_del(&dev->kobj); //删除设备的内核模块 put_device(parent); }
设备属性
linux中使用device_attribute结构体表示一个设备的属性
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); };
通常使用to_dev_attr宏定义得到device_attribute对象
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
同时为了方便定义设备的属性,内核提供了一系列相关的宏定义,用于初始化设备的属性。
#define DEVICE_ATTR(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store) #define DEVICE_ATTR_RW(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RW(_name) #define DEVICE_ATTR_RO(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RO(_name) #define DEVICE_ATTR_WO(_name) \ struct device_attribute dev_attr_##_name = __ATTR_WO(_name)
关于设备属性的调用过程,最终会调用到设备的show和store函数中,具体的流程分析可见Linux设备驱动模型-Ktype
static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct device_attribute *dev_attr = to_dev_attr(attr); struct device *dev = kobj_to_dev(kobj); ssize_t ret = -EIO; if (dev_attr->show) ret = dev_attr->show(dev, dev_attr, buf); if (ret >= (ssize_t)PAGE_SIZE) { print_symbol("dev_attr_show: %s returned bad count\n", (unsigned long)dev_attr->show); } return ret; } static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct device_attribute *dev_attr = to_dev_attr(attr); struct device *dev = kobj_to_dev(kobj); ssize_t ret = -EIO; if (dev_attr->store) ret = dev_attr->store(dev, dev_attr, buf, count); return ret; } static const struct sysfs_ops dev_sysfs_ops = { .show = dev_attr_show, .store = dev_attr_store, };
设备类型
在include/linux/device.h文件存在这样的结构体:
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, umode_t *mode, kuid_t *uid, kgid_t *gid); void (*release)(struct device *dev); const struct dev_pm_ops *pm; };
此结构代表设备类型。通常一个Bus下会存在各种设备的,比如:disks, mouse, event等。而此结构就表明此设备是何种类型的设备。
name: 代表设备的名称,在上报event的时候,会通过DEVTYPE设置设备的类型名称。
groups: 代码设备的属性,在添加设备的属性的时候,如果存在设备类型,也会添加设备类型的属性。
uevent: 在新增一个设备时,通过该函数上报设备类型的uevent。
devnode: 在创建一个设备节点的时候会使用到,获取设备的信息。
release: 在设备释放时候,如果存在设备类型会调用到。