title: faux
categories:
- linux
- drivers
- base
tags: - linux
- drivers
- base
abbrlink: 7a8aa269
date: 2025-10-03 09:01:49

文章目录
- [drivers/base/faux.c 虚拟设备总线(Faux Bus) 简化虚拟设备驱动的创建](#drivers/base/faux.c 虚拟设备总线(Faux Bus) 简化虚拟设备驱动的创建)
drivers/base/faux.c 虚拟设备总线(Faux Bus) 简化虚拟设备驱动的创建
历史与背景
这项技术是为了解决什么特定问题而诞生的?
drivers/base/faux.c 文件所实现的 "Faux Bus"(虚拟总线)是一项较新的内核技术,在 Linux 6.14 内核中被合并。它的诞生主要是为了解决一个长期存在的内核开发"滥用"问题:将平台总线(Platform Bus)用于不属于平台设备(Platform Device)的场景。
内核维护者 Greg Kroah-Hartman 指出,多年来,许多开发者为了利用设备模型的便利性(如自动的驱动-设备绑定、probe/remove回调机制),会将一些纯粹的虚拟设备或简单的逻辑设备注册为"平台设备"。 这种做法在功能上可行,但从设计哲学上看是不正确的,因为平台设备本意是指那些在SoC(片上系统)上,无法通过正常总线(如PCI, USB)枚举发现的、有固定内存地址或硬件资源的设备。将纯虚拟设备(例如一个用于测试的dummy regulator)注册为平台设备,是一种概念上的滥用,并给代码带来了不必要的复杂性。
Faux Bus 的目的就是提供一个极其简单的、专为这类虚拟或"假"设备设计的总线,从而清理代码,让平台总线的用途回归其本意。
它的发展经历了哪些重要的里程碑或版本迭代?
- 2025年2月:由内核核心维护者 Greg Kroah-Hartman 提出 "Faux Bus" 的概念和初始补丁集,并征求社区意见。
- Linux 6.14内核:该技术被正式合并进入主线内核。一个显著的特点是,它在被引入的同时,就提供了C语言和Rust语言的API绑定,这在内核新接口的开发中较为少见,也体现了对新兴内核开发语言的支持。
- Linux 6.15及以后:社区开始将内核中原先滥用平台总线的驱动,逐步转换到使用 Faux Bus。
目前该技术的社区活跃度和主流应用情况如何?
作为一个新兴的内核API,Faux Bus 正处于被逐步推广和应用的阶段。内核社区正在积极地识别那些适合转换的驱动程序,并提交补丁。它的主要应用场景是内核内部的虚拟设备、测试驱动和一些简单的逻辑抽象设备,例如:
- Dummy regulator 驱动
- USB PHY (physical layer) 的一些辅助代码
核心原理与设计
它的核心工作原理是什么?
drivers/base/faux.c 的核心原理是提供一个极简的API,将"创建设备"和"创建并绑定驱动"这两个动作合二为一。
- 定义一个新总线 :代码内部定义了一个名为 "faux" 的
bus_type。这是一个非常简单的总线,没有复杂的匹配逻辑。 - 统一的创建函数 :它对外暴露的核心API是
faux_device_create_with_groups()。当一个内核模块调用这个函数时,它会执行以下一系列操作:- 动态地创建一个
struct device_driver实例。这个驱动被设置为属于 "faux" 总线。 - 动态地创建一个
struct device实例,并将其总线也设置为 "faux"。 - 自动绑定 :由于设备和驱动被创建时就明确了对应关系,内核会立即将它们绑定在一起。这个绑定操作会触发调用调用者提供的
.probe回调函数。
- 动态地创建一个
- 生命周期管理 :
faux_device_create_with_groups()函数会返回一个指向新创建的faux_device的指针。这个设备的生命周期由调用者管理。当不再需要该设备时,调用者只需调用faux_device_destroy(),该函数会自动处理驱动的.remove回调、设备和驱动的注销以及内存的释放。
它的主要优势体现在哪些方面?
- API极其简单:相比平台设备的注册,Faux Bus 的API更简单,只需要两个核心函数(创建和销毁)。
- 概念清晰:为虚拟设备和逻辑设备提供了一个专属的、名正言顺的"家",避免了对平台总线的滥用,使内核代码的意图更清晰。
- 代码更简洁:在一些转换案例中,使用 Faux Bus 相比原先使用平台总线的代码,行数有所减少,逻辑也更直接。
- 自动绑定:创建设备的同时就隐式创建并绑定了驱动,开发者无需关心匹配逻辑。
它存在哪些已知的劣势、局限性或在特定场景下的不适用性?
- 功能极简:Faux Bus 被设计为只处理最简单的情况。它没有设备树(Device Tree)或ACPI支持,不适用于需要从固件获取信息的设备。
- 无匹配逻辑:它不支持一个驱动匹配多个设备,或者一个设备在多个驱动间选择。设备和驱动是一对一的、在创建时就锁定的关系。
- 不适用于真实硬件:它的设计目标是纯软件的、虚拟的设备。任何与真实物理总线或硬件资源相关的设备都不应该使用 Faux Bus。
使用场景
在哪些具体的业务或技术场景下,它是首选解决方案?请举例说明。
Faux Bus 是为那些纯软件模拟的、生命周期由其他内核模块动态管理的逻辑设备提供的首选解决方案。
- 场景一:Dummy Regulator
内核的regulator/dummy.c驱动可以创建一个虚拟的稳压器(regulator),用于测试或满足那些在软件上需要一个regulator句柄但实际上没有硬件对应的场景。在过去,它通过注册一个platform_device和platform_driver来实现。现在,它可以直接使用faux_device_create()来创建这个虚拟稳压器,代码更简洁且概念上更正确。 - 场景二:USB PHY 辅助逻辑
某些USB控制器驱动需要在内部管理一个抽象的PHY层,这个PHY层可能没有独立的物理设备与之对应。使用 Faux Bus 可以为此创建一个逻辑上的设备和驱动,以便利用设备模型的probe/remove机制来管理其初始化和清理过程,而无需污染平台总线。
是否有不推荐使用该技术的场景?为什么?
- 所有与物理硬件相关的设备 :如果一个设备是通过PCIe、USB、I2C、SPI等物理总线连接的,或者是在设备树/ACPI中描述的SoC内部设备,绝对不能 使用 Faux Bus。这些设备必须使用其对应的真实总线类型(
pci_driver,usb_driver,platform_driver等)。 - 需要驱动与设备分离的场景:如果希望一个驱动能支持多种设备,或者一个设备能由多个驱动中的一个来处理(例如一个通用驱动和一个厂商特定驱动),则不应使用 Faux Bus,因为它不支持这种分离的匹配逻辑。
对比分析
请将其 与 其他相似技术 进行详细对比。
Faux Bus 的主要对比对象就是被它取代的"滥用"方案------平台总线(Platform Bus),以及另一个虚拟总线------虚拟总线(Virtual Bus)。
| 特性 | Faux Bus (faux.c) |
Platform Bus (platform.c) |
Virtual Bus (subsys_virtual_register) |
|---|---|---|---|
| 设计用途 | 用于纯粹的、简单的、与硬件无关的虚拟/逻辑设备,以取代对平台总线的滥用。 | 用于SoC上那些无法被硬件自动枚举的、通常在设备树或ACPI中描述的真实硬件设备。 | 用于表示一类虚拟设备,这些设备通常出现在 /sys/devices/virtual/ 下,如网络设备(net)或输入设备(input)。 |
| API复杂度 | 极低。核心API只有创建和销毁,设备和驱动在创建时自动绑定。 | 中等 。需要分别定义platform_device和platform_driver,并通过匹配机制(如设备树的compatible字符串或设备名)进行绑定。 |
低。提供注册总线的API,但设备和驱动的注册仍需分开处理。 |
| 设备-驱动关系 | 一对一,创建时锁定 。一个faux_device_create调用就唯一对应一个驱动实例。 |
多对多,运行时匹配。一个驱动可以支持多种设备,一个设备也可以匹配多个潜在的驱动。 | 灵活,由子系统自身决定。 |
| 硬件/固件支持 | 无。完全不处理设备树或ACPI。 | 核心特性。平台总线的核心任务就是处理通过固件描述的硬件设备。 | 无。 |
| Sysfs路径 | /sys/bus/faux/devices/ |
/sys/bus/platform/devices/ |
/sys/devices/virtual/<subsys_name>/ |
| 典型例子 | Dummy regulator, USB PHY 逻辑。 | I2C控制器、SPI控制器、SoC内部的DMA引擎。 | 网络接口(eth0)、TTY设备、输入设备(event0)。 |
faux_bus_init: 初始化并注册一个虚拟总线
此代码片段的作用是定义并注册一个完整的、自包含的 "faux" (虚拟) 总线系统。这个过程是 Linux 设备模型中建立新总线的基础, 它包括三个核心步骤:
- 注册一个根设备 (
faux_bus_root) : 这个设备在sysfs中作为虚拟总线层次结构的顶层目录, 通常位于/sys/devices/faux。 - 注册一个总线类型 (
faux_bus_type) : 这定义了总线的名称、行为以及最重要的------设备与驱动的匹配规则 (match函数) 和探测/移除逻辑 (probe/remove函数)。 - 注册一个驱动程序 (
faux_driver): 这是挂载在此总线上的一个驱动实例。
c
/*
* 定义一个静态的 struct device 实例, 作为虚拟总线的根设备.
* 在 sysfs 中, 它通常会表现为 /sys/devices/faux 目录.
* 'static' 关键字使其在此文件外部不可见.
*/
static struct device faux_bus_root = {
/*
* .init_name: 初始化时使用的设备名称.
* device_register() 函数将使用这个名称来创建 kobject, 也就是 sysfs 中的目录名.
*/
.init_name = "faux",
};
/*
* 定义一个静态的、常量类型的 struct bus_type 实例.
* 它描述了 "faux" 总线的所有核心属性和行为.
* 'const' 表示这个结构体的内容在编译后是只读的.
*/
static const struct bus_type faux_bus_type = {
/*
* .name: 总线的名称. 这将会在 sysfs 中创建 /sys/bus/faux 目录.
*/
.name = "faux",
/*
* .match: 一个函数指针, 指向用于判断设备和驱动是否匹配的函数.
* 当有新设备或新驱动注册到这个总线时, 内核会调用此函数进行匹配.
* 此处指向未提供的 faux_match 函数.
*/
.match = faux_match,
/*
* .probe: 一个函数指针, 指向总线级别的探测函数.
* 在驱动的 probe 函数被调用之前, 总线核心可能会调用此函数进行一些通用的设置.
* 此处指向未提供的 faux_probe 函数.
*/
.probe = faux_probe,
/*
* .remove: 一个函数指针, 指向总线级别的移除函数.
* 当设备从总线上移除时调用.
* 此处指向未提供的 faux_remove 函数.
*/
.remove = faux_remove,
};
/*
* 定义一个静态的 struct device_driver 实例.
* 它代表一个将要注册到 "faux" 总线上的驱动程序.
*/
static struct device_driver faux_driver = {
/*
* .name: 驱动程序的名称.
* 这将会在 /sys/bus/faux/drivers/ 目录下创建名为 "faux_driver" 的条目.
*/
.name = "faux_driver",
/*
* .bus: 一个指针, 指向此驱动所属的总线类型.
* 这是将驱动程序与 faux_bus_type 关联起来的关键链接.
*/
.bus = &faux_bus_type,
/*
* .probe_type: 指定探测类型为强制同步.
* 这意味着当设备与此驱动匹配时, 驱动的 probe 函数会立即在当前上下文中被同步调用,
* 而不是被推迟到异步的工作队列中. 这可以简化初始化时的依赖关系.
*/
.probe_type = PROBE_FORCE_SYNCHRONOUS,
/*
* .suppress_bind_attrs: 设置为 true, 禁止在 sysfs 中为此驱动自动创建 "bind" 和 "unbind" 文件.
* 这通常用于那些不希望被用户从用户空间手动绑定或解绑的驱动.
*/
.suppress_bind_attrs = true,
};
/*
* faux_bus_init: 模块初始化函数.
* __init 宏告诉编译器将此函数放入 ".init.text" 段.
* 在内核启动过程完成后, 这个段的内存可以被释放以节省RAM.
* @return: 成功时返回 0, 失败时返回负的错误码.
*/
int __init faux_bus_init(void)
{
/*
* 定义一个整型变量 ret, 用于存储函数调用的返回值.
*/
int ret;
/*
* 调用 device_register() 函数注册 faux_bus_root 设备.
* 这会在 sysfs 中创建 /sys/devices/faux.
*/
ret = device_register(&faux_bus_root);
/*
* 检查注册是否失败 (返回值非零).
*/
if (ret) {
/*
* 如果注册失败, 调用 put_device() 来清理已部分初始化的 device 结构体, 释放其引用.
*/
put_device(&faux_bus_root);
/*
* 返回错误码.
*/
return ret;
}
/*
* 调用 bus_register() 函数注册 faux_bus_type 总线类型.
* 这会在 sysfs 中创建 /sys/bus/faux, 并使总线对内核的设备模型可见.
*/
ret = bus_register(&faux_bus_type);
/*
* 检查注册是否失败.
*/
if (ret)
/*
* 如果失败, 跳转到 error_bus 标签处进行清理.
*/
goto error_bus;
/*
* 调用 driver_register() 函数注册 faux_driver 驱动程序.
* 这会将驱动添加到 faux_bus_type 的驱动列表中, 并创建 /sys/bus/faux/drivers/faux_driver.
*/
ret = driver_register(&faux_driver);
/*
* 检查注册是否失败.
*/
if (ret)
/*
* 如果失败, 跳转到 error_driver 标签处进行清理.
*/
goto error_driver;
/*
* 所有步骤都成功, 返回 0.
*/
return ret;
/*
* 错误处理标签. 当 driver_register 失败时, 从这里开始清理.
*/
error_driver:
/*
* 调用 bus_unregister() 注销之前成功注册的总线类型.
*/
bus_unregister(&faux_bus_type);
/*
* 错误处理标签. 当 bus_register 失败时, 从这里开始清理.
*/
error_bus:
/*
* 调用 device_unregister() 注销之前成功注册的根设备.
*/
device_unregister(&faux_bus_root);
/*
* 返回导致失败的错误码.
*/
return ret;
}
faux_device_create_with_groups / faux_device_create: 创建并注册一个虚拟设备
这两个函数共同提供了一个接口, 用于在内核中创建和注册一个虚拟的 (faux) 设备。虚拟设备是纯粹由软件定义的实体, 它们不直接对应任何物理硬件, 但能被无缝地集成到Linux的设备驱动模型中。这使得它们可以拥有自己的驱动程序(通过回调函数实现)、出现在sysfs文件系统中, 并与其他真实设备进行交互。
faux_device_create_with_groups是核心实现, 它允许在创建设备的同时为其定义一组sysfs属性文件。faux_device_create则是一个简化的便利性封装, 用于创建一个不带任何额外sysfs属性的虚拟设备。
faux_device_create_with_groups: 创建带sysfs属性的虚拟设备 (核心实现)
c
/*
* faux_device_create_with_groups - 创建一个虚拟设备, 将其注册到驱动核心,
* 并为该设备填充一组初始的sysfs属性文件.
*
* @name: 我们正在添加的设备的名称, 对于所有虚拟设备必须是唯一的.
* @parent: 指向一个可能的父设备(struct device)的指针. 如果设置为NULL,
* 设备将被创建在sysfs中虚拟设备树的"根"目录下.
* @faux_ops: 一个新的设备将回调的 struct faux_device_ops. 可以是NULL.
* @groups: 当设备注册到驱动核心时, 将为此设备创建的一组sysfs属性.
*
* (函数说明省略...)
*
* 返回值:
* * 如果创建设备时发生错误, 返回 NULL.
* * 指向一个已在sysfs中注册的、有效的 struct faux_device 的指针.
*/
struct faux_device *faux_device_create_with_groups(const char *name,
struct device *parent,
const struct faux_device_ops *faux_ops,
const struct attribute_group **groups)
{
/*
* 定义一个指向 faux_object 结构体的指针. faux_object 是包含 faux_device 和其他元数据的容器.
*/
struct faux_object *faux_obj;
/*
* 定义一个指向 faux_device 结构体的指针. faux_device 是对标准 device 结构体的封装.
*/
struct faux_device *faux_dev;
/*
* 定义一个指向标准 device 结构体的指针. 这是Linux内核中所有设备的通用表示.
*/
struct device *dev;
/*
* 定义一个整型变量 ret, 用于存储函数调用的返回值(错误码).
*/
int ret;
/*
* 为 faux_object 容器分配内存. kzalloc 会分配内存并将其内容清零.
* GFP_KERNEL 表示这是一个常规的内核内存分配, 在需要时可以阻塞(睡眠).
*/
faux_obj = kzalloc(sizeof(*faux_obj), GFP_KERNEL);
/*
* 如果内存分配失败, kzalloc 返回 NULL.
*/
if (!faux_obj)
return NULL;
/* 保存回调函数和属性组, 以便将来使用 */
faux_obj->faux_ops = faux_ops;
faux_obj->groups = groups;
/* 初始化设备部分, 并将其注册到驱动核心 */
/*
* 获取 faux_obj 中内嵌的 faux_device 结构体的地址.
*/
faux_dev = &faux_obj->faux_dev;
/*
* 获取 faux_dev 中内嵌的标准 device 结构体的地址.
*/
dev = &faux_dev->dev;
/*
* 调用 device_initialize, 对标准 device 结构体的成员(如kobject, 锁等)进行初始化.
* 这会为设备增加一个引用计数.
*/
device_initialize(dev);
/*
* 设置设备的 .release 回调函数. 这是至关重要的一步.
* 当此设备的最后一个引用被释放时(通过 put_device), 这个函数将被调用以释放 faux_obj 容器的内存, 防止内存泄漏.
*/
dev->release = faux_device_release;
/*
* 如果调用者提供了父设备, 则设置它.
*/
if (parent)
dev->parent = parent;
else
/*
* 否则, 使用虚拟总线的根设备作为父设备.
*/
dev->parent = &faux_bus_root;
/*
* 将设备的总线类型设置为 faux_bus_type. 这会将设备与虚拟总线关联起来.
*/
dev->bus = &faux_bus_type;
/*
* 使用调用者提供的名称来设置设备名. 这个名字将显示在sysfs中.
*/
dev_set_name(dev, "%s", name);
/*
* 调用 device_add, 将设备正式添加到驱动核心.
* 这是一个关键步骤, 它会使设备在sysfs中可见, 并触发总线去为它寻找匹配的驱动程序,
* 从而导致 faux_ops 中的 .probe 回调函数(如果存在)被同步调用.
*/
ret = device_add(dev);
/*
* 如果 device_add 失败.
*/
if (ret) {
/*
* 打印一条错误日志.
*/
pr_err("%s: device_add for faux device '%s' failed with %d\n",
__func__, name, ret);
/*
* 调用 put_device 来减少因 device_initialize 增加的引用计数.
* 这将触发上面设置的 .release 回调, 从而释放 faux_obj 的内存.
*/
put_device(dev);
return NULL;
}
/*
* 验证我们是否已将驱动绑定到设备上 (即 probe 是否成功).
* 如果没有, 我们就让创建失败, 因为调用者几乎不可能判断 probe 是否成功.
*/
if (!dev->driver) {
/*
* 如果 probe 失败 (dev->driver 仍然是 NULL), 则认为创建失败.
*/
dev_dbg(dev, "probe did not succeed, tearing down the device\n");
/*
* 调用 faux_device_destroy 来清理并注销设备.
*/
faux_device_destroy(faux_dev);
/*
* 将返回指针设置为 NULL.
*/
faux_dev = NULL;
}
/*
* 返回创建的虚拟设备指针, 如果失败则为 NULL.
*/
return faux_dev;
}
/*
* 将 faux_device_create_with_groups 符号导出, 供GPL许可证的模块使用.
*/
EXPORT_SYMBOL_GPL(faux_device_create_with_groups);
/**
* faux_device_create - 创建一个虚拟设备并将其注册到驱动核心
*
* (函数说明省略...)
*
*/
struct faux_device *faux_device_create(const char *name,
struct device *parent,
const struct faux_device_ops *faux_ops)
{
/*
* 这是一个便利性封装函数. 它直接调用核心实现函数 faux_device_create_with_groups,
* 并将属性组参数(groups)传递为 NULL, 表示不创建任何额外的sysfs属性文件.
*/
return faux_device_create_with_groups(name, parent, faux_ops, NULL);
}
/*
* 将 faux_device_create 符号导出, 供GPL许可证的模块使用.
*/
EXPORT_SYMBOL_GPL(faux_device_create);