amazonparrot / aorus_waterforce_x240_kernel_module

Waterforce X 240, 280, 360 Kernel Module for Automatic Fan and Pump Control
2 stars 0 forks source link

Errors and Wrong path to the value of temperature #3

Open Jackiiiii opened 12 months ago

Jackiiiii commented 12 months ago

Hello, first of all, thank you very much for your code.

Unfortunately, it didn't work right away...

The line 153 with: sprintf(path, "/sys/class/thermal/thermal_zone%d/temp", priv->cputhermalzoneid); does not match my CPU and system; I am using Proxmox Debian and an AMD Threadripper. I changed line 153 to the path where my system actually shows the value that your code needs. However, I have the problem that your code posts a lot of errors in my console. I have deactivated it in your code, but I would like to know why this is happening and whether it is a serious issue?

20231121-050416 1

amazonparrot commented 11 months ago

If you changed line 153, you might need to change line 131 as well, since line 131 would try to search your cpu temp. sensor, I haven't tested this code with with Proxmox yet, but I think I will try it whenever I have the time.

Jackiiiii commented 11 months ago

do i need line 131? or can i just mark it out // # something like that?

Jackiiiii commented 11 months ago

I think i got everything right, could you maybe explain me what this is for? - >#define CPU_TEMP_CMD_LEN 9

Jackiiiii commented 11 months ago

There was a kernal update and i update the lm-sernsors What ever i do this error is not fixable idk what i should do even that i give the direct path still there that error...

But the Watercooler is working fine....

2023-12-12T12:52:37.042999+01:00 kernel: [   29.398101] Error opening file /sys/class/hwmon/hwmon3/name
2023-12-12T12:52:37.043008+01:00 kernel: [   29.398135] Error opening file /sys/class/hwmon/hwmon6/name
2023-12-12T12:52:37.090992+01:00 kernel: [   29.446640] Error opening file /sys/class/hwmon/hwmon3/name
2023-12-12T12:52:37.090996+01:00 kernel: [   29.446659] Error opening file /sys/class/hwmon/hwmon6/name
2023-12-12T12:52:39.154993+01:00 kernel: [   31.510077] Error opening file /sys/class/hwmon/hwmon3/name
2023-12-12T12:52:39.155000+01:00 kernel: [   31.510106] Error opening file /sys/class/hwmon/hwmon6/name
2023-12-12T12:52:39.202992+01:00 kernel: [   31.558523] Error opening file /sys/class/hwmon/hwmon3/name
2023-12-12T12:52:39.202996+01:00 kernel: [   31.558542] Error opening file /sys/class/hwmon/hwmon6/name
amazonparrot commented 11 months ago

There was a kernal update and i update the lm-sernsors What ever i do this error is not fixable idk what i should do even that i give the direct path still there that error...

But the Watercooler is working fine....

2023-12-12T12:52:37.042999+01:00 kernel: [   29.398101] Error opening file /sys/class/hwmon/hwmon3/name
2023-12-12T12:52:37.043008+01:00 kernel: [   29.398135] Error opening file /sys/class/hwmon/hwmon6/name
2023-12-12T12:52:37.090992+01:00 kernel: [   29.446640] Error opening file /sys/class/hwmon/hwmon3/name
2023-12-12T12:52:37.090996+01:00 kernel: [   29.446659] Error opening file /sys/class/hwmon/hwmon6/name
2023-12-12T12:52:39.154993+01:00 kernel: [   31.510077] Error opening file /sys/class/hwmon/hwmon3/name
2023-12-12T12:52:39.155000+01:00 kernel: [   31.510106] Error opening file /sys/class/hwmon/hwmon6/name
2023-12-12T12:52:39.202992+01:00 kernel: [   31.558523] Error opening file /sys/class/hwmon/hwmon3/name
2023-12-12T12:52:39.202996+01:00 kernel: [   31.558542] Error opening file /sys/class/hwmon/hwmon6/name

May I know your current kernel version? Are you using it in proxmox? I didn't try it on Proxmox yet. I will be testing on Proxmox soon though, please feel free to let me know the Proxmox version you are using so that I can test with it.

Jackiiiii commented 11 months ago

Kernel 6.5.11-7-pve

And this is not my cpu temp its something else /sys/class/thermal/thermal_zone%d/temp",priv->cputhermalzoneid); taking temp from /sys/class/hwmon/hwmon5/temp1_input

it was befor on hwmon4 <- now its hwmon5 since the update and i need to edite it allways my self cause it cant fine it by self.

is it because maybe i got just a diffrent cpu, using threadripper 3960x

amazonparrot commented 11 months ago

Kernel 6.5.11-7-pve

And this is not my cpu temp its something else /sys/class/thermal/thermal_zone%d/temp",priv->cputhermalzoneid); taking temp from /sys/class/hwmon/hwmon5/temp1_input

it was befor on hwmon4 <- now its hwmon5 since the update and i need to edite it allways my self cause it cant fine it by self.

is it because maybe i got just a diffrent cpu, using threadripper 3960x

Could you help me to check each of your /sys/class/thermal/thermal_zone?/type, because I don't have a threadripper to test with at the moment, and I would need to know which of the thermal_zone number correspond to your CPU temperature.

For you information, I tested PVE with my i9-13900 CPU and it runs fine, so I think it's because of my code does not recognize your CPU yet, your help to help me identify your CPU would be helpful.

Jackiiiii commented 11 months ago
# dir
cooling_device0   cooling_device15  cooling_device21  cooling_device28  cooling_device34  cooling_device40  cooling_device47  thermal_zone1
cooling_device1   cooling_device16  cooling_device22  cooling_device29  cooling_device35  cooling_device41  cooling_device5
cooling_device10  cooling_device17  cooling_device23  cooling_device3   cooling_device36  cooling_device42  cooling_device6
cooling_device11  cooling_device18  cooling_device24  cooling_device30  cooling_device37  cooling_device43  cooling_device7
cooling_device12  cooling_device19  cooling_device25  cooling_device31  cooling_device38  cooling_device44  cooling_device8
cooling_device13  cooling_device2   cooling_device26  cooling_device32  cooling_device39  cooling_device45  cooling_device9
cooling_device14  cooling_device20  cooling_device27  cooling_device33  cooling_device4   cooling_device46  thermal_zone0

its thermal_zone0 and 1 i think but its strangely not the cpu temp.. its says an other temp of an other device. sure, u got discord or something maybe and u wana have a look by your self maybe?

amazonparrot commented 11 months ago
# dir
cooling_device0   cooling_device15  cooling_device21  cooling_device28  cooling_device34  cooling_device40  cooling_device47  thermal_zone1
cooling_device1   cooling_device16  cooling_device22  cooling_device29  cooling_device35  cooling_device41  cooling_device5
cooling_device10  cooling_device17  cooling_device23  cooling_device3   cooling_device36  cooling_device42  cooling_device6
cooling_device11  cooling_device18  cooling_device24  cooling_device30  cooling_device37  cooling_device43  cooling_device7
cooling_device12  cooling_device19  cooling_device25  cooling_device31  cooling_device38  cooling_device44  cooling_device8
cooling_device13  cooling_device2   cooling_device26  cooling_device32  cooling_device39  cooling_device45  cooling_device9
cooling_device14  cooling_device20  cooling_device27  cooling_device33  cooling_device4   cooling_device46  thermal_zone0

