File Index
Files and directories
Sign in
FILE

asus_rog_ryujin.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hwmon/asus_rog_ryujin.c C
  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
 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 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
102static int rog_ryujin_percent_to_pwm(u16 val)
103{
104	return DIV_ROUND_CLOSEST(val * 255, 100);
105}
106
107static int rog_ryujin_pwm_to_percent(long val)
108{
109	return DIV_ROUND_CLOSEST(val * 100, 255);
110}
111
112static 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 */
150static 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
156static 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
185static 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
226static 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
258static 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
275static 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
330static 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
358static 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
365static 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
382static const struct hwmon_chip_info rog_ryujin_chip_info = {
383	.ops = &rog_ryujin_hwmon_ops,
384	.info = rog_ryujin_info,
385};
386
387static 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		}
433read_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		}
459read_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
469static 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
531fail_and_close:
532	hid_hw_close(hdev);
533fail_and_stop:
534	hid_hw_stop(hdev);
535	return ret;
536}
537
538static 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
548static 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
553MODULE_DEVICE_TABLE(hid, rog_ryujin_table);
554
555static 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
563static int __init rog_ryujin_init(void)
564{
565	return hid_register_driver(&rog_ryujin_driver);
566}
567
568static 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 */
574late_initcall(rog_ryujin_init);
575module_exit(rog_ryujin_exit);
576
577MODULE_LICENSE("GPL");
578MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");
579MODULE_DESCRIPTION("Hwmon driver for Asus ROG Ryujin II 360 AIO cooler");
~/ Read-only file preview.