Fan Service

一般来说,ASUS 笔记本电脑都有一个功能,允许用户切换风扇速度。在某些型号上是 Fn-F5,在另一些型号上是 Fn-F。直接的效果是限制风扇速度,从静音模式到狂暴模式,并间接控制性能。但是它在 OpenBSD 中不起作用,所以我需要编写一个 ASUS ACPI WMI 驱动程序。

acpi

ACPI 是一个硬件抽象层,让计算机告诉操作系统如何做事。它提供了字节码形式的函数,操作系统解释这些字节码来执行特定任务。因此,您无需为每个 CPU 编写驱动程序,而是调用 ACPI 方法 _PSS,它会告诉您该怎么做。

当然,供应商希望包含标准中未定义的功能,因此他们添加了一些额外的方法,然后您最终还是需要编写一些自定义驱动程序,用于打开和关闭麦克风的小按钮等等。OpenBSD(和其他操作系统)有很多用于 ThinkPads 等设备的驱动程序。

ACPI 方法名称只有四个字母长,那么如果两个供应商都包含一个名为 WMNB 的方法,我们该怎么办,以及我们如何确定它的作用?(从技术上讲,ACPI 节点由七个字母的名称标识,方法位于它们之下,因此这实际上不是问题,但是有人因此获得了报酬来创建一个解决方案。)

wmi

解决方案是 ACPI 的一个扩展,称为 WMI,它代表 Windows Multiplies Irritations 或其他什么。您向 ACPI 请求一个名为 _WDG 的缓冲区,其中包含一个将 GUID(全局唯一标识符,名称中就说明了)映射到本地方法名称的表。浏览此表,我们遇到了 DCBA-FE-10-23-456789,我们将其识别为感兴趣的 GUID,这告诉我们要调用的方法是 WMNB,但现在我们知道它是正确的方法。

OpenBSD 没有 WMI 驱动程序,这是我设置风扇配置文件的一部分需求。 Linux 中有一个 WMI 驱动程序,它包括一个 ASUS 支持驱动程序,用于各种我感兴趣的功能。

first steps

首先,我编写了一个小型的 acpi 驱动程序,它连接到 PNP0C14。读取 _WDG 缓冲区并打印出所有 GUID。没有识别出任何值,然后意识到我弄错了字节顺序。修复了它,看到了一些非常接近的东西,意识到我仍然没有完全弄对字节顺序。 GUID 不是大端,它们不是小端,它们是 Goldilocks 端。

解决了这个问题,我能够启用事件,并在每次按下热键时获得一个 printf。

events

在接收到中断并进入我们的回调后,我们需要通过调用 _WED 来识别刚刚发生的事件。我这样做了,但每次只得到 1 作为响应,无论按哪个键。我有点卡在这里。非常接近得到我想要的东西,但没有太多关于哪里出错的线索。

事实证明,您可以直接查看系统的 ACPI 代码。 OpenBSD 在启动时运行 acpidump 并保存所有文件。将它们复制到工作目录,运行 iasl -d ,然后查看。快速 grep _WED 会显示源代码。

      Method (_WED, 1, NotSerialized)
      {  
        If ((Arg0 == 0xFF))
        {
          Return (GANQ ())
        }
   
        Return (One)
      }
      Method (GANQ, 0, Serialized)
      {
        P8XH (Zero, 0xF2)
        If (AQNO)
        {
          AQNO--
          Local0 = DerefOf (ATKQ [AQHI])
          AQHI++ 
          AQHI &= 0x0F
          Return (Local0)
        }
        Return (Ones)
      }

即使对 ASL 一无所知,这看起来也像是一个将事件出队的函数,但只有在使用 255 的参数调用时才会这样做。这就是 bug!一旦我使用正确的参数调用它,我就开始接收用于背光切换、风扇切换、AC 连接等的不同事件代码。

devices

编写了更多代码来处理获取和设置设备状态。有一个神奇的咒语可以调用 ACPI 方法并将其传递给设备 ID,该设备 ID 可以是键盘背光、wifi LED 或风扇配置文件等。从键盘背光开始,因为这很容易观察。