its thermal_zone0 and 1 i think but its strangely not the cpu temp.. its says an other temp of an other device. sure, u got discord or something maybe and u wana have a look by your self maybe?

could you help to run the following commands and show me the result? cat thermal_zone0/type cat thermal_zone1/type

Jackiiiii commented 11 months ago
cat thermal_zone0/type
acpitz
cat thermal_zone1/type
iwlwifi_1
amazonparrot commented 11 months ago
cat thermal_zone0/type
acpitz
cat thermal_zone1/type
iwlwifi_1

You are right both are not the CPU temp, when you run "sensors" do you see your CPU temperature?

Jackiiiii commented 11 months ago
sensors
waterforce-hid-3-1
Adapter: HID adapter
Fan speed:    2046 RPM
Pump speed:   3101 RPM
Coolant temp:  +39.0°C
Fan duty:      77.00 uW
Pump duty:     95.00 uW

it8792-isa-0a60
Adapter: ISA adapter
in0:           1.36 V  (min =  +0.00 V, max =  +2.78 V)
in1:           1.51 V  (min =  +0.00 V, max =  +2.78 V)
in2:         992.00 mV (min =  +0.00 V, max =  +2.78 V)
in3:         283.00 mV (min =  +0.00 V, max =  +2.78 V)
in4:           1.79 V  (min =  +0.00 V, max =  +2.78 V)
in5:           1.50 V  (min =  +0.00 V, max =  +2.78 V)
in6:           2.78 V  (min =  +0.00 V, max =  +2.78 V)  ALARM
3VSB:          3.36 V  (min =  +0.00 V, max =  +5.56 V)
Vbat:          3.21 V
fan1:           0 RPM  (min =    0 RPM)
fan2:           0 RPM  (min =    0 RPM)
fan3:           0 RPM  (min =    0 RPM)
temp1:        +48.0°C  (low  = +127.0°C, high = +127.0°C)  sensor = thermistor
temp2:        -55.0°C  (low  = +127.0°C, high = +127.0°C)  sensor = thermistor
temp3:        +46.0°C  (low  = +127.0°C, high = +127.0°C)  sensor = thermistor
intrusion0:  ALARM

nvme-pci-0100
Adapter: PCI adapter
Composite:    +47.9°C  (low  =  -5.2°C, high = +83.8°C)
                       (crit = +87.8°C)

acpitz-acpi-0
Adapter: ACPI interface
temp1:        +16.8°C  (crit = +20.8°C)

iwlwifi_1-virtual-0
Adapter: Virtual device
temp1:        +48.0°C

nouveau-pci-4a00
Adapter: PCI adapter
GPU core:    987.00 mV (min =  +0.60 V, max =  +1.27 V)
fan1:           0 RPM
temp1:        +49.0°C  (high = +95.0°C, hyst =  +3.0°C)
                       (crit = +105.0°C, hyst =  +5.0°C)
                       (emerg = +135.0°C, hyst =  +5.0°C)
power1:       17.52 W  (crit = 177.00 mW)

k10temp-pci-00c3
Adapter: PCI adapter
Tctl:         +58.6°C
Tccd1:        +48.0°C
Tccd3:        +46.8°C
Tccd5:        +47.5°C
Tccd7:        +43.5°C

nvme-pci-0200
Adapter: PCI adapter
Composite:    +49.9°C  (low  =  -5.2°C, high = +83.8°C)
                       (crit = +87.8°C)
Jackiiiii commented 11 months ago

its in - > /sys/class/hwmon/hwmon5/temp1_input But befor update it was in hwmon4 And i have turn of the print cause it trying to search in hwmon1 2 3 4 5 6 and there is nothing.

Jackiiiii commented 11 months ago

