我刚买了一个蓝天N141WU(在系统 76 中称为 galago pro)来自丹麦 PC 商店。
它大多数情况下工作得很好,但是当风扇旋转下来时(在繁重的工作负载之后),它开始发出非常高的声音并且风扇停止(听起来风扇没有获得旋转所需的电压)。
我给商店打电话,他们的解决方案是一些 Windows 软件,但 PC 没有 Windows,我买它首先是为了运行 Linux(因为它与 galago pro 相同,我认为它可以工作)。
由于笔记本电脑运行的是system76的Linux,我认为这应该是可行的。
我应该安装什么东西才能让它运行得更好,或者有人知道BIOS技巧可以让粉丝高兴吗?
我正在运行 Solus 3.X,其中 x 是您想要花时间插入的任意多个 9 ;-)
使用键盘快捷键Fn+ 1(在有关 system76 galago pro 大声风扇的帖子中找到)两次将打开和关闭风扇。这将消除声音,直到下一个硬负载消失。
自从最初发布以来,我发现了两件事:
- system76 有一些固件更新,但谁知道他们是否愿意将其发送给拥有其他经销商的笔记本电脑的人(我会很好地询问他们)
- System76 在 ubuntu 中有一个名为 system76-dkms 的软件包,它可能提供风扇控制,但它不在 Solus 存储库中。 (今晚我可能会在 Solus irc 上询问包装是如何工作的。)
答案1
我在 Windows 10 中使用以下代码成功了。它处理风扇可能出现的两种故障,即:“风扇在风扇负载 = 0 时突然停止”和“风扇在 rpm > 10000 时突然停止,并可听到从风扇发出的电噪音”。它需要加载 Winring0 的程序(例如 ThrottleStop)在后台运行。我还没有在安装了蓝天控制中心的情况下对其进行测试。它使用 MinGW-w64 进行编译\yourmingwpath\i686-w64-mingw32-gcc.exe \yoursourcepath\main.c -o \yourexepath\main.exe -Wall -mwindows
#define UNICODE 1
#define _UNICODE 1
#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#include <stddef.h>
#define OLS_TYPE 40000
#define IOCTL_OLS_READ_IO_PORT_BYTE CTL_CODE(OLS_TYPE, 0x833, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_OLS_WRITE_IO_PORT_BYTE CTL_CODE(OLS_TYPE, 0x836, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define EC_SC 0x66
#define EC_DATA 0x62
#define IBF 1
#define OBF 0
#define EC_SC_READ_CMD 0x80
typedef struct _OLS_WRITE_IO_PORT_INPUT {
ULONG PortNumber;
union {
ULONG LongData;
USHORT ShortData;
UCHAR CharData;
};
} OLS_WRITE_IO_PORT_INPUT;
HANDLE hDevice = INVALID_HANDLE_VALUE;
char filename[1024] = {0};
WORD WInp(WORD port) {
FILE *outlog;
unsigned int error = 0;
DWORD returnedLength = 0;
WORD value = 0;
BOOL bResult = FALSE;
bResult = DeviceIoControl(hDevice,
IOCTL_OLS_READ_IO_PORT_BYTE,
&port, sizeof(port),
&value, sizeof(value),
&returnedLength,
NULL );
if (bResult) {
/*outlog = fopen(filename, "ab");
fprintf(outlog, "port=%d, value=%d, retlength=%d\n", port, value, (int)returnedLength);
fclose(outlog);*/
return value;
} else {
error = GetLastError();
outlog = fopen(filename, "ab");
fprintf(outlog, "DeviceIoControl (read) failed. Error %d.\n", error);
fclose(outlog);
CloseHandle(hDevice);
return 0;
}
}
WORD WOutp(WORD port, BYTE value) {
FILE *outlog;
unsigned int error = 0;
DWORD returnedLength = 0;
BOOL bResult = FALSE;
DWORD length = 0;
OLS_WRITE_IO_PORT_INPUT inBuf;
inBuf.CharData = value;
inBuf.PortNumber = port;
length = offsetof(OLS_WRITE_IO_PORT_INPUT, CharData) + sizeof(inBuf.CharData);
bResult = DeviceIoControl(hDevice,
IOCTL_OLS_WRITE_IO_PORT_BYTE,
&inBuf, length,
NULL, 0,
&returnedLength,
NULL);
if (bResult) {
/*outlog = fopen(filename, "ab");
fprintf(outlog, "port=%d, value=%d, retlength=%d\n", port, value, (int)returnedLength);
fclose(outlog);*/
return value;
} else {
error = GetLastError();
outlog = fopen(filename, "ab");
fprintf(outlog, "DeviceIoControl (write) failed. Error %d.\n", error);
fclose(outlog);
CloseHandle(hDevice);
return 0;
}
}
int wait_ec(const unsigned int port, const unsigned int flag, const char value) {
int i = 0;
unsigned char data = WInp(port);
while (((data >> flag)&0x1)!=value) {
Sleep(1);
if (i>10) {
//printf( "Still waiting on port 0x%x, data=0x%x, flag=0x%x, value=0x%x, i=%d\n", port, data, flag, value, i);
return 0;
}
i++;
data = WInp(port);
}
//printf( "Succeeded port 0x%x, data=0x%x, flag=0x%x, value=0x%x, i=%d\n", port, data, flag, value, i);
return 0;
}
unsigned char read_ec(const unsigned int port) {
wait_ec(EC_SC, IBF, 0);
WOutp(EC_SC, EC_SC_READ_CMD);
wait_ec(EC_SC, IBF, 0);
WOutp(EC_DATA, port);
wait_ec(EC_SC, OBF, 1);
return WInp(EC_DATA);
}
void do_ec(const unsigned int cmd, const unsigned int port, const unsigned char value) {
wait_ec(EC_SC, IBF, 0);
WOutp(EC_SC, cmd);
wait_ec(EC_SC, IBF, 0);
WOutp(EC_DATA, port);
wait_ec(EC_SC, IBF, 0);
WOutp(EC_DATA, value);
wait_ec(EC_SC, IBF, 0);
return;
}
void write_fan_duty(int duty_percentage) {
do_ec(0x99, 0x01, (int)(((double) duty_percentage) / 100.0 * 255.0));
//FILE *outlog = fopen(filename, "ab");
//fprintf(outlog, "Fan set to %d\n", duty_percentage);
//fclose(outlog);
return;
}
int main(){
// get the path of this executable and append "stdout.txt\0" to it for the log file.
int i = GetModuleFileNameA(NULL, filename, 1024);
for (;i>0 && filename[i] != '\\';i--) {}
char *dest=&filename[i+1], *src="stdout.txt\0";
for (i=0;i<11;i++) dest[i]=src[i];
FILE *outlog;
outlog = fopen(filename, "wb"); // clear the log at every start
fclose(outlog);
unsigned int error = 0;
// I could loop CreateFile until a valid handle is returned (which means that WinRing0_1_2_0 got started by throttlestop)
// but windows defender blocks the program at start for a few seconds with 100% core usage if i do that.
Sleep(3000); // ... so this is what i have to do instead. Disgusting.
hDevice = CreateFile(L"\\\\.\\WinRing0_1_2_0",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hDevice == INVALID_HANDLE_VALUE) {
error = GetLastError();
if (error == ERROR_ACCESS_DENIED) {
outlog = fopen(filename, "ab");
fprintf(outlog, "CreateFile failed. Please retry as administrator.\n");
fclose(outlog);
} else if (error == ERROR_FILE_NOT_FOUND) {
outlog = fopen(filename, "ab");
fprintf(outlog, "CreateFile failed. The WinRing0 driver is probably not loaded yet.\n");
fclose(outlog);
} else {
outlog = fopen(filename, "ab");
fprintf(outlog, "CreateFile failed. Error %d.\n", error);
fclose(outlog);
}
return 0;
}
int val_duty, raw_rpm, val_rpm, temp, last_valid_duty=50;
while (1) {
val_duty = (int) ((double) (read_ec(0xCE)) / 255.0 * 100.0);
raw_rpm = (read_ec(0xD0) << 8) + (read_ec(0xD1));
if (raw_rpm == 0)
val_rpm = 0;
else
val_rpm = 2156220 / raw_rpm;
temp = read_ec(0x07);
//outlog = fopen(filename, "ab");
//fprintf(outlog, "FAN Duty: %d%%, FAN RPMs: %d RPM, CPU Temp: %d°C\n", val_duty, val_rpm, temp);
//fclose(outlog);
if (val_rpm > 10000 || val_duty == 0) {
// there are two malfunctions that can happen:
// - fan stops suddenly with fan duty=0
// - fan stops suddenly with rpm > 10000 with a electric noise that can be heard coming from the fan.
outlog = fopen(filename, "ab");
fprintf(outlog, "MALFUNCTION DETECTED: val_rpm=%d, val_duty=%d\n", val_rpm, val_duty);
fclose(outlog);
// Panic :O
if (last_valid_duty<80) {
write_fan_duty(last_valid_duty+20);
} else {
write_fan_duty(last_valid_duty-20);
}
} else {
// This is the custom fan curve code. Can be adjusted to your liking.
// It's required because i don't know to to set the fan back to "automatic" without manual intervention.
// Can definitely conflict with other fan speed programs, so be careful.
// Writes to fan speed are limited to only if the target fan duty changes.
if (temp<55) {
if (last_valid_duty > 32 || last_valid_duty < 29) write_fan_duty(31);
} else if (temp<60) {
if (last_valid_duty > 42 || last_valid_duty < 39) write_fan_duty(41);
} else if (temp<65) {
if (last_valid_duty > 52 || last_valid_duty < 49) write_fan_duty(51);
} else if (temp<70) {
if (last_valid_duty > 62 || last_valid_duty < 59) write_fan_duty(61);
} else if (temp<75) {
if (last_valid_duty > 72 || last_valid_duty < 69) write_fan_duty(71);
} else if (temp<80) {
if (last_valid_duty > 82 || last_valid_duty < 79) write_fan_duty(81);
} else if (temp<85) {
if (last_valid_duty > 92 || last_valid_duty < 89) write_fan_duty(91);
} else {
if (last_valid_duty < 98) write_fan_duty(100);
}
last_valid_duty = val_duty;
}
Sleep(200);
}
return 0;
}
我还没有移植代码以供在基于 linux 的操作系统中使用。这样做需要:
- 用和替换
WInp(port)
和WOutp(port, value)
函数inb(port)
,outb(value, port)
ioperm
在开头添加,例如在此代码片段中,- 替换
Sleep(milliseconds)
为usleep(microseconds)
, - 清理所有现在无用的包含、定义、结构和句柄,
GetModuleFileNameA
用等效函数替换。