1 // SPDX-License-Identifier: GPL-2.0+
3 * hwmon driver for Asus ROG Ryujin II 360 AIO cooler.
5 * Copyright 2024 Aleksa Savic <savicaleksa83@gmail.com>
8 #include <linux/debugfs.h>
10 #include <linux/hwmon.h>
11 #include <linux/jiffies.h>
12 #include <linux/module.h>
13 #include <linux/spinlock.h>
14 #include <linux/unaligned.h>
16 #define DRIVER_NAME "asus_rog_ryujin"
18 #define USB_VENDOR_ID_ASUS_ROG 0x0b05
19 #define USB_PRODUCT_ID_RYUJIN_AIO 0x1988 /* ASUS ROG RYUJIN II 360 */
21 #define STATUS_VALIDITY 1500 /* ms */
22 #define MAX_REPORT_LENGTH 65
24 /* Cooler status report offsets */
25 #define RYUJIN_TEMP_SENSOR_1 3
26 #define RYUJIN_TEMP_SENSOR_2 4
27 #define RYUJIN_PUMP_SPEED 5
28 #define RYUJIN_INTERNAL_FAN_SPEED 7
30 /* Cooler duty report offsets */
31 #define RYUJIN_PUMP_DUTY 4
32 #define RYUJIN_INTERNAL_FAN_DUTY 5
34 /* Controller status (speeds) report offsets */
35 #define RYUJIN_CONTROLLER_SPEED_1 5
36 #define RYUJIN_CONTROLLER_SPEED_2 7
37 #define RYUJIN_CONTROLLER_SPEED_3 9
38 #define RYUJIN_CONTROLLER_SPEED_4 3
40 /* Controller duty report offsets */
41 #define RYUJIN_CONTROLLER_DUTY 4
43 /* Control commands and their inner offsets */
44 #define RYUJIN_CMD_PREFIX 0xEC
46 static const u8 get_cooler_status_cmd
[] = { RYUJIN_CMD_PREFIX
, 0x99 };
47 static const u8 get_cooler_duty_cmd
[] = { RYUJIN_CMD_PREFIX
, 0x9A };
48 static const u8 get_controller_speed_cmd
[] = { RYUJIN_CMD_PREFIX
, 0xA0 };
49 static const u8 get_controller_duty_cmd
[] = { RYUJIN_CMD_PREFIX
, 0xA1 };
51 #define RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET 3
52 #define RYUJIN_SET_COOLER_FAN_DUTY_OFFSET 4
53 static const u8 set_cooler_duty_cmd
[] = { RYUJIN_CMD_PREFIX
, 0x1A, 0x00, 0x00, 0x00 };
55 #define RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET 4
56 static const u8 set_controller_duty_cmd
[] = { RYUJIN_CMD_PREFIX
, 0x21, 0x00, 0x00, 0x00 };
59 #define GET_CMD_LENGTH 2 /* Same length for all get commands */
60 #define SET_CMD_LENGTH 5 /* Same length for all set commands */
62 /* Command response headers */
63 #define RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE 0x19
64 #define RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE 0x1A
65 #define RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE 0x20
66 #define RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE 0x21
68 static const char *const rog_ryujin_temp_label
[] = {
72 static const char *const rog_ryujin_speed_label
[] = {
75 "Controller fan 1 speed",
76 "Controller fan 2 speed",
77 "Controller fan 3 speed",
78 "Controller fan 4 speed",
81 struct rog_ryujin_data
{
82 struct hid_device
*hdev
;
83 struct device
*hwmon_dev
;
84 /* For locking access to buffer */
85 struct mutex buffer_lock
;
86 /* For queueing multiple readers */
87 struct mutex status_report_request_mutex
;
88 /* For reinitializing the completions below */
89 spinlock_t status_report_request_lock
;
90 struct completion cooler_status_received
;
91 struct completion controller_status_received
;
92 struct completion cooler_duty_received
;
93 struct completion controller_duty_received
;
94 struct completion cooler_duty_set
;
95 struct completion controller_duty_set
;
99 u16 speed_input
[6]; /* Pump, internal fan and four controller fan speeds in RPM */
100 u8 duty_input
[3]; /* Pump, internal fan and controller fan duty in PWM */
103 unsigned long updated
; /* jiffies */
106 static int rog_ryujin_percent_to_pwm(u16 val
)
108 return DIV_ROUND_CLOSEST(val
* 255, 100);
111 static int rog_ryujin_pwm_to_percent(long val
)
113 return DIV_ROUND_CLOSEST(val
* 100, 255);
116 static umode_t
rog_ryujin_is_visible(const void *data
,
117 enum hwmon_sensor_types type
, u32 attr
, int channel
)
122 case hwmon_temp_label
:
123 case hwmon_temp_input
:
131 case hwmon_fan_label
:
132 case hwmon_fan_input
:
140 case hwmon_pwm_input
:
153 /* Writes the command to the device with the rest of the report filled with zeroes */
154 static int rog_ryujin_write_expanded(struct rog_ryujin_data
*priv
, const u8
*cmd
, int cmd_length
)
158 mutex_lock(&priv
->buffer_lock
);
160 memcpy_and_pad(priv
->buffer
, MAX_REPORT_LENGTH
, cmd
, cmd_length
, 0x00);
161 ret
= hid_hw_output_report(priv
->hdev
, priv
->buffer
, MAX_REPORT_LENGTH
);
163 mutex_unlock(&priv
->buffer_lock
);
167 /* Assumes priv->status_report_request_mutex is locked */
168 static int rog_ryujin_execute_cmd(struct rog_ryujin_data
*priv
, const u8
*cmd
, int cmd_length
,
169 struct completion
*status_completion
)
174 * Disable raw event parsing for a moment to safely reinitialize the
175 * completion. Reinit is done because hidraw could have triggered
176 * the raw event parsing and marked the passed in completion as done.
178 spin_lock_bh(&priv
->status_report_request_lock
);
179 reinit_completion(status_completion
);
180 spin_unlock_bh(&priv
->status_report_request_lock
);
182 /* Send command for getting data */
183 ret
= rog_ryujin_write_expanded(priv
, cmd
, cmd_length
);
187 ret
= wait_for_completion_interruptible_timeout(status_completion
,
188 msecs_to_jiffies(STATUS_VALIDITY
));
197 static int rog_ryujin_get_status(struct rog_ryujin_data
*priv
)
199 int ret
= mutex_lock_interruptible(&priv
->status_report_request_mutex
);
204 if (!time_after(jiffies
, priv
->updated
+ msecs_to_jiffies(STATUS_VALIDITY
))) {
205 /* Data is up to date */
206 goto unlock_and_return
;
209 /* Retrieve cooler status */
211 rog_ryujin_execute_cmd(priv
, get_cooler_status_cmd
, GET_CMD_LENGTH
,
212 &priv
->cooler_status_received
);
214 goto unlock_and_return
;
216 /* Retrieve controller status (speeds) */
218 rog_ryujin_execute_cmd(priv
, get_controller_speed_cmd
, GET_CMD_LENGTH
,
219 &priv
->controller_status_received
);
221 goto unlock_and_return
;
223 /* Retrieve cooler duty */
225 rog_ryujin_execute_cmd(priv
, get_cooler_duty_cmd
, GET_CMD_LENGTH
,
226 &priv
->cooler_duty_received
);
228 goto unlock_and_return
;
230 /* Retrieve controller duty */
232 rog_ryujin_execute_cmd(priv
, get_controller_duty_cmd
, GET_CMD_LENGTH
,
233 &priv
->controller_duty_received
);
235 goto unlock_and_return
;
237 priv
->updated
= jiffies
;
240 mutex_unlock(&priv
->status_report_request_mutex
);
247 static int rog_ryujin_read(struct device
*dev
, enum hwmon_sensor_types type
,
248 u32 attr
, int channel
, long *val
)
250 struct rog_ryujin_data
*priv
= dev_get_drvdata(dev
);
251 int ret
= rog_ryujin_get_status(priv
);
258 *val
= priv
->temp_input
[channel
];
261 *val
= priv
->speed_input
[channel
];
265 case hwmon_pwm_input
:
266 *val
= priv
->duty_input
[channel
];
273 return -EOPNOTSUPP
; /* unreachable */
279 static int rog_ryujin_read_string(struct device
*dev
, enum hwmon_sensor_types type
,
280 u32 attr
, int channel
, const char **str
)
284 *str
= rog_ryujin_temp_label
[channel
];
287 *str
= rog_ryujin_speed_label
[channel
];
290 return -EOPNOTSUPP
; /* unreachable */
296 static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data
*priv
, int channel
, int val
)
298 u8 set_cmd
[SET_CMD_LENGTH
];
303 * Retrieve cooler duty since both pump and internal fan are set
304 * together, then write back with one of them modified.
306 ret
= mutex_lock_interruptible(&priv
->status_report_request_mutex
);
310 rog_ryujin_execute_cmd(priv
, get_cooler_duty_cmd
, GET_CMD_LENGTH
,
311 &priv
->cooler_duty_received
);
313 goto unlock_and_return
;
315 memcpy(set_cmd
, set_cooler_duty_cmd
, SET_CMD_LENGTH
);
317 /* Cooler duties are set as 0-100% */
318 val
= rog_ryujin_pwm_to_percent(val
);
321 /* Cooler pump duty */
322 set_cmd
[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET
] = val
;
323 set_cmd
[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET
] =
324 rog_ryujin_pwm_to_percent(priv
->duty_input
[1]);
325 } else if (channel
== 1) {
326 /* Cooler internal fan duty */
327 set_cmd
[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET
] =
328 rog_ryujin_pwm_to_percent(priv
->duty_input
[0]);
329 set_cmd
[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET
] = val
;
332 ret
= rog_ryujin_execute_cmd(priv
, set_cmd
, SET_CMD_LENGTH
, &priv
->cooler_duty_set
);
334 mutex_unlock(&priv
->status_report_request_mutex
);
339 * Controller fan duty (channel == 2). No need to retrieve current
340 * duty, so just send the command.
342 memcpy(set_cmd
, set_controller_duty_cmd
, SET_CMD_LENGTH
);
343 set_cmd
[RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET
] = val
;
346 rog_ryujin_execute_cmd(priv
, set_cmd
, SET_CMD_LENGTH
,
347 &priv
->controller_duty_set
);
352 /* Lock onto this value until next refresh cycle */
353 priv
->duty_input
[channel
] = val
;
358 static int rog_ryujin_write(struct device
*dev
, enum hwmon_sensor_types type
, u32 attr
, int channel
,
361 struct rog_ryujin_data
*priv
= dev_get_drvdata(dev
);
367 case hwmon_pwm_input
:
368 if (val
< 0 || val
> 255)
371 ret
= rog_ryujin_write_fixed_duty(priv
, channel
, val
);
386 static const struct hwmon_ops rog_ryujin_hwmon_ops
= {
387 .is_visible
= rog_ryujin_is_visible
,
388 .read
= rog_ryujin_read
,
389 .read_string
= rog_ryujin_read_string
,
390 .write
= rog_ryujin_write
393 static const struct hwmon_channel_info
*rog_ryujin_info
[] = {
394 HWMON_CHANNEL_INFO(temp
,
395 HWMON_T_INPUT
| HWMON_T_LABEL
),
396 HWMON_CHANNEL_INFO(fan
,
397 HWMON_F_INPUT
| HWMON_F_LABEL
,
398 HWMON_F_INPUT
| HWMON_F_LABEL
,
399 HWMON_F_INPUT
| HWMON_F_LABEL
,
400 HWMON_F_INPUT
| HWMON_F_LABEL
,
401 HWMON_F_INPUT
| HWMON_F_LABEL
,
402 HWMON_F_INPUT
| HWMON_F_LABEL
),
403 HWMON_CHANNEL_INFO(pwm
,
410 static const struct hwmon_chip_info rog_ryujin_chip_info
= {
411 .ops
= &rog_ryujin_hwmon_ops
,
412 .info
= rog_ryujin_info
,
415 static int rog_ryujin_raw_event(struct hid_device
*hdev
, struct hid_report
*report
, u8
*data
,
418 struct rog_ryujin_data
*priv
= hid_get_drvdata(hdev
);
420 if (data
[0] != RYUJIN_CMD_PREFIX
)
423 if (data
[1] == RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE
) {
424 /* Received coolant temp and speeds of pump and internal fan */
425 priv
->temp_input
[0] =
426 data
[RYUJIN_TEMP_SENSOR_1
] * 1000 + data
[RYUJIN_TEMP_SENSOR_2
] * 100;
427 priv
->speed_input
[0] = get_unaligned_le16(data
+ RYUJIN_PUMP_SPEED
);
428 priv
->speed_input
[1] = get_unaligned_le16(data
+ RYUJIN_INTERNAL_FAN_SPEED
);
430 if (!completion_done(&priv
->cooler_status_received
))
431 complete_all(&priv
->cooler_status_received
);
432 } else if (data
[1] == RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE
) {
433 /* Received speeds of four fans attached to the controller */
434 priv
->speed_input
[2] = get_unaligned_le16(data
+ RYUJIN_CONTROLLER_SPEED_1
);
435 priv
->speed_input
[3] = get_unaligned_le16(data
+ RYUJIN_CONTROLLER_SPEED_2
);
436 priv
->speed_input
[4] = get_unaligned_le16(data
+ RYUJIN_CONTROLLER_SPEED_3
);
437 priv
->speed_input
[5] = get_unaligned_le16(data
+ RYUJIN_CONTROLLER_SPEED_4
);
439 if (!completion_done(&priv
->controller_status_received
))
440 complete_all(&priv
->controller_status_received
);
441 } else if (data
[1] == RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE
) {
442 /* Received report for pump and internal fan duties (in %) */
443 if (data
[RYUJIN_PUMP_DUTY
] == 0 && data
[RYUJIN_INTERNAL_FAN_DUTY
] == 0) {
445 * We received a report with zeroes for duty in both places.
446 * The device returns this as a confirmation that setting values
447 * is successful. If we initiated a write, mark it as complete.
449 if (!completion_done(&priv
->cooler_duty_set
))
450 complete_all(&priv
->cooler_duty_set
);
451 else if (!completion_done(&priv
->cooler_duty_received
))
453 * We didn't initiate a write, but received both zeroes.
454 * This means that either both duties are actually zero,
455 * or that we received a success report caused by userspace.
456 * We're expecting a report, so parse it.
458 goto read_cooler_duty
;
462 priv
->duty_input
[0] = rog_ryujin_percent_to_pwm(data
[RYUJIN_PUMP_DUTY
]);
463 priv
->duty_input
[1] = rog_ryujin_percent_to_pwm(data
[RYUJIN_INTERNAL_FAN_DUTY
]);
465 if (!completion_done(&priv
->cooler_duty_received
))
466 complete_all(&priv
->cooler_duty_received
);
467 } else if (data
[1] == RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE
) {
468 /* Received report for controller duty for fans (in PWM) */
469 if (data
[RYUJIN_CONTROLLER_DUTY
] == 0) {
471 * We received a report with a zero for duty. The device returns this as
472 * a confirmation that setting the controller duty value was successful.
473 * If we initiated a write, mark it as complete.
475 if (!completion_done(&priv
->controller_duty_set
))
476 complete_all(&priv
->controller_duty_set
);
477 else if (!completion_done(&priv
->controller_duty_received
))
479 * We didn't initiate a write, but received a zero for duty.
480 * This means that either the duty is actually zero, or that
481 * we received a success report caused by userspace.
482 * We're expecting a report, so parse it.
484 goto read_controller_duty
;
487 read_controller_duty
:
488 priv
->duty_input
[2] = data
[RYUJIN_CONTROLLER_DUTY
];
490 if (!completion_done(&priv
->controller_duty_received
))
491 complete_all(&priv
->controller_duty_received
);
497 static int rog_ryujin_probe(struct hid_device
*hdev
, const struct hid_device_id
*id
)
499 struct rog_ryujin_data
*priv
;
502 priv
= devm_kzalloc(&hdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
507 hid_set_drvdata(hdev
, priv
);
510 * Initialize priv->updated to STATUS_VALIDITY seconds in the past, making
511 * the initial empty data invalid for rog_ryujin_read() without the need for
512 * a special case there.
514 priv
->updated
= jiffies
- msecs_to_jiffies(STATUS_VALIDITY
);
516 ret
= hid_parse(hdev
);
518 hid_err(hdev
, "hid parse failed with %d\n", ret
);
522 /* Enable hidraw so existing user-space tools can continue to work */
523 ret
= hid_hw_start(hdev
, HID_CONNECT_HIDRAW
);
525 hid_err(hdev
, "hid hw start failed with %d\n", ret
);
529 ret
= hid_hw_open(hdev
);
531 hid_err(hdev
, "hid hw open failed with %d\n", ret
);
535 priv
->buffer
= devm_kzalloc(&hdev
->dev
, MAX_REPORT_LENGTH
, GFP_KERNEL
);
541 mutex_init(&priv
->status_report_request_mutex
);
542 mutex_init(&priv
->buffer_lock
);
543 spin_lock_init(&priv
->status_report_request_lock
);
544 init_completion(&priv
->cooler_status_received
);
545 init_completion(&priv
->controller_status_received
);
546 init_completion(&priv
->cooler_duty_received
);
547 init_completion(&priv
->controller_duty_received
);
548 init_completion(&priv
->cooler_duty_set
);
549 init_completion(&priv
->controller_duty_set
);
551 priv
->hwmon_dev
= hwmon_device_register_with_info(&hdev
->dev
, "rog_ryujin",
552 priv
, &rog_ryujin_chip_info
, NULL
);
553 if (IS_ERR(priv
->hwmon_dev
)) {
554 ret
= PTR_ERR(priv
->hwmon_dev
);
555 hid_err(hdev
, "hwmon registration failed with %d\n", ret
);
568 static void rog_ryujin_remove(struct hid_device
*hdev
)
570 struct rog_ryujin_data
*priv
= hid_get_drvdata(hdev
);
572 hwmon_device_unregister(priv
->hwmon_dev
);
578 static const struct hid_device_id rog_ryujin_table
[] = {
579 { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG
, USB_PRODUCT_ID_RYUJIN_AIO
) },
583 MODULE_DEVICE_TABLE(hid
, rog_ryujin_table
);
585 static struct hid_driver rog_ryujin_driver
= {
586 .name
= "rog_ryujin",
587 .id_table
= rog_ryujin_table
,
588 .probe
= rog_ryujin_probe
,
589 .remove
= rog_ryujin_remove
,
590 .raw_event
= rog_ryujin_raw_event
,
593 static int __init
rog_ryujin_init(void)
595 return hid_register_driver(&rog_ryujin_driver
);
598 static void __exit
rog_ryujin_exit(void)
600 hid_unregister_driver(&rog_ryujin_driver
);
603 /* When compiled into the kernel, initialize after the HID bus */
604 late_initcall(rog_ryujin_init
);
605 module_exit(rog_ryujin_exit
);
607 MODULE_LICENSE("GPL");
608 MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");
609 MODULE_DESCRIPTION("Hwmon driver for Asus ROG Ryujin II 360 AIO cooler");