to get it work even with error i dont this:

        sprintf(path_string,"/sys/class/hwmon/hwmon%d/name",i);
        //cat /sys/class/hwmon/hwmon5/name
            for (u8 i=0;i<9;i++) {
        sprintf(path_string,"/sys/class/thermal/cooling_device%d/type",i);
    //sprintf(path,"/sys/class/thermal/thermal_zone%d/temp",priv->cputhermalzoneid);
    sprintf(path,"/sys/class/hwmon/hwmon5/temp1_input",priv->cputhermalzoneid);
amazonparrot commented 11 months ago

its in - > /sys/class/hwmon/hwmon5/temp1_input But befor update it was in hwmon4 And i have turn of the print cause it trying to search in hwmon1 2 3 4 5 6 and there is nothing.

could you help to run the following command? cat /sys/class/hwmon/hwmon5/name

Jackiiiii commented 11 months ago

Thats the CPU:

cat /sys/class/hwmon/hwmon5/name
k10temp
Jackiiiii commented 11 months ago

/sys/class/hwmon/hwmon5/temp1_input

temp1 is i think

Tctl: +58.6°C

cause checking it show the same temp as sensors command

ls /sys/class/hwmon/hwmon5/
device  power      temp1_input  temp3_input  temp5_input  temp7_input  temp9_input  uevent
name    subsystem  temp1_label  temp3_label  temp5_label  temp7_label  temp9_label

It could be that may mainboard handel it otherway.

amazonparrot commented 11 months ago

/sys/class/hwmon/hwmon5/temp1_input

temp1 is i think

Tctl: +58.6°C

cause checking it show the same temp as sensors command

ls /sys/class/hwmon/hwmon5/
device  power      temp1_input  temp3_input  temp5_input  temp7_input  temp9_input  uevent
name    subsystem  temp1_label  temp3_label  temp5_label  temp7_label  temp9_label

It could be that may mainboard handel it otherway.

seems your CPU temp is in this it8792-isa-0a60? would you be using Gigabyte Motherboard X570 Chipset?

Jackiiiii commented 11 months ago

when i turn on prime test for cpu it dont go high the temp in it8792-isa-0a60

the cpu is k10temp-pci-00c3

k10temp-pci-00c3
Adapter: PCI adapter
Tctl:         +52.5°C
Tccd1:        +51.2°C
Tccd3:        +49.0°C
Tccd5:        +45.2°C
Tccd7:        +42.0°C

Main sensor is Tctl <--

i got gigabyte TRX40-AORUS-XTREME

amazonparrot commented 11 months ago

when i turn on prime test for cpu it dont go high the temp in it8792-isa-0a60

the cpu is k10temp-pci-00c3

k10temp-pci-00c3
Adapter: PCI adapter
Tctl:         +52.5°C
Tccd1:        +51.2°C
Tccd3:        +49.0°C
Tccd5:        +45.2°C
Tccd7:        +42.0°C

Main sensor is Tctl <--

i got gigabyte TRX40-AORUS-XTREME

please try to change line to 131: sprintf(path_string,"/sys/class/hwmon/hwmon%d/name",i); 110: if (strcmp(temp_string,"k10temp\n")==0) 153: sprintf(path,"/sys/class/hwmon/hwmon%d/temp1_input",priv->cputhermalzoneid);

Jackiiiii commented 11 months ago

20231214_201129.jpg

Not working at all, cooler stuck.

Jackiiiii commented 11 months ago

this works but has same errors too and print is off:

/*
 * POC code use at your own risk
 * modifed based on https://github.com/aleksamagicka/waterforce-hwmon/
 */
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <linux/buffer_head.h>
#include <asm/unaligned.h>
#include <linux/hid.h>
#include <linux/hwmon.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cpufreq.h>
#include <linux/timer.h>
#include <linux/thermal.h>
#include <linux/kthread.h>
#define USB_VENDOR_ID_GIGABYTE      0x1044
#define USB_PRODUCT_ID_WATERFORCE_1 0x7a4d  /* Gigabyte AORUS WATERFORCE X (240, 280, 360) */
#define USB_PRODUCT_ID_WATERFORCE_2 0x7a52  /* Gigabyte AORUS WATERFORCE X 360G */
#define USB_PRODUCT_ID_WATERFORCE_3 0x7a53  /* Gigabyte AORUS WATERFORCE EX 360 */

#define STATUS_VALIDITY     2   /* seconds */
#define MAX_REPORT_LENGTH   6144

#define WATERFORCE_TEMP_SENSOR  0xD
#define WATERFORCE_FAN_SPEED    0x02
#define WATERFORCE_PUMP_SPEED   0x05
#define WATERFORCE_FAN_DUTY 0x08
#define WATERFORCE_PUMP_DUTY    0x09
#define CPU_TEMP_CMD_LEN    9
#define FAN_COLOR_CMD_LEN   5
#define TIMER_WAKEUP_S      2
DECLARE_COMPLETION(status_report_received);

static const u8 get_status_cmd[] = { 0x99, 0xDA };
static const u8 cpu_temp_cmd[] = { 0x99,0xE0,0x00,0x25,0x20,0x05,0x05,0x18,0x30 };
static const u8 fan_color_cmd[] = { 0x99,0xCD,0xFF,0xFF,0xFF };

static const char *const waterforce_temp_label[] = {
    "Coolant temp",
};

static const char *const waterforce_speed_label[] = {
    "Fan speed",
    "Pump speed",
    "Fan duty",
    "Pump duty"
};
static const char *const waterforce_pwm_label[] = {
    "Fan duty",
    "Pump duty"
};
static struct timer_list waterforce_timer;
struct hid_device *waterforce_device=NULL;

struct waterforce_data {
    struct hid_device *hdev;
    struct device *hwmon_dev;
    struct mutex buffer_lock;   /* For locking access to buffer */

    /* Sensor data */
    s32 temp_input[1];
    u16 speed_input[4]; /* Fan and pump speed in RPM */
    u16 duty_input[4];  /* Fan and pump duty in 0-100% */
        u8 cpu_temp;
    u8 ncore;
    u8 nthread;
        u8 cpufreqGhz1;
        u8 cpufreqGhz2;
    u8 fanColorR;
    u8 fanColorG;
    u8 fanColorB;
    u8 *buffer;
        u8 cputhermalzoneid;
    u8 hwmonid;
        bool updating;
    unsigned long updated;  /* jiffies */
};
static int read_file(char * path,char *buf,int buflen) {
    struct file *temp_file;

    // Open the temperature file for reading
    temp_file = filp_open(path, O_RDONLY, 0);

    if (IS_ERR(temp_file)) {
        printk(KERN_ERR "Error opening file %s\n", path);
        return -ENOENT;
    }

    // Read the value as a string
    memset(buf, 0, buflen);
    kernel_read(temp_file, buf, buflen - 1, &temp_file->f_pos);

    // Close the temperature file
    filp_close(temp_file, NULL);
    return 0;
}
static int get_hwmon_id_of_waterforce(struct waterforce_data *priv)
{

    char temp_string[20];
    char path_string[60];
    for (u8 i=0;i<9;i++) {
        sprintf(path_string,"/sys/class/hwmon/hwmon%d/name",i);

        if (read_file(path_string,temp_string,sizeof(temp_string))==0) {

                    if (strcmp(temp_string,"k10temp\n")==0) {

            //    printk("HWMon name %s %d\n",temp_string,i);
            priv->hwmonid=i;
            break;
            }

            }

    }

    return 0;
}

static int get_thermal_zone_id_of_cpu(struct waterforce_data *priv)
{

    char temp_string[20];
    char path_string[60];
    for (u8 i=0;i<9;i++) {
        sprintf(path_string,"/sys/class/hwmon/hwmon%d/name",i);

        if (read_file(path_string,temp_string,sizeof(temp_string))==0) {

                    if (strcmp(temp_string,"x86_pkg_temp\n")==0) {
            priv->cputhermalzoneid=i;
              //  printk("Thermal Zone type %s %d\n",temp_string,i);
            break;
            }

            }

    }

    return 0;
}
static int get_cpu_temp(struct waterforce_data *priv)
{
    int temp_millicelsius;
    char temp_string[10];
    struct file *temp_file;
    char path[60];
    sprintf(path,"/sys/class/hwmon/hwmon%d/temp1_input",priv->cputhermalzoneid);

    // Open the temperature file for reading
    temp_file = filp_open(path, O_RDONLY, 0);

    if (IS_ERR(temp_file)) {
        printk(KERN_ERR "Error opening temperature file %s\n", path);
        return -ENOENT;
    }

    // Read the temperature value as a string
    memset(temp_string, 0, sizeof(temp_string));
    kernel_read(temp_file, temp_string, sizeof(temp_string) - 1, &temp_file->f_pos);

    // Close the temperature file
    filp_close(temp_file, NULL);

    // Convert the temperature string to an integer
    sscanf(temp_string, "%d", &temp_millicelsius);
    priv->cpu_temp=temp_millicelsius/1000;
    // Convert the temperature to degrees Celsius and print it
    printk(KERN_INFO "CPU temperature: %d.%d C\n", priv->cpu_temp, temp_millicelsius % 1000);

    return 0;
}
static int get_cpu_freq(struct waterforce_data *priv)
{
    unsigned cpu = cpumask_first(cpu_online_mask);
        priv->nthread=nr_cpu_ids;
        unsigned maxfreq=0;
        while (cpu < nr_cpu_ids) {
//      struct cpufreq_policy policy;
//      cpufreq_get_policy(&policy,cpu);
        unsigned freq=cpufreq_quick_get_max(cpu);
//      unsigned freq=policy.cur;
        if (freq>maxfreq) { maxfreq=freq; }
 //              pr_info("CPU: %u, freq: %u kHz\n", cpu, freq);
                cpu = cpumask_next(cpu, cpu_online_mask);
        }

    priv->cpufreqGhz1=maxfreq/1000000;
    priv->cpufreqGhz2=(maxfreq%1000000)/100000;
//  printk("CPU Max freq: %d.%d",priv->cpufreqGhz1,priv->cpufreqGhz2);
    return 0;
}
/*
 * Writes the command to the device with the rest of the report (up to 64 bytes) filled
 * with zeroes
 */
static int waterforce_write_expanded(struct waterforce_data *priv, const u8 *cmd, int cmd_length)
{
    int ret=0;

    mutex_lock(&priv->buffer_lock);
        if (!priv->updating) {
        priv->updating=true;
    get_cpu_temp(priv);
    get_cpu_freq(priv);
    priv->fanColorR=DIV_ROUND_CLOSEST(priv->cpu_temp*256,100);
    priv->fanColorG=DIV_ROUND_CLOSEST(priv->speed_input[0]*256,2500);
    priv->fanColorB=DIV_ROUND_CLOSEST(priv->speed_input[1]*256,2800);
//  printk("Waterforce Color RGB: %d %d %d\n",priv->fanColorR,priv->fanColorG,priv->fanColorB);
    memset(priv->buffer, 0x00, MAX_REPORT_LENGTH);
    memcpy(priv->buffer, fan_color_cmd, FAN_COLOR_CMD_LEN);
        memcpy(&(priv->buffer[2]),&(priv->fanColorR),1);
        memcpy(&(priv->buffer[3]),&(priv->fanColorG),1);
        memcpy(&(priv->buffer[4]),&(priv->fanColorB),1);
    ret = hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH);

    memset(priv->buffer, 0x00, MAX_REPORT_LENGTH);
    memcpy(priv->buffer, cpu_temp_cmd, CPU_TEMP_CMD_LEN);
        memcpy(&(priv->buffer[3]),&(priv->cpu_temp),1);
        memcpy(&(priv->buffer[4]),&(priv->nthread),1);
        memcpy(&(priv->buffer[5]),&(priv->cpufreqGhz1),1);
        memcpy(&(priv->buffer[6]),&(priv->cpufreqGhz2),1);

    ret = hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH);

    memset(priv->buffer, 0x00, MAX_REPORT_LENGTH);
    memcpy(priv->buffer, cmd, cmd_length);
    ret = hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH);

        }
    mutex_unlock(&priv->buffer_lock);
    return ret;
}

