当前位置: 首页 > news >正文

联想笔记本电脑在Windows下通过联想驱动实现风扇控制

概述

本文旨在解决部分联想笔记本电脑无法使用主流的风扇控制工具(如Fan Control, SpeedFan)控制风扇的问题。主流的风扇控制工具在这些电脑上会因无法找到控制风扇的EC寄存器而无法发挥作用。但这是不是就意味着没办法控制风扇了呢?答案是否定的。在出厂自带的联想电脑管家程序中有一个叫做降温除尘的工具,运行这个工具能够让风扇达到最高转速,维持几秒然后恢复正常转速。

降温除尘

查看联想电脑管家程序文件,这个降温除尘工具其实是调用了一个叫做WSHardwarePlugin.dll的链接库,该链接库位于程序安装目录中的plugins文件夹中,使用IDA反编译此dll可以找到一个名为pcm_plugin_action的导出函数,查看此函数能找到如下控制风扇的关键代码。

反编译

鉴于笔者能力有限,未能进一步挖掘,有能力或感兴趣的朋友可以深入研究一下这个dll。测试发现联想电脑管家是通过Lenovo ACPI-Compliant Virtual Power Controller驱动来控制风扇的,这个驱动正是联想笔记本电脑所特有的电源控制驱动。

按照这个思路,我最终找到了2个相关的开源项目FanControl和Lenovo-IdeaPad-Z500-Fan-Controller,这2个项目通过逆向了相关驱动,找到了使用Lenovo ACPI-Compliant Virtual Power Controller驱动控制风扇的方法,不过美中不足的是只能控制风扇在最高转速和正常转速间切换,而不能设定和读取具体转速。

但是这2个项目还有个问题,就是只能实现让风扇断断续续地以最高转速运行,而我通过调试找到了解决这个问题的方法,详见下文。

具体实现

以下代码主要参考了FanControl项目,查看该开源项目的源码,发现需要用到的核心函数就2个,一个用于控制风扇,一个用于获取风扇状态,且都是通过直接读写\\.\EnergyDrv这个设备的某些字节实现的,以下是修改项目源码得到的一个简单的样例:

#include <stdio.h>
#include <Windows.h>// NORMAL: Fan spins at normal speed.
// FAST: Fan spins at the highest speed.
enum FanMode { NORMAL, FAST };// Control the operating mode of the fan.
int fan_control(enum FanMode mode) {HANDLE hndl = CreateFileW(L"\\\\.\\EnergyDrv", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if (hndl == INVALID_HANDLE_VALUE) {return -1;}// lpInBuffer value: 06 00 00 00  01 00 00 00  01 00 00 00 ~ [ 6, 1, 1 ] (inv endian)DWORD inBuffer[3] = { 6, 1 };inBuffer[2] = mode;DWORD bytesReturned = 0;DeviceIoControl(hndl, 0x831020C0, inBuffer, sizeof(inBuffer), NULL, 0, &bytesReturned, NULL);CloseHandle(hndl);return 1;
}// Get the current operating mode of the fan.
int read_state() {HANDLE hndl = CreateFileW(L"\\\\.\\EnergyDrv", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if (hndl == INVALID_HANDLE_VALUE) {return -1;}// lpInBuffer value: 0E 00 00 00 ~ [ 14 ] (inv endian)DWORD inBuffer = 14;DWORD outBuffer;DWORD bytesReturned = 0;DeviceIoControl(hndl, 0x831020C4, &inBuffer, sizeof(inBuffer), &outBuffer, sizeof(outBuffer), &bytesReturned, NULL);CloseHandle(hndl);if (outBuffer == 3) {return FAST;}return Normal;
}int main() {int mode = read_state();if (mode == -1) {printf("Failed to open \\\\.\\EnergyDrv\n");return 1;}if (mode == NORMAL) {printf("FAST mode on\n");fan_control(FAST);} else {printf("NORMAL mode on\n");fan_control(NORMAL);}return 0;
}

函数int fan_control(enum FanMode mode)用于控制风扇的运行状态,参数mode指定风扇运行模式,取值0控制风扇恢复正常转速,取值1控制风扇达到最高转速。 返回值-1表示没有找到联想电源管理驱动,无法实现控制,返回1表示成功实现控制。

函数int read_state()用于获取风扇当前的运行状态,返回0表示当前风扇为正常转速模式,返回1表示当前风扇为最高速运行模式。

编译运行程序,控制台输出FAST mode on则风扇开启最高转速,程序结束运行,再次运行程序控制台输出NORMAL mode on,风扇恢复正常运行状态。如果输出Failed to open \\.\EnergyDrv则说明没有安装Lenovo ACPI-Compliant Virtual Power Controller驱动,或没有驱动正常运行。

到这里事情还没结束,上述代码控制风扇所使用的实际上是驱动所提供的一个风扇除尘功能(联想电脑管家和IdeaFan也是如此),在实际使用中风扇并非如我们所设想的那样一直保持最高转速状态运行,而是会先以最高速度转约9秒钟,暂停2秒,再以最高转速转9秒,再停顿2秒如此循环往复,直到大约2分钟后风扇恢复正常运行状态,风扇又重新交给系统进行控制。

也就是说,开源项目中的上述代码只能让风扇断断续续地运行,且只能维持2分钟,需要继续改进。

通过不断调试,我找到了能让风扇保持高速运行的方法,代码如下:

void keep_fast() {int interval = 9000; // ms, fine-tuned, see https://www.allstone.lt/ideafan/while (1) {if (read_state() == FAST) {Sleep(interval);fan_control(NORMAL); // Reset the fan to NORMAL mode, than switch to FAST mode as soon as possible.} else {fan_control(FAST);Sleep(10);}}
}

在函数void keep_fast()中,先循环判断风扇当前运行状态,如果不为高速运行状态则不断尝试控制风扇高速运行,当风扇此时为高速运行状态时,维持9秒后将风扇切换回正常运行状态以此重置计时,随后在风扇停止转动前又立即切换回高速运行状态,这样一次快速的切换就能让风扇跳过原来2秒漫长的停顿,取而代之的是0.1秒左右的短暂停顿,这样不断循环就能实现风扇一直以高速状态运行了。

void keep_fast()整合进样例中得到能够完美控制风扇的代码:

#include <stdio.h>
#include <Windows.h>// NORMAL: Fan spins at normal speed.
// FAST: Fan spins at the highest speed.
enum FanMode { NORMAL, FAST };// Control the operating mode of the fan.
int fan_control(enum FanMode mode) {HANDLE hndl = CreateFileW(L"\\\\.\\EnergyDrv", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if (hndl == INVALID_HANDLE_VALUE) {return -1;}// lpInBuffer value: 06 00 00 00  01 00 00 00  01 00 00 00 ~ [ 6, 1, 1 ] (inv endian)DWORD inBuffer[3] = { 6, 1 };inBuffer[2] = mode;DWORD bytesReturned = 0;DeviceIoControl(hndl, 0x831020C0, inBuffer, sizeof(inBuffer), NULL, 0, &bytesReturned, NULL);CloseHandle(hndl);return 1;
}// Get the current operating mode of the fan.
int read_state() {HANDLE hndl = CreateFileW(L"\\\\.\\EnergyDrv", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if (hndl == INVALID_HANDLE_VALUE) {return -1;}// lpInBuffer value: 0E 00 00 00 ~ [ 14 ] (inv endian)DWORD inBuffer = 14;DWORD outBuffer;DWORD bytesReturned = 0;DeviceIoControl(hndl, 0x831020C4, &inBuffer, sizeof(inBuffer), &outBuffer, sizeof(outBuffer), &bytesReturned, NULL);CloseHandle(hndl);if (outBuffer == 3) {return FAST;}return NORMAL;
}// Try to keep the fan in fast mode. Blocking.
void keep_fast() {int interval = 9000; // ms, fine-tuned, see https://www.allstone.lt/ideafan/while (1) {if (read_state() == FAST) {Sleep(interval);fan_control(NORMAL); // Reset the fan to NORMAL mode, than switch to FAST mode as soon as possible.} else {fan_control(FAST);Sleep(10);}}
}int main() {int mode = read_state();switch (mode) {case -1:printf("Failed to open \\\\.\\EnergyDrv\n");break;case FAST:printf("NORMAL mode on\n");fan_control(NORMAL);break;case NORMAL:printf("FAST mode on\n");keep_fast();break;}printf("Enter to exit...");getchar();return 0;
}

相关进一步完善的代码及样例程序我已上传至Github:

https://github.com/jiarandiana0307/Lenovo-Fan-Control

原理探讨

那么这样控制风扇的原理是什么呢?归根结底,笔记本电脑的风扇一般是由嵌入式控制器(Embedded Controller, EC)控制的,我们可以通过改变EC中寄存器的值来控制风扇。

但是,有些电脑的数据手册并没有说明EC用于控制风扇转速的是哪个寄存器,所以,前文提到的开源项目的作者逆向了Lenovo Energy Manager和相关驱动,最终发现可以通过Lenovo ACPI-Compliant Virtual Power Controller驱动控制EC,进而实现控制风扇的目的。

前文代码中出现的\\.\EnergyDrv就是由Lenovo ACPI-Compliant Virtual Power Controller驱动所生成的设备,也是该驱动向外提供的用于控制和访问的接口,上文的代码中直接读写\\.\EnergyDrv这个设备其实就是在与驱动交互,进而实现控制风扇、获取风扇状态的目的。进一步实验也证实了这一点,当这个驱动正常工作时,可以正常访问\\.\EnergyDrv设备,而当卸载了驱动并重启后这个设备就不存在且无法访问了。

不妨大胆假设,只要是安装了Lenovo ACPI-Compliant Virtual Power Controller驱动的联想笔记本电脑,应该都可以实现风扇控制。根据网上的信息和我自己的测试,以下联想机型是可行的:

  • Lenovo G500
  • Lenovo G580
  • Lenovo Ideapad 3 15ALC6 82KU
  • Lenovo Ideapad 3 15ITL6
  • Lenovo IdeaPad 330
  • Lenovo IdeaPad Y510
  • Lenovo Ideapad Z580
  • Lenovo Xiaoxin 15IIL 2020
  • Lenovo Y410P
  • Lenovo Y500
  • Lenovo Y50-70
  • Lenovo Y510p
  • Lenovo Y580
  • Lenovo Z580

但是碍于身边没有其他的联想笔记本电脑,无法进行更多的测试,无法确定其他机型是否使用。

参考

  1. Lenovo-Fan-Control
  2. IdeaFan
  3. FanControl
  4. Lenovo-IdeaPad-Z500-Fan-Controller
  5. Windows Drivers Reverse Engineering Methodology
http://www.xdnf.cn/news/151777.html

相关文章:

  • 从像素到驾驶决策:Python与OpenCV赋能自动驾驶图像识别
  • django之账号管理功能
  • MySQL 数据类型
  • WPF高级用法示例
  • 【含文档+PPT+源码】基于Python校园跑腿管理系统设计与实现
  • C语言中字符类型的定义、存储与输出详解
  • 我爱学算法之—— 二分查找(上)
  • OTA和IAP的关系
  • Pycharm 代理配置
  • 案例拆解:主数据平台如何支撑智能推荐系统精准发力?
  • 魔百盒CM311-3-YST代工-晨星MSO9385芯片-2+8G-免拆卡刷通刷固件包
  • 【软考-架构】14、软件可靠性基础
  • 【优选算法 | 滑动窗口】滑动窗口算法:高效处理子数组和子串问题
  • Flink反压问题解析
  • WPF实现类似Microsoft Visual Studio2022界面效果及动态生成界面技术
  • WPF之项目创建
  • 【那些年踩过的坑】Docker换源加速详细教程(截至2025年4月)
  • 【GoChat】密码处理与实现JWT+进行功能单测
  • 【网络入侵检测】基于源码分析Suricata的PCAP模式
  • 小火电视桌面 TV版 老旧历史版本安装包 官方免费下载
  • 应力腐蚀环功能及指标
  • 模块化集成建筑(MiC建筑):重新定义未来人居空间
  • 深度探索多模态数据:从声音到图像的奇妙世界
  • 什么是数据湖?应用场景有哪些?
  • Linux文件管理2
  • 人工智能在创意设计中的应用:激发无限可能
  • Codeforces Round 1019 (Div. 2) ABC
  • Vue2升级到Vue3
  • 模方ModelFun是什么?如何安装?
  • C语言程序环境和预处理详解