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 <asm/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
46static const u8 get_cooler_status_cmd[] = { RYUJIN_CMD_PREFIX, 0x99 };
47static const u8 get_cooler_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x9A };
48static const u8 get_controller_speed_cmd[] = { RYUJIN_CMD_PREFIX, 0xA0 };
49static 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
53static const u8 set_cooler_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x1A, 0x00, 0x00, 0x00 };
54
55#define RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET	4
56static 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
68static const char *const rog_ryujin_temp_label[] = {
69	"Coolant temp"
70};
71
72static 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
81struct 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;
96
97	/* Sensor data */
98	s32 temp_input[1];
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 */
101
102	u8 *buffer;
103	unsigned long updated;	/* jiffies */
104};
105
106static int rog_ryujin_percent_to_pwm(u16 val)
107{
108	return DIV_ROUND_CLOSEST(val * 255, 100);
109}
110
111static int rog_ryujin_pwm_to_percent(long val)
112{
113	return DIV_ROUND_CLOSEST(val * 100, 255);
114}
115
116static umode_t rog_ryujin_is_visible(const void *data,
117				     enum hwmon_sensor_types type, u32 attr, int channel)
118{
119	switch (type) {
120	case hwmon_temp:
121		switch (attr) {
122		case hwmon_temp_label:
123		case hwmon_temp_input:
124			return 0444;
125		default:
126			break;
127		}
128		break;
129	case hwmon_fan:
130		switch (attr) {
131		case hwmon_fan_label:
132		case hwmon_fan_input:
133			return 0444;
134		default:
135			break;
136		}
137		break;
138	case hwmon_pwm:
139		switch (attr) {
140		case hwmon_pwm_input:
141			return 0644;
142		default:
143			break;
144		}
145		break;
146	default:
147		break;
148	}
149
150	return 0;
151}
152
153/* Writes the command to the device with the rest of the report filled with zeroes */
154static int rog_ryujin_write_expanded(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length)
155{
156	int ret;
157
158	mutex_lock(&priv->buffer_lock);
159
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);
162
163	mutex_unlock(&priv->buffer_lock);
164	return ret;
165}
166
167/* Assumes priv->status_report_request_mutex is locked */
168static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length,
169				  struct completion *status_completion)
170{
171	int ret;
172
173	/*
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.
177	 */
178	spin_lock_bh(&priv->status_report_request_lock);
179	reinit_completion(status_completion);
180	spin_unlock_bh(&priv->status_report_request_lock);
181
182	/* Send command for getting data */
183	ret = rog_ryujin_write_expanded(priv, cmd, cmd_length);
184	if (ret < 0)
185		return ret;
186
187	ret = wait_for_completion_interruptible_timeout(status_completion,
188							msecs_to_jiffies(STATUS_VALIDITY));
189	if (ret == 0)
190		return -ETIMEDOUT;
191	else if (ret < 0)
192		return ret;
193
194	return 0;
195}
196
197static int rog_ryujin_get_status(struct rog_ryujin_data *priv)
198{
199	int ret = mutex_lock_interruptible(&priv->status_report_request_mutex);
200
201	if (ret < 0)
202		return ret;
203
204	if (!time_after(jiffies, priv->updated + msecs_to_jiffies(STATUS_VALIDITY))) {
205		/* Data is up to date */
206		goto unlock_and_return;
207	}
208
209	/* Retrieve cooler status */
210	ret =
211	    rog_ryujin_execute_cmd(priv, get_cooler_status_cmd, GET_CMD_LENGTH,
212				   &priv->cooler_status_received);
213	if (ret < 0)
214		goto unlock_and_return;
215
216	/* Retrieve controller status (speeds) */
217	ret =
218	    rog_ryujin_execute_cmd(priv, get_controller_speed_cmd, GET_CMD_LENGTH,
219				   &priv->controller_status_received);
220	if (ret < 0)
221		goto unlock_and_return;
222
223	/* Retrieve cooler duty */
224	ret =
225	    rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
226				   &priv->cooler_duty_received);
227	if (ret < 0)
228		goto unlock_and_return;
229
230	/* Retrieve controller duty */
231	ret =
232	    rog_ryujin_execute_cmd(priv, get_controller_duty_cmd, GET_CMD_LENGTH,
233				   &priv->controller_duty_received);
234	if (ret < 0)
235		goto unlock_and_return;
236
237	priv->updated = jiffies;
238
239unlock_and_return:
240	mutex_unlock(&priv->status_report_request_mutex);
241	if (ret < 0)
242		return ret;
243
244	return 0;
245}
246
247static int rog_ryujin_read(struct device *dev, enum hwmon_sensor_types type,
248			   u32 attr, int channel, long *val)
249{
250	struct rog_ryujin_data *priv = dev_get_drvdata(dev);
251	int ret = rog_ryujin_get_status(priv);
252
253	if (ret < 0)
254		return ret;
255
256	switch (type) {
257	case hwmon_temp:
258		*val = priv->temp_input[channel];
259		break;
260	case hwmon_fan:
261		*val = priv->speed_input[channel];
262		break;
263	case hwmon_pwm:
264		switch (attr) {
265		case hwmon_pwm_input:
266			*val = priv->duty_input[channel];
267			break;
268		default:
269			return -EOPNOTSUPP;
270		}
271		break;
272	default:
273		return -EOPNOTSUPP;	/* unreachable */
274	}
275
276	return 0;
277}
278
279static int rog_ryujin_read_string(struct device *dev, enum hwmon_sensor_types type,
280				  u32 attr, int channel, const char **str)
281{
282	switch (type) {
283	case hwmon_temp:
284		*str = rog_ryujin_temp_label[channel];
285		break;
286	case hwmon_fan:
287		*str = rog_ryujin_speed_label[channel];
288		break;
289	default:
290		return -EOPNOTSUPP;	/* unreachable */
291	}
292
293	return 0;
294}
295
296static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel, int val)
297{
298	u8 set_cmd[SET_CMD_LENGTH];
299	int ret;
300
301	if (channel < 2) {
302		/*
303		 * Retrieve cooler duty since both pump and internal fan are set
304		 * together, then write back with one of them modified.
305		 */
306		ret = mutex_lock_interruptible(&priv->status_report_request_mutex);
307		if (ret < 0)
308			return ret;
309		ret =
310		    rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
311					   &priv->cooler_duty_received);
312		if (ret < 0)
313			goto unlock_and_return;
314
315		memcpy(set_cmd, set_cooler_duty_cmd, SET_CMD_LENGTH);
316
317		/* Cooler duties are set as 0-100% */
318		val = rog_ryujin_pwm_to_percent(val);
319
320		if (channel == 0) {
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;
330		}
331
332		ret = rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
333unlock_and_return:
334		mutex_unlock(&priv->status_report_request_mutex);
335		if (ret < 0)
336			return ret;
337	} else {
338		/*
339		 * Controller fan duty (channel == 2). No need to retrieve current
340		 * duty, so just send the command.
341		 */
342		memcpy(set_cmd, set_controller_duty_cmd, SET_CMD_LENGTH);
343		set_cmd[RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET] = val;
344
345		ret =
346		    rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH,
347					   &priv->controller_duty_set);
348		if (ret < 0)
349			return ret;
350	}
351
352	/* Lock onto this value until next refresh cycle */
353	priv->duty_input[channel] = val;
354
355	return 0;
356}
357
358static int rog_ryujin_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
359			    long val)
360{
361	struct rog_ryujin_data *priv = dev_get_drvdata(dev);
362	int ret;
363
364	switch (type) {
365	case hwmon_pwm:
366		switch (attr) {
367		case hwmon_pwm_input:
368			if (val < 0 || val > 255)
369				return -EINVAL;
370
371			ret = rog_ryujin_write_fixed_duty(priv, channel, val);
372			if (ret < 0)
373				return ret;
374			break;
375		default:
376			return -EOPNOTSUPP;
377		}
378		break;
379	default:
380		return -EOPNOTSUPP;
381	}
382
383	return 0;
384}
385
386static 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
391};
392
393static 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,
404			   HWMON_PWM_INPUT,
405			   HWMON_PWM_INPUT,
406			   HWMON_PWM_INPUT),
407	NULL
408};
409
410static const struct hwmon_chip_info rog_ryujin_chip_info = {
411	.ops = &rog_ryujin_hwmon_ops,
412	.info = rog_ryujin_info,
413};
414
415static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
416				int size)
417{
418	struct rog_ryujin_data *priv = hid_get_drvdata(hdev);
419
420	if (data[0] != RYUJIN_CMD_PREFIX)
421		return 0;
422
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);
429
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);
438
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) {
444			/*
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.
448			 */
449			if (!completion_done(&priv->cooler_duty_set))
450				complete_all(&priv->cooler_duty_set);
451			else if (!completion_done(&priv->cooler_duty_received))
452				/*
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.
457				 */
458				goto read_cooler_duty;
459			return 0;
460		}
461read_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]);
464
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) {
470			/*
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.
474			 */
475			if (!completion_done(&priv->controller_duty_set))
476				complete_all(&priv->controller_duty_set);
477			else if (!completion_done(&priv->controller_duty_received))
478				/*
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.
483				 */
484				goto read_controller_duty;
485			return 0;
486		}
487read_controller_duty:
488		priv->duty_input[2] = data[RYUJIN_CONTROLLER_DUTY];
489
490		if (!completion_done(&priv->controller_duty_received))
491			complete_all(&priv->controller_duty_received);
492	}
493
494	return 0;
495}
496
497static int rog_ryujin_probe(struct hid_device *hdev, const struct hid_device_id *id)
498{
499	struct rog_ryujin_data *priv;
500	int ret;
501
502	priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
503	if (!priv)
504		return -ENOMEM;
505
506	priv->hdev = hdev;
507	hid_set_drvdata(hdev, priv);
508
509	/*
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.
513	 */
514	priv->updated = jiffies - msecs_to_jiffies(STATUS_VALIDITY);
515
516	ret = hid_parse(hdev);
517	if (ret) {
518		hid_err(hdev, "hid parse failed with %d\n", ret);
519		return ret;
520	}
521
522	/* Enable hidraw so existing user-space tools can continue to work */
523	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
524	if (ret) {
525		hid_err(hdev, "hid hw start failed with %d\n", ret);
526		return ret;
527	}
528
529	ret = hid_hw_open(hdev);
530	if (ret) {
531		hid_err(hdev, "hid hw open failed with %d\n", ret);
532		goto fail_and_stop;
533	}
534
535	priv->buffer = devm_kzalloc(&hdev->dev, MAX_REPORT_LENGTH, GFP_KERNEL);
536	if (!priv->buffer) {
537		ret = -ENOMEM;
538		goto fail_and_close;
539	}
540
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);
550
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);
556		goto fail_and_close;
557	}
558
559	return 0;
560
561fail_and_close:
562	hid_hw_close(hdev);
563fail_and_stop:
564	hid_hw_stop(hdev);
565	return ret;
566}
567
568static void rog_ryujin_remove(struct hid_device *hdev)
569{
570	struct rog_ryujin_data *priv = hid_get_drvdata(hdev);
571
572	hwmon_device_unregister(priv->hwmon_dev);
573
574	hid_hw_close(hdev);
575	hid_hw_stop(hdev);
576}
577
578static const struct hid_device_id rog_ryujin_table[] = {
579	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_AIO) },
580	{ }
581};
582
583MODULE_DEVICE_TABLE(hid, rog_ryujin_table);
584
585static 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,
591};
592
593static int __init rog_ryujin_init(void)
594{
595	return hid_register_driver(&rog_ryujin_driver);
596}
597
598static void __exit rog_ryujin_exit(void)
599{
600	hid_unregister_driver(&rog_ryujin_driver);
601}
602
603/* When compiled into the kernel, initialize after the HID bus */
604late_initcall(rog_ryujin_init);
605module_exit(rog_ryujin_exit);
606
607MODULE_LICENSE("GPL");
608MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");
609MODULE_DESCRIPTION("Hwmon driver for Asus ROG Ryujin II 360 AIO cooler");
610