static int waterforce_get_status(struct waterforce_data *priv)
{
    int ret;
    reinit_completion(&status_report_received);

    /* Send command for getting status */
    ret = waterforce_write_expanded(priv, get_status_cmd, 2);
    if (ret < 0)
        return ret;

    if (!wait_for_completion_timeout
        (&status_report_received, msecs_to_jiffies(STATUS_VALIDITY * 1000)))
        return -ENODATA;

    return 0;
}

static umode_t waterforce_is_visible(const void *data,
                     enum hwmon_sensor_types type, u32 attr, int channel)
{
        return 0444; 
    switch (type) {
    case hwmon_temp:
    case hwmon_fan:
        return 0444;
    case hwmon_power:

        switch (attr) {
        case hwmon_pwm_input:
            return 0444;
        default:
            break;
        }
        break;
    default:
        break;
    }

    return 0;
}

static int waterforce_read(struct device *dev, enum hwmon_sensor_types type,
               u32 attr, int channel, long *val)
{
    int ret;

    struct waterforce_data *priv = NULL;

    if (dev==NULL) { priv=hid_get_drvdata(waterforce_device); }
    else { priv=dev_get_drvdata(dev); }

    if (time_after(jiffies, priv->updated + STATUS_VALIDITY * HZ)) {
        /* Request status on demand */
        ret = waterforce_get_status(priv);
        if (ret < 0) {
            return -ENODATA;
        }
    }

    switch (type) {
    case hwmon_temp:
        *val = priv->temp_input[channel];
        break;
    case hwmon_fan:
        *val = priv->speed_input[channel];
        break;
    case hwmon_power:
        *val = priv->duty_input[channel];
                break;
        switch (attr) {
        case hwmon_pwm_input:
            *val = DIV_ROUND_CLOSEST(priv->duty_input[channel] * 255, 100);
            break;
        default:
            break;
        }
        break;
    default:
        return -EOPNOTSUPP; /* unreachable */
    }

    return 0;
}

static int waterforce_read_string(struct device *dev, enum hwmon_sensor_types type,
                  u32 attr, int channel, const char **str)
{
    switch (type) {
    case hwmon_temp:
        *str = waterforce_temp_label[channel];
        break;
    case hwmon_fan:
        *str = waterforce_speed_label[channel];
        break;
    case hwmon_power:
        *str = waterforce_pwm_label[channel];
        break;
    default:
        return -EOPNOTSUPP; /* unreachable */
    }

    return 0;
}

static const struct hwmon_ops waterforce_hwmon_ops = {
    .is_visible = waterforce_is_visible,
    .read = waterforce_read,
    .read_string = waterforce_read_string,
};

static const struct hwmon_channel_info *waterforce_info[] = {
    HWMON_CHANNEL_INFO(temp,
               HWMON_T_INPUT | HWMON_T_LABEL),
    HWMON_CHANNEL_INFO(fan,
               HWMON_F_INPUT | HWMON_F_LABEL,
               HWMON_F_INPUT | HWMON_F_LABEL
                           ),

    HWMON_CHANNEL_INFO(power,
               HWMON_P_INPUT | HWMON_P_LABEL,
               HWMON_P_INPUT | HWMON_P_LABEL),
    NULL
};

static const struct hwmon_chip_info waterforce_chip_info = {
    .ops = &waterforce_hwmon_ops,
    .info = waterforce_info,
};

static int waterforce_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
                int size)
{
    struct waterforce_data *priv = hid_get_drvdata(hdev);

    if (data[0] != get_status_cmd[0] || data[1] != get_status_cmd[1]) {
        /* Device returned improper data */
        hid_err_once(priv->hdev, "firmware or device is possibly damaged\n");
        return 0;
    }
   /*     for (int i=0;i<16;i++) {
        printk("data[%d]=%d\n",i,data[i]);
    } */

    priv->temp_input[0] = data[WATERFORCE_TEMP_SENSOR]*1000;
    priv->speed_input[0] = get_unaligned_le16(data + WATERFORCE_FAN_SPEED);
    priv->speed_input[1] = get_unaligned_le16(data + WATERFORCE_PUMP_SPEED);
    priv->speed_input[2] = data[WATERFORCE_FAN_DUTY];
    priv->speed_input[3] = data[WATERFORCE_PUMP_DUTY];

    priv->duty_input[0] = data[WATERFORCE_FAN_DUTY];
    priv->duty_input[1] = data[WATERFORCE_PUMP_DUTY];

    complete(&status_report_received);

    priv->updated = jiffies;
        priv->updating=false;

    return 0;
}
static void waterforce_timer_callback(struct timer_list *timer)
{
//    printk(KERN_INFO "Timer expired\n"); 
    if (waterforce_device!=NULL) {
    struct waterforce_data *priv = hid_get_drvdata(waterforce_device);
    if (priv) {
            get_thermal_zone_id_of_cpu(priv);
            get_hwmon_id_of_waterforce(priv);

        char buf[100];
        char path[100];
        sprintf(path,"/sys/class/hwmon/hwmon%d/fan1_input",priv->hwmonid);
        read_file(path,buf,sizeof(buf));
    }
    }

}

