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