在修复了一些简单的错误(例如反转 fnlock 和背光的设备 ID)之后,我能够通过按 F7 来闪烁键盘灯。一切都聚集在一起了。

但是,切换风扇配置文件似乎没有任何作用。在 Windows 下,切换到静音模式时可以听到明显的区别,但我没有观察到任何变化。

Linux 驱动程序包括至少三个不同的风扇和性能配置文件设置的代码。同一个驱动程序用于各种笔记本电脑,从轻薄本到游戏本。根据 Linux 驱动程序,您可以通过读取设备的状态来检测哪些设备存在,然后检查高位,然后通过屏蔽掉一些低位来获取该值。这似乎表明我有一个风扇加速设备,但它没有响应我的更改。

在打印出原始值之后,我注意到它们都是 -2。这似乎不太可能。但它肯定设置了高位,并且屏蔽掉低位使其看起来合理。我想我错过了 Linux 驱动程序中的某些内容?有很多地方都有很多的移位和屏蔽。

回到 ACPI 转储,WMNB 方法是一个巨大的 switch 语句,以 Return (0xFFFFFFFE) 结尾。所以果然,有一个 -2 表示缺少设备,但我仍然不知道应该使用哪个设备 ID。

回到 Linux 驱动程序,有一个史诗般的 norse define 用于 ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO,我还没有集成它,因为我正在 Zenbook 上进行测试,而不是 Vivobook。无论如何添加了一个检查,重新启动,那就对了! Linux define 名称是假的。通过检查 ASL 代码确认。

      If ((IIA0 == 0x00110019))
      {
        Return (FANL (IIA1))
      }

现在我有了所有的碎片。经过一些清理和重构后,我的驱动程序完成了。差不多。仍然有很多传感器值得读取,但我最关心的问题已经解决。

results

启动后按 Fn-F 明显降低了风扇速度,从中等嗡嗡声到几乎听不到的耳语。这对普通性能只有适度的影响。处理器的功耗预算降低了,但其(单核)时钟速度没有降低,因此一切仍然运行平稳。长时间的编译速度较慢,但我也可以通过点击按钮切换回狂暴 turbo 模式。

电池续航时间也更好。风扇本身显然消耗的电力更少。我认为 CPU 在静音模式下也进入了较低的功耗状态,或者它愿意睡得更深。尚不清楚所有更改的内容,并且因机器而异,但这就是它的美妙之处。我在 AMD Zenbook 上编写了驱动程序,但它可以在 Intel Vivobook 上无需更改地工作。非常方便。

misc

我还研究了 FreeBSD 驱动程序。 FreeBSD 直接使用 ACPICA 代码接口,调用诸如 AcpiEvaluateObject 之类的函数,这些函数个人看起来有点不适合 BSD 驱动程序代码。 Linux 也是如此,但所有函数都已重命名为 acpi_evaluate_object,因此它们类似于本地样式。我不太确定这里的历史,但我认为通用的 ACPICA 是在某个指针处从 Linux 代码中提取和泛化的。 OpenBSD 使用自定义 AML 解释器,因此我们有诸如 aml_evalname 之类的函数。

在 OpenBSD 中,几乎所有头文件都与其 C 文件一起保存。因此,dsdt.c 和 dsdt.h 都位于 dev/acpi 中。(值得注意的例外是 kern 中的核心 C 文件和 sys 中的头文件。)作为第一个 acpi 驱动程序编写者,非常方便的是,我需要引用的所有 OpenBSD 代码,从示例驱动程序到数据结构再到辅助函数,都在一个地方。

Linux 分隔了内容,因此我在 drivers/platform/x86 中查看 C 文件,并在 include/linux/platform_data/x86 中查看头文件。 ACPI 代码也位于其他位置。一切都井井有条,但有时感觉就像在按字母顺序排列产品的杂货店中导航。合乎逻辑,但并不完全舒适。

发布时间:2025 年 5 月 11 日 01:57,作者:tedu,更新时间:2025 年 5 月 11 日 01:57,标签:computers openbsd programming