static int waterforce_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
    struct waterforce_data *priv;
    int ret;

    priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    priv->hdev = hdev;
    hid_set_drvdata(hdev, priv);
        priv->updating=false;
        waterforce_device=hdev;
    /*
     * Initialize ->updated to STATUS_VALIDITY seconds in the past, making
     * the initial empty data invalid for waterforce_read without the need for
     * a special case there.
     */
    priv->updated = jiffies - STATUS_VALIDITY * HZ;

    ret = hid_parse(hdev);
    if (ret) {
        hid_err(hdev, "hid parse failed with %d\n", ret);
        return ret;
    }

    /*
     * Enable hidraw so existing user-space tools can continue to work.
     */
    ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
    if (ret) {
        hid_err(hdev, "hid hw start failed with %d\n", ret);
        goto fail_and_stop;
    }

    ret = hid_hw_open(hdev);
    if (ret) {
        hid_err(hdev, "hid hw open failed with %d\n", ret);
        goto fail_and_close;
    }

    priv->buffer = devm_kzalloc(&hdev->dev, MAX_REPORT_LENGTH, GFP_KERNEL);
    if (!priv->buffer) {
        ret = -ENOMEM;
        goto fail_and_close;
    }

    mutex_init(&priv->buffer_lock);

    priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "waterforce",
                              priv, &waterforce_chip_info, NULL);
    if (IS_ERR(priv->hwmon_dev)) {
        ret = PTR_ERR(priv->hwmon_dev);
        hid_err(hdev, "hwmon registration failed with %d\n", ret);
        goto fail_and_close;
    }
    get_cpu_freq(priv);
    return 0;

fail_and_close:
    hid_hw_close(hdev);
fail_and_stop:
    hid_hw_stop(hdev);
    return ret;
}

static void waterforce_remove(struct hid_device *hdev)
{
    struct waterforce_data *priv = hid_get_drvdata(hdev);

    hwmon_device_unregister(priv->hwmon_dev);

    hid_hw_close(hdev);
    hid_hw_stop(hdev);
}

static const struct hid_device_id waterforce_table[] = {
    { HID_USB_DEVICE(USB_VENDOR_ID_GIGABYTE, USB_PRODUCT_ID_WATERFORCE_1) },
    { HID_USB_DEVICE(USB_VENDOR_ID_GIGABYTE, USB_PRODUCT_ID_WATERFORCE_2) },
    { HID_USB_DEVICE(USB_VENDOR_ID_GIGABYTE, USB_PRODUCT_ID_WATERFORCE_3) },
    { }
};

MODULE_DEVICE_TABLE(hid, waterforce_table);

static struct hid_driver waterforce_driver = {
    .name = "waterforce",
    .id_table = waterforce_table,
    .probe = waterforce_probe,
    .remove = waterforce_remove,
    .raw_event = waterforce_raw_event,
};

static struct task_struct *waterforce_timer_thread;

static int waterforce_thread_fn(void *data)
{
//  timer_setup(&waterforce_timer,waterforce_timer_callback,0);
//        mod_timer(&waterforce_timer, jiffies + msecs_to_jiffies(TIMER_WAKEUP_MS));

    while (!kthread_should_stop()) {
    set_current_state(TASK_INTERRUPTIBLE);
//  printk("waterforce_thread_fn\n");

    waterforce_timer_callback(NULL);
    schedule_timeout(HZ*TIMER_WAKEUP_S);
    }
    return 0;
}
static int __init waterforce_init(void)
{
        waterforce_timer_thread = kthread_run(waterforce_thread_fn,NULL,"waterforce_timer_thread");
    if (IS_ERR(waterforce_timer_thread)) {
            printk(KERN_ERR "Waterforce: Failed to create thread\n");
//          del_timer(&waterforce_timer);
            return PTR_ERR(waterforce_timer_thread);
    }
    return hid_register_driver(&waterforce_driver);
}

static void __exit waterforce_exit(void)
{
//  del_timer_sync(&waterforce_timer);
//  wake_up_process(waterforce_timer_thread);
    kthread_stop(waterforce_timer_thread);
    hid_unregister_driver(&waterforce_driver);
}

/*
 * When compiled into the kernel, initialize after the hid bus.
 */
