Blue Sky N141WU 팬에서 열을 방출할 때 소음이 발생함

Blue Sky N141WU 팬에서 열을 방출할 때 소음이 발생함

방금 하나 샀어요파란 하늘N141WU(시스템 76에서는 galago pro로 알려짐)는 덴마크 PC 매장에서 판매됩니다.

대부분의 경우 잘 작동하지만, 작업량이 많은 후 팬이 회전을 멈추면 매우 높은 소리가 나기 시작하고 팬이 멈춥니다(팬이 회전하는 데 필요한 전압을 공급받지 못하는 것처럼 들림).

매장에 전화했더니 솔루션이 일부 Windows 소프트웨어였지만 PC에는 창이 없었고 Linux를 실행하기 위해 먼저 구입했습니다(갈라고 프로와 동일했기 때문에 작동할 것이라고 생각했습니다).

노트북이 system76 Linux를 실행하고 있기 때문에 이것이 가능해야 한다고 생각했습니다.

더 나은 실행을 위해 설치해야 할 것이 있나요? 아니면 팬들을 행복하게 해줄 BIOS 트릭을 아는 사람이 있나요?

저는 Solus 3.X를 실행하고 있습니다. 여기서 x는 삽입하는 데 시간이 걸리는 만큼의 9입니다 ;-)

키보드 단축키 Fn+ 1(system76 galago pro 시끄러운 팬에 대한 게시물에 있음)를 두 번 사용하면 팬이 켜지고 꺼집니다. 이렇게 하면 다음 번 하드 로드까지 소리가 들리지 않습니다.

원본 게시물 이후 두 가지 사실을 발견했습니다.

  • system76에는 일부 펌웨어 업데이트가 있지만 다른 리셀러의 노트북을 가지고 있는 사람들에게 이를 보낼 의향이 있는지 누가 알겠습니까? (그들에게 물어보면 좋을 것 같습니다.)
  • System76에는 팬 제어를 제공할 수 있는 우분투의 system76-dkms라는 패키지가 있지만 Solus 저장소에는 없습니다. (오늘 밤 Solus irc에 포장이 어떻게 작동하는지 물어볼 수도 있습니다.)

답변1

Windows 10에서 다음 코드를 사용하여 성공했습니다. 이는 팬의 두 가지 가능한 오류를 처리합니다. 즉, "팬 부하 = 0에서 팬이 갑자기 정지합니다." 및 "rpm > 10000에서 팬이 갑자기 정지하고 팬에서 전기 소음이 들립니다." 백그라운드에서 실행하려면 Winring0(예: ThrottleStop)을 로드하는 프로그램이 필요합니다. Blue Sky 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 기반 운영 체제에서 사용하기 위해 코드를 이식하지 않았습니다. 그렇게 하려면 다음이 필요합니다.

  • 합계 함수를 및 WInp(port)로 대체합니다 .WOutp(port, value)inb(port)outb(value, port)
  • ioperm처음에 추가하세요.이 코드 조각에서는,
  • Sleep(milliseconds)로 교체 usleep(microseconds),
  • 이제 쓸모없는 포함, 정의, 구조 및 핸들을 모두 정리하고,
  • GetModuleFileNameA동등한 기능으로 교체하십시오.

관련 정보