[Linux]学习笔记系列 -- [drivers][base]faux


title: faux

categories:

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

https://github.com/wdfk-prog/linux-study

文章目录

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,将"创建设备"和"创建并绑定驱动"这两个动作合二为一。

  1. 定义一个新总线 :代码内部定义了一个名为 "faux" 的 bus_type。这是一个非常简单的总线,没有复杂的匹配逻辑。
  2. 统一的创建函数 :它对外暴露的核心API是 faux_device_create_with_groups()。当一个内核模块调用这个函数时,它会执行以下一系列操作:
    • 动态地创建一个 struct device_driver 实例。这个驱动被设置为属于 "faux" 总线。
    • 动态地创建一个 struct device 实例,并将其总线也设置为 "faux"。
    • 自动绑定 :由于设备和驱动被创建时就明确了对应关系,内核会立即将它们绑定在一起。这个绑定操作会触发调用调用者提供的 .probe 回调函数。
  3. 生命周期管理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_deviceplatform_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_deviceplatform_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 设备模型中建立新总线的基础, 它包括三个核心步骤:

  1. 注册一个根设备 (faux_bus_root) : 这个设备在 sysfs 中作为虚拟总线层次结构的顶层目录, 通常位于 /sys/devices/faux
  2. 注册一个总线类型 (faux_bus_type) : 这定义了总线的名称、行为以及最重要的------设备与驱动的匹配规则 (match 函数) 和探测/移除逻辑 (probe/remove 函数)。
  3. 注册一个驱动程序 (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);
相关推荐
你的秋裤穿反了2 小时前
笔记13--------报警记录
笔记
@小博的博客2 小时前
Linux 中的编译器 GCC 的编译原理和使用详解
linux·运维·服务器
电饭叔2 小时前
has_solution = False 是什么 费马大定律代码化和定理《计算机科学中的数学》外扩学习3
学习·算法
wheeldown2 小时前
【Linux】TCP协议【2】: 从 echo 到远程命令执行:Linux TCP 服务器的并发与安全实践
linux·服务器·tcp/ip
后来后来啊2 小时前
2026.1.19学习笔记
笔记·学习·算法
鄭郑2 小时前
【Wordpress笔记02】文章分类与标签
笔记
HalvmånEver2 小时前
Linux:信号初识上(信号一)
linux·运维·服务器·c++·系统架构·信号
米高梅狮子2 小时前
04. iSCSI 服务器
linux·运维·服务器
403240732 小时前
【2026最新】Jetson全系列安装支持CUDA加速的OpenCV 4.10保姆级教程(适配Jetpack 6/5及Orin/Xavier/Nano等)
linux·opencv·计算机视觉·nvidia·cuda·jetson