late_initcall(waterforce_init);
module_exit(waterforce_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Hwmon driver for Gigabyte AORUS Waterforce AIO coolers");
amazonparrot commented 11 months ago

sorry my bad, line 110 should be line 135 please try to change line to 131: sprintf(path_string,"/sys/class/hwmon/hwmon%d/name",i); 135: if (strcmp(temp_string,"k10temp\n")==0) 153: sprintf(path,"/sys/class/hwmon/hwmon%d/temp1_input",priv->cputhermalzoneid);

Jackiiiii commented 11 months ago

i think it work, thanks!!! No error untill now

btw this dont work for me:

sudo make modules_install

sudo depmod

or do i need to restart then?

Only work if i do:

sudo make
sudo make install 
sudo depmod

For none reboot then this: ./Makefile

amazonparrot commented 11 months ago

i think it work, thanks!!! No error untill now

btw this dont work for me:

sudo make modules_install

sudo depmod

or do i need to restart then?

Only work if i do:

sudo make
sudo make install 
sudo depmod

For none reboot then this: ./Makefile thx, if you do not reboot "make dev" would do

Jackiiiii commented 11 months ago

And if i want to install it i dont it right ?

amazonparrot commented 11 months ago

And if i want to install it i dont it right ?

if you want to install it, I think you did right, i just tested on my PVE

make install
depmod
Jackiiiii commented 11 months ago

Thanks very much!! 👍

This is not the right place to ask but:

I got a other question, i m using pve as workstation and i m on my windows hole time with it as vm. Do u know something about laytency? i have IRQ balance installed cause i dont wana use cpu pinning, if i use it, i wont be flexibel just creating vm or use more cores if i need, cause i need to then always edite the pinning or SMT affinity. Its total a mess with that and laytency i got. When i start LatencyMon even then my sound start cracking more and often if i test with LatencyMon. Do u know something about it? Already compiled the kernel with 1000hz i think it help with video a bit and now i m working on qemu changes and compiling because some Games not working because anticheat. How i have it right now is perfect for work stuff and gaming but Latency is not great.

amazonparrot commented 11 months ago

Thanks very much!! 👍

This is not the right place to ask but:

I got a other question, i m using pve as workstation and i m on my windows hole time with it as vm. Do u know something about laytency? i have IRQ balance installed cause i dont wana use cpu pinning, if i use it, i wont be flexibel just creating vm or use more cores if i need, cause i need to then always edite the pinning or SMT affinity. Its total a mess with that and laytency i got. When i start LatencyMon even then my sound start cracking more and often if i test with LatencyMon. Do u know something about it? Already compiled the kernel with 1000hz i think it help with video a bit and now i m working on qemu changes and compiling because some Games not working because anticheat. How i have it right now is perfect for work stuff and gaming but Latency is not great.

I am not familiar with latency tuning on QEMU vm at the moment, if I some how get those information in future, I would get back to you. I am also interested to know how to recompile QEMU to fix the anticheat issue, would appreciate if you would put in a few pointers here.

Jackiiiii commented 11 months ago

Here we go again after reboot it start posting error but when I run ./Makefile there no error but when I restart errors. Even with make install and the other commands... but this time only hwon3

Cooler is working!

posting20231217_135230.jpg

Jackiiiii commented 11 months ago

Thanks very much!! 👍

This is not the right place to ask but:

I got a other question, i m using pve as workstation and i m on my windows hole time with it as vm. Do u know something about laytency? i have IRQ balance installed cause i dont wana use cpu pinning, if i use it, i wont be flexibel just creating vm or use more cores if i need, cause i need to then always edite the pinning or SMT affinity. Its total a mess with that and laytency i got. When i start LatencyMon even then my sound start cracking more and often if i test with LatencyMon. Do u know something about it? Already compiled the kernel with 1000hz i think it help with video a bit and now i m working on qemu changes and compiling because some Games not working because anticheat. How i have it right now is perfect for work stuff and gaming but Latency is not great.

I am not familiar with latency tuning on QEMU vm at the moment, if I some how get those information in future, I would get back to you. I am also interested to know how to recompile QEMU to fix the anticheat issue, would appreciate if you would put in a few pointers here.

Thanks😊

Jackiiiii commented 11 months ago

Do u know maybe why i m getting again errors? 🥲

Jackiiiii commented 11 months ago

I just turn off the print again.

amazonparrot commented 11 months ago

I just turn off the print again.

Could you post the waterforce.c here and let me have a look?

Jackiiiii commented 11 months ago

in this code i turn the print off in the line:

    if (IS_ERR(temp_file)) {
        //printk(KERN_ERR "Error opening file %s\n", path);
        return -ENOENT;
    }
/*
 * POC code use at your own risk
 * modifed based on https://github.com/aleksamagicka/waterforce-hwmon/
 */
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <linux/buffer_head.h>
#include <asm/unaligned.h>
#include <linux/hid.h>
#include <linux/hwmon.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cpufreq.h>
#include <linux/timer.h>
#include <linux/thermal.h>
#include <linux/kthread.h>
#define USB_VENDOR_ID_GIGABYTE      0x1044
#define USB_PRODUCT_ID_WATERFORCE_1 0x7a4d  /* Gigabyte AORUS WATERFORCE X (240, 280, 360) */
#define USB_PRODUCT_ID_WATERFORCE_2 0x7a52  /* Gigabyte AORUS WATERFORCE X 360G */
#define USB_PRODUCT_ID_WATERFORCE_3 0x7a53  /* Gigabyte AORUS WATERFORCE EX 360 */

#define STATUS_VALIDITY     2   /* seconds */
#define MAX_REPORT_LENGTH   6144

#define WATERFORCE_TEMP_SENSOR  0xD
#define WATERFORCE_FAN_SPEED    0x02
#define WATERFORCE_PUMP_SPEED   0x05
#define WATERFORCE_FAN_DUTY 0x08
#define WATERFORCE_PUMP_DUTY    0x09
#define CPU_TEMP_CMD_LEN    9
#define FAN_COLOR_CMD_LEN   5
#define TIMER_WAKEUP_S      2
DECLARE_COMPLETION(status_report_received);

static const u8 get_status_cmd[] = { 0x99, 0xDA };
static const u8 cpu_temp_cmd[] = { 0x99,0xE0,0x00,0x25,0x20,0x05,0x05,0x18,0x30 };
static const u8 fan_color_cmd[] = { 0x99,0xCD,0xFF,0xFF,0xFF };

static const char *const waterforce_temp_label[] = {
    "Coolant temp",
};

static const char *const waterforce_speed_label[] = {
    "Fan speed",
    "Pump speed",
    "Fan duty",
    "Pump duty"
};
static const char *const waterforce_pwm_label[] = {
    "Fan duty",
    "Pump duty"
};
static struct timer_list waterforce_timer;
struct hid_device *waterforce_device=NULL;

struct waterforce_data {
    struct hid_device *hdev;
    struct device *hwmon_dev;
    struct mutex buffer_lock;   /* For locking access to buffer */

    /* Sensor data */
    s32 temp_input[1];
    u16 speed_input[4]; /* Fan and pump speed in RPM */
    u16 duty_input[4];  /* Fan and pump duty in 0-100% */
        u8 cpu_temp;
    u8 ncore;
    u8 nthread;
        u8 cpufreqGhz1;
        u8 cpufreqGhz2;
    u8 fanColorR;
    u8 fanColorG;
    u8 fanColorB;
    u8 *buffer;
        u8 cputhermalzoneid;
    u8 hwmonid;
        bool updating;
    unsigned long updated;  /* jiffies */
};
static int read_file(char * path,char *buf,int buflen) {
    struct file *temp_file;

    // Open the temperature file for reading
    temp_file = filp_open(path, O_RDONLY, 0);

    if (IS_ERR(temp_file)) {
        //printk(KERN_ERR "Error opening file %s\n", path);
        return -ENOENT;
    }

    // Read the value as a string
    memset(buf, 0, buflen);
    kernel_read(temp_file, buf, buflen - 1, &temp_file->f_pos);

    // Close the temperature file
    filp_close(temp_file, NULL);
    return 0;
}
static int get_hwmon_id_of_waterforce(struct waterforce_data *priv)
{

    char temp_string[20];
    char path_string[60];
    for (u8 i=0;i<9;i++) {
        sprintf(path_string,"/sys/class/hwmon/hwmon%d/name",i);

        if (read_file(path_string,temp_string,sizeof(temp_string))==0) {

                    if (strcmp(temp_string,"waterforce\n")==0) {

            //    printk("HWMon name %s %d\n",temp_string,i);
            priv->hwmonid=i;
            break;
            }

            }

    }

    return 0;
}

static int get_thermal_zone_id_of_cpu(struct waterforce_data *priv)
{

    char temp_string[20];
    char path_string[60];
    for (u8 i=0;i<9;i++) {
        sprintf(path_string,"/sys/class/hwmon/hwmon%d/name",i);

        if (read_file(path_string,temp_string,sizeof(temp_string))==0) {

                    if (strcmp(temp_string,"k10temp\n")==0) {
            priv->cputhermalzoneid=i;
              //  printk("Thermal Zone type %s %d\n",temp_string,i);
            break;
            }

            }

    }

    return 0;
}
static int get_cpu_temp(struct waterforce_data *priv)
{
    int temp_millicelsius;
    char temp_string[10];
    struct file *temp_file;
    char path[60];
    sprintf(path,"/sys/class/hwmon/hwmon%d/temp1_input",priv->cputhermalzoneid);

    // Open the temperature file for reading
    temp_file = filp_open(path, O_RDONLY, 0);

    if (IS_ERR(temp_file)) {
        printk(KERN_ERR "Error opening temperature file %s\n", path);
        return -ENOENT;
    }

    // Read the temperature value as a string
    memset(temp_string, 0, sizeof(temp_string));
    kernel_read(temp_file, temp_string, sizeof(temp_string) - 1, &temp_file->f_pos);

    // Close the temperature file
    filp_close(temp_file, NULL);

    // Convert the temperature string to an integer
    sscanf(temp_string, "%d", &temp_millicelsius);
    priv->cpu_temp=temp_millicelsius/1000;
    // Convert the temperature to degrees Celsius and print it
    printk(KERN_INFO "CPU temperature: %d.%d C\n", priv->cpu_temp, temp_millicelsius % 1000);

    return 0;
}
static int get_cpu_freq(struct waterforce_data *priv)
{
    unsigned cpu = cpumask_first(cpu_online_mask);
        priv->nthread=nr_cpu_ids;
        unsigned maxfreq=0;
        while (cpu < nr_cpu_ids) {
//      struct cpufreq_policy policy;
//      cpufreq_get_policy(&policy,cpu);
        unsigned freq=cpufreq_quick_get_max(cpu);
//      unsigned freq=policy.cur;
        if (freq>maxfreq) { maxfreq=freq; }
 //              pr_info("CPU: %u, freq: %u kHz\n", cpu, freq);
                cpu = cpumask_next(cpu, cpu_online_mask);
        }

    priv->cpufreqGhz1=maxfreq/1000000;
    priv->cpufreqGhz2=(maxfreq%1000000)/100000;
//  printk("CPU Max freq: %d.%d",priv->cpufreqGhz1,priv->cpufreqGhz2);
    return 0;
}
/*
 * Writes the command to the device with the rest of the report (up to 64 bytes) filled
 * with zeroes
 */
static int waterforce_write_expanded(struct waterforce_data *priv, const u8 *cmd, int cmd_length)
{
    int ret=0;

    mutex_lock(&priv->buffer_lock);
        if (!priv->updating) {
        priv->updating=true;
    get_cpu_temp(priv);
    get_cpu_freq(priv);
    priv->fanColorR=DIV_ROUND_CLOSEST(priv->cpu_temp*256,100);
    priv->fanColorG=DIV_ROUND_CLOSEST(priv->speed_input[0]*256,2500);
    priv->fanColorB=DIV_ROUND_CLOSEST(priv->speed_input[1]*256,2800);
//  printk("Waterforce Color RGB: %d %d %d\n",priv->fanColorR,priv->fanColorG,priv->fanColorB);
    memset(priv->buffer, 0x00, MAX_REPORT_LENGTH);
    memcpy(priv->buffer, fan_color_cmd, FAN_COLOR_CMD_LEN);
        memcpy(&(priv->buffer[2]),&(priv->fanColorR),1);
        memcpy(&(priv->buffer[3]),&(priv->fanColorG),1);
        memcpy(&(priv->buffer[4]),&(priv->fanColorB),1);
    ret = hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH);

    memset(priv->buffer, 0x00, MAX_REPORT_LENGTH);
    memcpy(priv->buffer, cpu_temp_cmd, CPU_TEMP_CMD_LEN);
        memcpy(&(priv->buffer[3]),&(priv->cpu_temp),1);
        memcpy(&(priv->buffer[4]),&(priv->nthread),1);
        memcpy(&(priv->buffer[5]),&(priv->cpufreqGhz1),1);
        memcpy(&(priv->buffer[6]),&(priv->cpufreqGhz2),1);

    ret = hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH);

    memset(priv->buffer, 0x00, MAX_REPORT_LENGTH);
    memcpy(priv->buffer, cmd, cmd_length);
    ret = hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH);

        }
    mutex_unlock(&priv->buffer_lock);
    return ret;
}

