1. 概述
Linux 设备模型(Linux Device Model)是内核中统一描述和管理硬件及其驱动关系的基础框架,核心目标是:抽象硬件、统一驱动模型、支持热插拔、并向用户空间暴露一致的视图(sysfs / uevent)。

2. 数据结构定义

struct bus_type 代表总线
struct bus_type { const char *name; /* 总线的名字 */struct subsys_private *p; /* 总线私有数据 */};struct subsys_private {struct kset subsys;struct kset *devices_kset; /* 所有dev都属于这个kset集合 */struct list_head interfaces;struct mutex mutex;struct kset *drivers_kset; /* 所有drv都属于这个kset集合 */struct klist klist_devices; /* 设备链表头节点 */struct klist klist_drivers; /* 驱动链表头节点 */struct blocking_notifier_head bus_notifier; unsigned int drivers_autoprobe:1;struct bus_type *bus;struct class *class;};
struct device 代表设备
struct device {struct device *parent;struct device_private *p;struct kobject kobj; const char *init_name; /* dev名字e */ conststruct device_type *type;struct bus_type *bus; /* 指向所属bus */struct klist_node knode_class; /* 作为class成员时使用的链表节点 */struct class *class};struct device_private {struct klist klist_children; /* 链接children devic的链表头节点 */struct klist_node knode_parent; /* 链接到parent的链接节点 */struct klist_node knode_driver; /* 链接到绑定驱动的链表节点 */struct klist_node knode_bus; /* 用于挂到其所属bus的klist_devices链表中 */struct device *device;}
struct device_driver 代表驱动
struct device_driver { const char *name;struct bus_type *bus;struct module *owner;enum probe_type probe_type; int (*probe) (struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); int (*resume) (struct device *dev);struct driver_private *p;};struct driver_private {struct kobject kobj;struct klist klist_devices; /* 用于链接绑定该驱动的dev节点 */struct klist_node knode_bus; /* 用于挂到其所属bus的klist_drivers链表中 */struct module_kobject *mkobj;struct device_driver *driver;};
3. 核心操作函数源码分析
当内核启动后,首先会依次调用devices_init 、buses_init、classes_init函数,用于建立设备模型的基础目录结构。
/** * driver_init: * ├── devices_init * ├── buses_init * └── classes_init*/int __init devices_init(void){ /* /sys/devices */ devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); /* /sys/dev */ dev_kobj = kobject_create_and_add("dev", NULL); /* /sys/dev/block */ sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj); /* /sys/dev/char */ sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);}int __init buses_init(void){ /* /sys/bus */ bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL); /* /sys/devices/system */ system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);}int __init classes_init(void){ /* /sys/class */ class_kset = kset_create_and_add("class", NULL, NULL);}
现在目录结构如下

3.1. 注册总线
bus_register 用于注册一个总线,首先将总线的目录注册到/sys/bus/[bus->name],函数内部使用bus_kset和bus_ktype是全局变量,所有总线都指向它。drivers_autoprobe用于控制驱动或者设备加载是是否自动探测。然后创建/sys/bus/[bus->name]文件,/sys/bus/[bus->name]/devices和/sys/bus/[bus->name]/drivers 目录,创建两个链表priv→klist_devices和priv→klist_drivers 用于后续链接驱动和设备,在/sys/bus/[bus->name]/创建drivers_probe、drivers_autoprobe文件和属性组文件。
int bus_register(struct bus_type *bus){ int retval;struct subsys_private *priv; /* 双向关联 */ priv->bus = bus; bus->p = priv; /* 设置subsys的属性 */ retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype; /* 设置驱动自动探测 */ priv->drivers_autoprobe = 1; retval = kset_register(&priv->subsys); /* 创建/sys/bus/[bus->name]/uevent文件 */ retval = bus_create_file(bus, &bus_attr_uevent); /* 创建/sys/bus/[bus->name]/devices和/sys/bus/[bus->name]/drivers */ priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj); priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj); /* 创建/sys/bus/platform/drivers_probe 和/sys/bus/platform/drivers_probe/drivers_autoprobe */ retval = add_probe_files(bus); /* 在/sys/bus/[bus->name]/下创建属性组相关文件 */ retval = bus_add_groups(bus, bus->bus_groups);}
现在/sys/bus/的目录结构如下所示

