Clevo N141WU 風扇冷卻時的噪音
我剛從丹麥 PC 商店買了一台Clevo N141WU(在系統 76 上它被稱為 galago pro)。
它主要工作得非常好,但是當風扇旋轉時(在繁重的工作量之後)它開始發出非常高的聲音並且風扇停止(聽起來風扇沒有獲得旋轉所需的電壓)。
我打電話給商店,他們的解決方案是一些 Windows 軟體,但 PC 沒有 Windows,我買它是為了執行 Linux(因為它與 galago pro 相同,我認為它會工作)。
由於筆記型電腦從 system76 執行 Linux,我認為它應該是可行的。
我應該安裝什麼讓它執行得更好,或者有人知道讓粉絲開心的 BIOS 技巧嗎?
我正在執行 Solus 3.X,其中 x 是您想花時間插入的許多九;-)
使用鍵盤快捷鍵
Fn
+1
(在有關 system76 galago pro 的大聲風扇的執行緒中找到)兩次將打開和關閉風扇。這將消除聲音,直到下一個硬負載消失。自最初發布以來,我發現了兩件事:
- system76 有一些韌體更新,但誰知道他們是否願意將它發送給另一個經銷商的筆記型電腦(我會很好地問他們)
- System76 在 ubuntu 中有一個名為 system76-dkms 的軟體包,它可能提供風扇控制,但它不在 Solus 儲存庫中。(今晚我可能會在 Solus irc 中詢問包裝的工作原理。)
我使用下面的程式碼在 Windows 10 中取得了成功。它處理風扇可能出現的兩種故障,即:“風扇突然停止,風扇佔空比=0”和“風扇突然停止,rpm > 10000,並且可以聽到來自風扇的電雜訊”。它需要一個載入 Winring0 的程序,例如在後台執行的 ThrottleStop。我沒有在安裝了 Clevo Control Center 的情況下對其進行測試。它與 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 的 oses 中使用。這樣做需要:
- 用and替換
WInp(port)
andWOutp(port, value)
函式,inb(port)``outb(value, port)
- 在此程式碼片段
ioperm
的開頭添加,- 替換
Sleep(milliseconds)
為usleep(microseconds)
,- 清理所有現在無用的包含、定義、結構和句柄,
- 替換
GetModuleFileNameA
為等效功能。