static int waterforce_get_status(struct waterforce_data *priv)
{
    int ret;
    reinit_completion(&status_report_received);

    /* Send command for getting status */
    ret = waterforce_write_expanded(priv, get_status_cmd, 2);
    if (ret < 0)
        return ret;

    if (!wait_for_completion_timeout
        (&status_report_received, msecs_to_jiffies(STATUS_VALIDITY * 1000)))
        return -ENODATA;

    return 0;
}

static umode_t waterforce_is_visible(const void *data,
                     enum hwmon_sensor_types type, u32 attr, int channel)
{
        return 0444; 
    switch (type) {
    case hwmon_temp:
    case hwmon_fan:
        return 0444;
    case hwmon_power:

        switch (attr) {
        case hwmon_pwm_input:
            return 0444;
        default:
            break;
        }
        break;
    default:
        break;
    }

    return 0;
}

static int waterforce_read(struct device *dev, enum hwmon_sensor_types type,
               u32 attr, int channel, long *val)
{
    int ret;

    struct waterforce_data *priv = NULL;

    if (dev==NULL) { priv=hid_get_drvdata(waterforce_device); }
    else { priv=dev_get_drvdata(dev); }

    if (time_after(jiffies, priv->updated + STATUS_VALIDITY * HZ)) {
        /* Request status on demand */
        ret = waterforce_get_status(priv);
        if (ret < 0) {
            return -ENODATA;
        }
    }

    switch (type) {
    case hwmon_temp:
        *val = priv->temp_input[channel];
        break;
    case hwmon_fan:
        *val = priv->speed_input[channel];
        break;
    case hwmon_power:
        *val = priv->duty_input[channel];
                break;
        switch (attr) {
        case hwmon_pwm_input:
            *val = DIV_ROUND_CLOSEST(priv->duty_input[channel] * 255, 100);
            break;
        default:
            break;
        }
        break;
    default:
        return -EOPNOTSUPP; /* unreachable */
    }

    return 0;
}

static int waterforce_read_string(struct device *dev, enum hwmon_sensor_types type,
                  u32 attr, int channel, const char **str)
{
    switch (type) {
    case hwmon_temp:
        *str = waterforce_temp_label[channel];
        break;
    case hwmon_fan:
        *str = waterforce_speed_label[channel];
        break;
    case hwmon_power:
        *str = waterforce_pwm_label[channel];
        break;
    default:
        return -EOPNOTSUPP; /* unreachable */
    }

    return 0;
}

static const struct hwmon_ops waterforce_hwmon_ops = {
    .is_visible = waterforce_is_visible,
    .read = waterforce_read,
    .read_string = waterforce_read_string,
};

static const struct hwmon_channel_info *waterforce_info[] = {
    HWMON_CHANNEL_INFO(temp,
               HWMON_T_INPUT | HWMON_T_LABEL),
    HWMON_CHANNEL_INFO(fan,
               HWMON_F_INPUT | HWMON_F_LABEL,
               HWMON_F_INPUT | HWMON_F_LABEL
                           ),

    HWMON_CHANNEL_INFO(power,
               HWMON_P_INPUT | HWMON_P_LABEL,
               HWMON_P_INPUT | HWMON_P_LABEL),
    NULL
};

static const struct hwmon_chip_info waterforce_chip_info = {
    .ops = &waterforce_hwmon_ops,
    .info = waterforce_info,
};

