Мне удалось успешно запустить Windows 10 с помощью приведенного ниже кода. Он обрабатывает обе неисправности, которые могут быть у вентилятора: :«вентилятор внезапно останавливается с режимом работы вентилятора = 0» и «вентилятор внезапно останавливается со скоростью вращения > 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
#include
#include
#include
#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
эквивалентной функцией. Мне удалось преобразовать пакет deb в локальный пакет, который можно установить в void linux с помощью скрипта xdebhttps://github.com/toluschr/xdeb.
Было легко преобразовать и установить с помощью сценария xdeb.