3.2 device注册
函数bus_register 用于注册一个设备,其完成device结构体的初始化、然后把设备注册进系统。
device_register ├── device_initialize:初始化各种结构体成员 └── device_add:设备上线
device_initialize用于初始化device对象的基础成员,为后续device_add()/device_register()做准备。函数先设置dev的kset指向devices_kset,初始化各种链表和锁,最后将dev的状态设置为没有驱动,此时设备仍然不可见。
void device_initialize(struct device *dev){ /* 设置kset */ dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); lockdep_set_novalidate_class(&dev->mutex); /* 状态为无驱动 */ dev->links.status = DL_DEV_NO_DRIVER;}
device_add这一阶段是“设备正式上线”,核心动作包括:设置dev_name,建立parent-child关系,创建 /sys/devices/...目录,挂到bus->p->klist_devices ,触发uevent,尝试和driver进行匹配,从这里开始,设备对内核和用户态都是可见的。
int device_add(struct device *dev){struct device *parent;struct kobject *kobj;struct class_interface *class_intf;struct kobject *glue_dir = NULL; /* 引用计数+1 */ dev = get_device(dev); /* 初始化私有数据 */ if (!dev->p) { error = device_private_init(dev); } /* 设置name */ if (dev->init_name) { dev_set_name(dev, "%s", dev->init_name); dev->init_name = NULL; } 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)) { error = -EINVAL; goto name_error; } parent = get_device(dev->parent); /* 计算实际的父目录 */ kobj = get_device_parent(dev, parent); if (kobj) dev->kobj.parent = kobj; /* use parent numa_node */ if (parent && (dev_to_node(dev) == NUMA_NO_NODE)) set_dev_node(dev, dev_to_node(parent)); /* 添加到sysfs层次结构中 */ error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); /* 创建uevent文件 */ error = device_create_file(dev, &dev_attr_uevent); /* 建立device和class之间的双向链接 */ error = device_add_class_symlinks(dev); /* 创建属性文件 */ error = device_add_attrs(dev); /* 添加dev到bus的链表 */ error = bus_add_device(dev); /* 电源管理 */ error = dpm_sysfs_add(dev); device_pm_add(dev); if (MAJOR(dev->devt)) { /* 创建文件 */ error = device_create_file(dev, &dev_attr_dev); error = device_create_sys_dev_entry(dev); /* 创建字符设备/dev/节点 */ devtmpfs_create_node(dev); } /* 内核通知链,ADD事件*/ if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); /*告知用户空间的ADD事件*/ kobject_uevent(&dev->kobj, KOBJ_ADD); /* 驱动探测 */ bus_probe_device(dev); if (parent) klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children); /* class和device绑定 */ if (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); }}
bus_add_device 建立两个软链接,并将dev加入到bus的链表进行管理。
int bus_add_device(struct device *dev){struct bus_type *bus = bus_get(dev->bus); int error = 0; if (bus) { error = device_add_groups(dev, bus->dev_groups); /* 建立/sys/bus/<bus>/devices/<dev_name> → /sys/devices/.../<dev_name>的软链接 */ error = sysfs_create_link(&bus->p->devices_kset->kobj, &dev->kobj, dev_name(dev)); /* 建立/sys/devices/.../<dev>/subsystem → /sys/bus/<bus>的软链接 */ error = sysfs_create_link(&dev->kobj, &dev->bus->p->subsys.kobj, "subsystem"); /* 加入到klist_devices链表 */ klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); } return 0;}
3.3. 注册驱动
函数driver_register用于注册一个驱动,首先根据名字在总线里面查找驱动,如果没有同名驱动的话则将驱动注册到总线,创建属性组,生成uevent的ADD事件。
/** * driver_register: * ├── driver_find:查找同名驱动 * ├── bus_add_driver: * └── kobject_uevent:生成ADD事件*/int driver_register(struct device_driver *drv){ int ret;struct device_driver *other; /* 查找是否有同名驱动 */ other = driver_find(drv->name, drv->bus); if (other) { return -EBUSY; } ret = bus_add_driver(drv); if (ret) return ret; ret = driver_add_groups(drv, drv->groups); if (ret) { return ret; } kobject_uevent(&drv->p->kobj, KOBJ_ADD); return ret;}
上面注册总线时提到一个函数bus_add_driver 是将驱动注册到总线的核心功能实现。
int bus_add_driver(struct device_driver *drv){struct bus_type *bus;struct driver_private *priv; int error = 0; /* 总线引用计数+1 */ bus = bus_get(drv->bus); priv = kzalloc(sizeof(*priv), GFP_KERNEL); klist_init(&priv->klist_devices, NULL, NULL); priv->driver = drv; drv->p = priv; /* 创建/sys/bus/[bus->name]/drivers/[drv->name]节点 */ priv->kobj.kset = bus->p->drivers_kset; error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name); /* 加入链表管理 */ klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); if (drv->bus->p->drivers_autoprobe) { /* 判断驱动的probe能不能放到异步线程中执行,而不是在同步路径里阻塞内核初始化流程 */ if (driver_allows_async_probing(drv)) { /* 异步遍历device,调用match函数匹配 */ async_schedule(driver_attach_async, drv); } else { /* 同步遍历device,调用match函数匹配 */ error = driver_attach(drv); if (error) goto out_unregister; } } module_add_driver(drv->owner, drv); /* 创建uevent文件 */ error = driver_create_file(drv, &driver_attr_uevent); /* 创建属性组 */ error = driver_add_groups(drv, bus->drv_groups); return 0;}
3.4 驱动和设备的探测
当添加设备时,会通过bus总线去探测驱动,如果设备指定了驱动,那么直接和它绑定;如果设备未指定驱动,那么遍历驱动链表查找,如果找到则调用probe函数执行。

static int __device_attach(struct device *dev, bool allow_async){ device_lock(dev); if (dev->driver) { /* 指定了驱动程序 */ /* 设备绑定驱动 */ ret = device_bind_driver(dev); } else { /* 未指定驱动程序 */ ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); }}
设备指定驱动时会调用device_bind_driver 函数,加入链表节点,发送bind事件。
static void driver_bound(struct device *dev){ /* 将dev加入到klist_devices链表进行管理 */ klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); /* 发送BIND事件 */ kobject_uevent(&dev->kobj, KOBJ_BIND);}
设备探测驱动时,如果通过match函数找到匹配的驱动,那么则会调用probe函数
static int __device_attach_driver(struct device_driver *drv, void *_data){ ret = driver_match_device(drv, dev); return driver_probe_device(drv, dev);}
当添加驱动时,driver_attach会去匹配设备,如果匹配成功则调用probe函数

int driver_attach(struct device_driver *drv){ return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);}static int __driver_attach(struct device *dev, void *data){struct device_driver *drv = data; driver_match_device(drv, dev); driver_probe_device(drv, dev); return 0;