static int waterforce_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
                int size)
{
    struct waterforce_data *priv = hid_get_drvdata(hdev);

    if (data[0] != get_status_cmd[0] || data[1] != get_status_cmd[1]) {
        /* Device returned improper data */
        hid_err_once(priv->hdev, "firmware or device is possibly damaged\n");
        return 0;
    }
   /*     for (int i=0;i<16;i++) {
        printk("data[%d]=%d\n",i,data[i]);
    } */

    priv->temp_input[0] = data[WATERFORCE_TEMP_SENSOR]*1000;
    priv->speed_input[0] = get_unaligned_le16(data + WATERFORCE_FAN_SPEED);
    priv->speed_input[1] = get_unaligned_le16(data + WATERFORCE_PUMP_SPEED);
    priv->speed_input[2] = data[WATERFORCE_FAN_DUTY];
    priv->speed_input[3] = data[WATERFORCE_PUMP_DUTY];

    priv->duty_input[0] = data[WATERFORCE_FAN_DUTY];
    priv->duty_input[1] = data[WATERFORCE_PUMP_DUTY];

    complete(&status_report_received);

    priv->updated = jiffies;
        priv->updating=false;

    return 0;
}
static void waterforce_timer_callback(struct timer_list *timer)
{
//    printk(KERN_INFO "Timer expired\n"); 
    if (waterforce_device!=NULL) {
    struct waterforce_data *priv = hid_get_drvdata(waterforce_device);
    if (priv) {
            get_thermal_zone_id_of_cpu(priv);
            get_hwmon_id_of_waterforce(priv);

        char buf[100];
        char path[100];
        sprintf(path,"/sys/class/hwmon/hwmon%d/fan1_input",priv->hwmonid);
        read_file(path,buf,sizeof(buf));
    }
    }

}

static int waterforce_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
    struct waterforce_data *priv;
    int ret;

    priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    priv->hdev = hdev;
    hid_set_drvdata(hdev, priv);
        priv->updating=false;
        waterforce_device=hdev;
    /*
     * Initialize ->updated to STATUS_VALIDITY seconds in the past, making
     * the initial empty data invalid for waterforce_read without the need for
     * a special case there.
     */
    priv->updated = jiffies - STATUS_VALIDITY * HZ;

    ret = hid_parse(hdev);
    if (ret) {
        hid_err(hdev, "hid parse failed with %d\n", ret);
        return ret;
    }

    /*
     * Enable hidraw so existing user-space tools can continue to work.
     */
    ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
    if (ret) {
        hid_err(hdev, "hid hw start failed with %d\n", ret);
        goto fail_and_stop;
    }

    ret = hid_hw_open(hdev);
    if (ret) {
        hid_err(hdev, "hid hw open failed with %d\n", ret);
        goto fail_and_close;
    }

    priv->buffer = devm_kzalloc(&hdev->dev, MAX_REPORT_LENGTH, GFP_KERNEL);
    if (!priv->buffer) {
        ret = -ENOMEM;
        goto fail_and_close;
    }

    mutex_init(&priv->buffer_lock);

    priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "waterforce",
                              priv, &waterforce_chip_info, NULL);
    if (IS_ERR(priv->hwmon_dev)) {
        ret = PTR_ERR(priv->hwmon_dev);
        hid_err(hdev, "hwmon registration failed with %d\n", ret);
        goto fail_and_close;
    }
    get_cpu_freq(priv);
    return 0;

fail_and_close:
    hid_hw_close(hdev);
fail_and_stop:
    hid_hw_stop(hdev);
    return ret;
}

static void waterforce_remove(struct hid_device *hdev)
{
    struct waterforce_data *priv = hid_get_drvdata(hdev);

    hwmon_device_unregister(priv->hwmon_dev);

    hid_hw_close(hdev);
    hid_hw_stop(hdev);
}

static const struct hid_device_id waterforce_table[] = {
    { HID_USB_DEVICE(USB_VENDOR_ID_GIGABYTE, USB_PRODUCT_ID_WATERFORCE_1) },
    { HID_USB_DEVICE(USB_VENDOR_ID_GIGABYTE, USB_PRODUCT_ID_WATERFORCE_2) },
    { HID_USB_DEVICE(USB_VENDOR_ID_GIGABYTE, USB_PRODUCT_ID_WATERFORCE_3) },
    { }
};

MODULE_DEVICE_TABLE(hid, waterforce_table);

static struct hid_driver waterforce_driver = {
    .name = "waterforce",
    .id_table = waterforce_table,
    .probe = waterforce_probe,
    .remove = waterforce_remove,
    .raw_event = waterforce_raw_event,
};

static struct task_struct *waterforce_timer_thread;

static int waterforce_thread_fn(void *data)
{
//  timer_setup(&waterforce_timer,waterforce_timer_callback,0);
//        mod_timer(&waterforce_timer, jiffies + msecs_to_jiffies(TIMER_WAKEUP_MS));

    while (!kthread_should_stop()) {
    set_current_state(TASK_INTERRUPTIBLE);
//  printk("waterforce_thread_fn\n");

    waterforce_timer_callback(NULL);
    schedule_timeout(HZ*TIMER_WAKEUP_S);
    }
    return 0;
}
static int __init waterforce_init(void)
{
        waterforce_timer_thread = kthread_run(waterforce_thread_fn,NULL,"waterforce_timer_thread");
    if (IS_ERR(waterforce_timer_thread)) {
            printk(KERN_ERR "Waterforce: Failed to create thread\n");
//          del_timer(&waterforce_timer);
            return PTR_ERR(waterforce_timer_thread);
    }
    return hid_register_driver(&waterforce_driver);
}

static void __exit waterforce_exit(void)
{
//  del_timer_sync(&waterforce_timer);
//  wake_up_process(waterforce_timer_thread);
    kthread_stop(waterforce_timer_thread);
    hid_unregister_driver(&waterforce_driver);
}

/*
 * When compiled into the kernel, initialize after the hid bus.
 */
late_initcall(waterforce_init);
module_exit(waterforce_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Hwmon driver for Gigabyte AORUS Waterforce AIO coolers");
amazonparrot commented 11 months ago

when the error show up, does the "CPU temperature:" also show up as well? could you help to check if the hwmon3/name exists when the error show up?

Jackiiiii commented 11 months ago

everything work, display, temp shows, cooler works to and cool the cpu. only thing is, this error is there even it works. And no does not exist:

/sys/class/hwmon# dir
hwmon0  hwmon1  hwmon11  hwmon2  hwmon4  hwmon5  hwmon6  hwmon9
root@gaming:/sys/class/hwmon#
amazonparrot commented 11 months ago

everything work, display, temp shows, cooler works to and cool the cpu. only thing is, this error is there even it works. And no does not exist:

/sys/class/hwmon# dir
hwmon0  hwmon1  hwmon11  hwmon2  hwmon4  hwmon5  hwmon6  hwmon9
root@gaming:/sys/class/hwmon#

there seems to be no hwmon3 directory, and since the code currently just iterative from hwmon0 to hwmon9, it would throw error message when the file or the directory is not there, I will see if I can just open directory from within the hwmon directory only. In the mean time you can just comment the Error opening file message.

Jackiiiii commented 11 months ago

Ok 👍

Would it not help something like if not exist? just ignore it?