1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * hwmon driver for Asus ROG Ryujin II 360 AIO cooler.
4 *
5 * Copyright 2024 Aleksa Savic <savicaleksa83@gmail.com>
6 */
7
8 #include <linux/debugfs.h>
9 #include <linux/hid.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>
15
16 #define DRIVER_NAME "asus_rog_ryujin"
17
18 #define USB_VENDOR_ID_ASUS_ROG 0x0b05
19 #define USB_PRODUCT_ID_RYUJIN_AIO 0x1988 /* ASUS ROG RYUJIN II 360 */
20
21 #define STATUS_VALIDITY 1500 /* ms */
22 #define MAX_REPORT_LENGTH 65
23
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
29
30 /* Cooler duty report offsets */
31 #define RYUJIN_PUMP_DUTY 4
32 #define RYUJIN_INTERNAL_FAN_DUTY 5
33
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
39
40 /* Controller duty report offsets */
41 #define RYUJIN_CONTROLLER_DUTY 4
42
43 /* Control commands and their inner offsets */
44 #define RYUJIN_CMD_PREFIX 0xEC
45
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 };
50
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 };
54
55 #define RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET 4
56 static const u8 set_controller_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x21, 0x00, 0x00, 0x00 };
57
58 /* Command lengths */
59 #define GET_CMD_LENGTH 2 /* Same length for all get commands */
60 #define SET_CMD_LENGTH 5 /* Same length for all set commands */
61
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
67
68 static const char *const rog_ryujin_temp_label[] = {
69 "Coolant temp"
70 };
71
72 static const char *const rog_ryujin_speed_label[] = {
73 "Pump speed",
74 "Internal fan speed",
75 "Controller fan 1 speed",
76 "Controller fan 2 speed",
77 "Controller fan 3 speed",
78 "Controller fan 4 speed",
79 };
80
81 struct rog_ryujin_data {
82 struct hid_device *hdev;
83 struct device *hwmon_dev;
84 /* For reinitializing the completions below */
85 spinlock_t status_report_request_lock;
86 struct completion cooler_status_received;
87 struct completion controller_status_received;
88 struct completion cooler_duty_received;
89 struct completion controller_duty_received;
90 struct completion cooler_duty_set;
91 struct completion controller_duty_set;
92
93 /* Sensor data */
94 s32 temp_input[1];
95 u16 speed_input[6]; /* Pump, internal fan and four controller fan speeds in RPM */
96 u8 duty_input[3]; /* Pump, internal fan and controller fan duty in PWM */
97
98 u8 *buffer;
99 unsigned long updated; /* jiffies */
100 };
101
rog_ryujin_percent_to_pwm(u16 val)102 static int rog_ryujin_percent_to_pwm(u16 val)
103 {
104 return DIV_ROUND_CLOSEST(val * 255, 100);
105 }
106
rog_ryujin_pwm_to_percent(long val)107 static int rog_ryujin_pwm_to_percent(long val)
108 {
109 return DIV_ROUND_CLOSEST(val * 100, 255);
110 }
111
rog_ryujin_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)112 static umode_t rog_ryujin_is_visible(const void *data,
113 enum hwmon_sensor_types type, u32 attr, int channel)
114 {
115 switch (type) {
116 case hwmon_temp:
117 switch (attr) {
118 case hwmon_temp_label:
119 case hwmon_temp_input:
120 return 0444;
121 default:
122 break;
123 }
124 break;
125 case hwmon_fan:
126 switch (attr) {
127 case hwmon_fan_label:
128 case hwmon_fan_input:
129 return 0444;
130 default:
131 break;
132 }
133 break;
134 case hwmon_pwm:
135 switch (attr) {
136 case hwmon_pwm_input:
137 return 0644;
138 default:
139 break;
140 }
141 break;
142 default:
143 break;
144 }
145
146 return 0;
147 }
148
149 /* Writes the command to the device with the rest of the report filled with zeroes */
rog_ryujin_write_expanded(struct rog_ryujin_data * priv,const u8 * cmd,int cmd_length)150 static int rog_ryujin_write_expanded(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length)
151 {
152 memcpy_and_pad(priv->buffer, MAX_REPORT_LENGTH, cmd, cmd_length, 0x00);
153 return hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH);
154 }
155
rog_ryujin_execute_cmd(struct rog_ryujin_data * priv,const u8 * cmd,int cmd_length,struct completion * status_completion)156 static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length,
157 struct completion *status_completion)
158 {
159 int ret;
160
161 /*
162 * Disable raw event parsing for a moment to safely reinitialize the
163 * completion. Reinit is done because hidraw could have triggered
164 * the raw event parsing and marked the passed in completion as done.
165 */
166 spin_lock_bh(&priv->status_report_request_lock);
167 reinit_completion(status_completion);
168 spin_unlock_bh(&priv->status_report_request_lock);
169
170 /* Send command for getting data */
171 ret = rog_ryujin_write_expanded(priv, cmd, cmd_length);
172 if (ret < 0)
173 return ret;
174
175 ret = wait_for_completion_interruptible_timeout(status_completion,
176 msecs_to_jiffies(STATUS_VALIDITY));
177 if (ret == 0)
178 return -ETIMEDOUT;
179 else if (ret < 0)
180 return ret;
181
182 return 0;
183 }
184
rog_ryujin_get_status(struct rog_ryujin_data * priv)185 static int rog_ryujin_get_status(struct rog_ryujin_data *priv)
186 {
187 int ret;
188
189 if (!time_after(jiffies, priv->updated + msecs_to_jiffies(STATUS_VALIDITY))) {
190 /* Data is up to date */
191 return 0;
192 }
193
194 /* Retrieve cooler status */
195 ret =
196 rog_ryujin_execute_cmd(priv, get_cooler_status_cmd, GET_CMD_LENGTH,
197 &priv->cooler_status_received);
198 if (ret < 0)
199 return ret;
200
201 /* Retrieve controller status (speeds) */
202 ret =
203 rog_ryujin_execute_cmd(priv, get_controller_speed_cmd, GET_CMD_LENGTH,
204 &priv->controller_status_received);
205 if (ret < 0)
206 return ret;
207
208 /* Retrieve cooler duty */
209 ret =
210 rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
211 &priv->cooler_duty_received);
212 if (ret < 0)
213 return ret;
214
215 /* Retrieve controller duty */
216 ret =
217 rog_ryujin_execute_cmd(priv, get_controller_duty_cmd, GET_CMD_LENGTH,
218 &priv->controller_duty_received);
219 if (ret < 0)
220 return ret;
221
222 priv->updated = jiffies;
223 return 0;
224 }
225
rog_ryujin_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)226 static int rog_ryujin_read(struct device *dev, enum hwmon_sensor_types type,
227 u32 attr, int channel, long *val)
228 {
229 struct rog_ryujin_data *priv = dev_get_drvdata(dev);
230 int ret = rog_ryujin_get_status(priv);
231
232 if (ret < 0)
233 return ret;
234
235 switch (type) {
236 case hwmon_temp:
237 *val = priv->temp_input[channel];
238 break;
239 case hwmon_fan:
240 *val = priv->speed_input[channel];
241 break;
242 case hwmon_pwm:
243 switch (attr) {
244 case hwmon_pwm_input:
245 *val = priv->duty_input[channel];
246 break;
247 default:
248 return -EOPNOTSUPP;
249 }
250 break;
251 default:
252 return -EOPNOTSUPP; /* unreachable */
253 }
254
255 return 0;
256 }
257
rog_ryujin_read_string(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,const char ** str)258 static int rog_ryujin_read_string(struct device *dev, enum hwmon_sensor_types type,
259 u32 attr, int channel, const char **str)
260 {
261 switch (type) {
262 case hwmon_temp:
263 *str = rog_ryujin_temp_label[channel];
264 break;
265 case hwmon_fan:
266 *str = rog_ryujin_speed_label[channel];
267 break;
268 default:
269 return -EOPNOTSUPP; /* unreachable */
270 }
271
272 return 0;
273 }
274
rog_ryujin_write_fixed_duty(struct rog_ryujin_data * priv,int channel,int val)275 static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel, int val)
276 {
277 u8 set_cmd[SET_CMD_LENGTH];
278 int ret;
279
280 if (channel < 2) {
281 /*
282 * Retrieve cooler duty since both pump and internal fan are set
283 * together, then write back with one of them modified.
284 */
285 ret =
286 rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
287 &priv->cooler_duty_received);
288 if (ret < 0)
289 return ret;
290
291 memcpy(set_cmd, set_cooler_duty_cmd, SET_CMD_LENGTH);
292
293 /* Cooler duties are set as 0-100% */
294 val = rog_ryujin_pwm_to_percent(val);
295
296 if (channel == 0) {
297 /* Cooler pump duty */
298 set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] = val;
299 set_cmd[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET] =
300 rog_ryujin_pwm_to_percent(priv->duty_input[1]);
301 } else if (channel == 1) {
302 /* Cooler internal fan duty */
303 set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] =
304 rog_ryujin_pwm_to_percent(priv->duty_input[0]);
305 set_cmd[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET] = val;
306 }
307
308 return rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
309 } else {
310 /*
311 * Controller fan duty (channel == 2). No need to retrieve current
312 * duty, so just send the command.
313 */
314 memcpy(set_cmd, set_controller_duty_cmd, SET_CMD_LENGTH);
315 set_cmd[RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET] = val;
316
317 ret =
318 rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH,
319 &priv->controller_duty_set);
320 if (ret < 0)
321 return ret;
322 }
323
324 /* Lock onto this value until next refresh cycle */
325 priv->duty_input[channel] = val;
326
327 return 0;
328 }
329
rog_ryujin_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)330 static int rog_ryujin_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
331 long val)
332 {
333 struct rog_ryujin_data *priv = dev_get_drvdata(dev);
334 int ret;
335
336 switch (type) {
337 case hwmon_pwm:
338 switch (attr) {
339 case hwmon_pwm_input:
340 if (val < 0 || val > 255)
341 return -EINVAL;
342
343 ret = rog_ryujin_write_fixed_duty(priv, channel, val);
344 if (ret < 0)
345 return ret;
346 break;
347 default:
348 return -EOPNOTSUPP;
349 }
350 break;
351 default:
352 return -EOPNOTSUPP;
353 }
354
355 return 0;
356 }
357
358 static const struct hwmon_ops rog_ryujin_hwmon_ops = {
359 .is_visible = rog_ryujin_is_visible,
360 .read = rog_ryujin_read,
361 .read_string = rog_ryujin_read_string,
362 .write = rog_ryujin_write
363 };
364
365 static const struct hwmon_channel_info *rog_ryujin_info[] = {
366 HWMON_CHANNEL_INFO(temp,
367 HWMON_T_INPUT | HWMON_T_LABEL),
368 HWMON_CHANNEL_INFO(fan,
369 HWMON_F_INPUT | HWMON_F_LABEL,
370 HWMON_F_INPUT | HWMON_F_LABEL,
371 HWMON_F_INPUT | HWMON_F_LABEL,
372 HWMON_F_INPUT | HWMON_F_LABEL,
373 HWMON_F_INPUT | HWMON_F_LABEL,
374 HWMON_F_INPUT | HWMON_F_LABEL),
375 HWMON_CHANNEL_INFO(pwm,
376 HWMON_PWM_INPUT,
377 HWMON_PWM_INPUT,
378 HWMON_PWM_INPUT),
379 NULL
380 };
381
382 static const struct hwmon_chip_info rog_ryujin_chip_info = {
383 .ops = &rog_ryujin_hwmon_ops,
384 .info = rog_ryujin_info,
385 };
386
rog_ryujin_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)387 static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
388 int size)
389 {
390 struct rog_ryujin_data *priv = hid_get_drvdata(hdev);
391
392 if (data[0] != RYUJIN_CMD_PREFIX)
393 return 0;
394
395 if (data[1] == RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE) {
396 /* Received coolant temp and speeds of pump and internal fan */
397 priv->temp_input[0] =
398 data[RYUJIN_TEMP_SENSOR_1] * 1000 + data[RYUJIN_TEMP_SENSOR_2] * 100;
399 priv->speed_input[0] = get_unaligned_le16(data + RYUJIN_PUMP_SPEED);
400 priv->speed_input[1] = get_unaligned_le16(data + RYUJIN_INTERNAL_FAN_SPEED);
401
402 if (!completion_done(&priv->cooler_status_received))
403 complete_all(&priv->cooler_status_received);
404 } else if (data[1] == RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE) {
405 /* Received speeds of four fans attached to the controller */
406 priv->speed_input[2] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_1);
407 priv->speed_input[3] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_2);
408 priv->speed_input[4] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_3);
409 priv->speed_input[5] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_4);
410
411 if (!completion_done(&priv->controller_status_received))
412 complete_all(&priv->controller_status_received);
413 } else if (data[1] == RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE) {
414 /* Received report for pump and internal fan duties (in %) */
415 if (data[RYUJIN_PUMP_DUTY] == 0 && data[RYUJIN_INTERNAL_FAN_DUTY] == 0) {
416 /*
417 * We received a report with zeroes for duty in both places.
418 * The device returns this as a confirmation that setting values
419 * is successful. If we initiated a write, mark it as complete.
420 */
421 if (!completion_done(&priv->cooler_duty_set))
422 complete_all(&priv->cooler_duty_set);
423 else if (!completion_done(&priv->cooler_duty_received))
424 /*
425 * We didn't initiate a write, but received both zeroes.
426 * This means that either both duties are actually zero,
427 * or that we received a success report caused by userspace.
428 * We're expecting a report, so parse it.
429 */
430 goto read_cooler_duty;
431 return 0;
432 }
433 read_cooler_duty:
434 priv->duty_input[0] = rog_ryujin_percent_to_pwm(data[RYUJIN_PUMP_DUTY]);
435 priv->duty_input[1] = rog_ryujin_percent_to_pwm(data[RYUJIN_INTERNAL_FAN_DUTY]);
436
437 if (!completion_done(&priv->cooler_duty_received))
438 complete_all(&priv->cooler_duty_received);
439 } else if (data[1] == RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE) {
440 /* Received report for controller duty for fans (in PWM) */
441 if (data[RYUJIN_CONTROLLER_DUTY] == 0) {
442 /*
443 * We received a report with a zero for duty. The device returns this as
444 * a confirmation that setting the controller duty value was successful.
445 * If we initiated a write, mark it as complete.
446 */
447 if (!completion_done(&priv->controller_duty_set))
448 complete_all(&priv->controller_duty_set);
449 else if (!completion_done(&priv->controller_duty_received))
450 /*
451 * We didn't initiate a write, but received a zero for duty.
452 * This means that either the duty is actually zero, or that
453 * we received a success report caused by userspace.
454 * We're expecting a report, so parse it.
455 */
456 goto read_controller_duty;
457 return 0;
458 }
459 read_controller_duty:
460 priv->duty_input[2] = data[RYUJIN_CONTROLLER_DUTY];
461
462 if (!completion_done(&priv->controller_duty_received))
463 complete_all(&priv->controller_duty_received);
464 }
465
466 return 0;
467 }
468
rog_ryujin_probe(struct hid_device * hdev,const struct hid_device_id * id)469 static int rog_ryujin_probe(struct hid_device *hdev, const struct hid_device_id *id)
470 {
471 struct rog_ryujin_data *priv;
472 int ret;
473
474 priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
475 if (!priv)
476 return -ENOMEM;
477
478 priv->hdev = hdev;
479 hid_set_drvdata(hdev, priv);
480
481 /*
482 * Initialize priv->updated to STATUS_VALIDITY seconds in the past, making
483 * the initial empty data invalid for rog_ryujin_read() without the need for
484 * a special case there.
485 */
486 priv->updated = jiffies - msecs_to_jiffies(STATUS_VALIDITY);
487
488 ret = hid_parse(hdev);
489 if (ret) {
490 hid_err(hdev, "hid parse failed with %d\n", ret);
491 return ret;
492 }
493
494 /* Enable hidraw so existing user-space tools can continue to work */
495 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
496 if (ret) {
497 hid_err(hdev, "hid hw start failed with %d\n", ret);
498 return ret;
499 }
500
501 ret = hid_hw_open(hdev);
502 if (ret) {
503 hid_err(hdev, "hid hw open failed with %d\n", ret);
504 goto fail_and_stop;
505 }
506
507 priv->buffer = devm_kzalloc(&hdev->dev, MAX_REPORT_LENGTH, GFP_KERNEL);
508 if (!priv->buffer) {
509 ret = -ENOMEM;
510 goto fail_and_close;
511 }
512
513 spin_lock_init(&priv->status_report_request_lock);
514 init_completion(&priv->cooler_status_received);
515 init_completion(&priv->controller_status_received);
516 init_completion(&priv->cooler_duty_received);
517 init_completion(&priv->controller_duty_received);
518 init_completion(&priv->cooler_duty_set);
519 init_completion(&priv->controller_duty_set);
520
521 priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "rog_ryujin",
522 priv, &rog_ryujin_chip_info, NULL);
523 if (IS_ERR(priv->hwmon_dev)) {
524 ret = PTR_ERR(priv->hwmon_dev);
525 hid_err(hdev, "hwmon registration failed with %d\n", ret);
526 goto fail_and_close;
527 }
528
529 return 0;
530
531 fail_and_close:
532 hid_hw_close(hdev);
533 fail_and_stop:
534 hid_hw_stop(hdev);
535 return ret;
536 }
537
rog_ryujin_remove(struct hid_device * hdev)538 static void rog_ryujin_remove(struct hid_device *hdev)
539 {
540 struct rog_ryujin_data *priv = hid_get_drvdata(hdev);
541
542 hwmon_device_unregister(priv->hwmon_dev);
543
544 hid_hw_close(hdev);
545 hid_hw_stop(hdev);
546 }
547
548 static const struct hid_device_id rog_ryujin_table[] = {
549 { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_AIO) },
550 { }
551 };
552
553 MODULE_DEVICE_TABLE(hid, rog_ryujin_table);
554
555 static struct hid_driver rog_ryujin_driver = {
556 .name = "rog_ryujin",
557 .id_table = rog_ryujin_table,
558 .probe = rog_ryujin_probe,
559 .remove = rog_ryujin_remove,
560 .raw_event = rog_ryujin_raw_event,
561 };
562
rog_ryujin_init(void)563 static int __init rog_ryujin_init(void)
564 {
565 return hid_register_driver(&rog_ryujin_driver);
566 }
567
rog_ryujin_exit(void)568 static void __exit rog_ryujin_exit(void)
569 {
570 hid_unregister_driver(&rog_ryujin_driver);
571 }
572
573 /* When compiled into the kernel, initialize after the HID bus */
574 late_initcall(rog_ryujin_init);
575 module_exit(rog_ryujin_exit);
576
577 MODULE_LICENSE("GPL");
578 MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");
579 MODULE_DESCRIPTION("Hwmon driver for Asus ROG Ryujin II 360 AIO cooler");
580