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
rog_ryujin_percent_to_pwm(u16 val)124 static int rog_ryujin_percent_to_pwm(u16 val)
125 {
126 return DIV_ROUND_CLOSEST(val * 255, 100);
127 }
128
rog_ryujin_pwm_to_percent(long val)129 static int rog_ryujin_pwm_to_percent(long val)
130 {
131 return DIV_ROUND_CLOSEST(val * 100, 255);
132 }
133
rog_ryujin_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)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 */
rog_ryujin_write_expanded(struct rog_ryujin_data * priv,const u8 * cmd,int cmd_length)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
rog_ryujin_execute_cmd(struct rog_ryujin_data * priv,const u8 * cmd,int cmd_length,struct completion * status_completion)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 unsigned long flags;
188 int ret;
189
190 /*
191 * Disable raw event parsing for a moment to safely reinitialize the
192 * completion. Reinit is done because hidraw could have triggered
193 * the raw event parsing and marked the passed in completion as done.
194 */
195 spin_lock_irqsave(&priv->status_report_request_lock, flags);
196 reinit_completion(status_completion);
197 spin_unlock_irqrestore(&priv->status_report_request_lock, flags);
198
199 /* Send command for getting data */
200 ret = rog_ryujin_write_expanded(priv, cmd, cmd_length);
201 if (ret < 0)
202 return ret;
203
204 ret = wait_for_completion_interruptible_timeout(status_completion,
205 msecs_to_jiffies(STATUS_VALIDITY));
206 if (ret == 0)
207 return -ETIMEDOUT;
208 else if (ret < 0)
209 return ret;
210
211 return 0;
212 }
213
rog_ryujin_get_status(struct rog_ryujin_data * priv)214 static int rog_ryujin_get_status(struct rog_ryujin_data *priv)
215 {
216 int ret;
217
218 if (!time_after(jiffies, priv->updated + msecs_to_jiffies(STATUS_VALIDITY))) {
219 /* Data is up to date */
220 return 0;
221 }
222
223 /* Retrieve cooler status */
224 ret =
225 rog_ryujin_execute_cmd(priv, get_cooler_status_cmd, GET_CMD_LENGTH,
226 &priv->cooler_status_received);
227 if (ret < 0)
228 return ret;
229
230 if (priv->info->has_controller) {
231 /* Retrieve controller status (speeds) */
232 ret = rog_ryujin_execute_cmd(priv, get_controller_speed_cmd,
233 GET_CMD_LENGTH,
234 &priv->controller_status_received);
235 if (ret < 0)
236 return ret;
237 }
238
239 /* Retrieve cooler duty */
240 ret =
241 rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
242 &priv->cooler_duty_received);
243 if (ret < 0)
244 return ret;
245
246 if (priv->info->has_controller) {
247 /* Retrieve controller duty */
248 ret = rog_ryujin_execute_cmd(priv, get_controller_duty_cmd,
249 GET_CMD_LENGTH,
250 &priv->controller_duty_received);
251 if (ret < 0)
252 return ret;
253 }
254
255 priv->updated = jiffies;
256 return 0;
257 }
258
rog_ryujin_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)259 static int rog_ryujin_read(struct device *dev, enum hwmon_sensor_types type,
260 u32 attr, int channel, long *val)
261 {
262 struct rog_ryujin_data *priv = dev_get_drvdata(dev);
263 int ret = rog_ryujin_get_status(priv);
264
265 if (ret < 0)
266 return ret;
267
268 switch (type) {
269 case hwmon_temp:
270 *val = priv->temp_input[channel];
271 break;
272 case hwmon_fan:
273 *val = priv->speed_input[channel];
274 break;
275 case hwmon_pwm:
276 switch (attr) {
277 case hwmon_pwm_input:
278 *val = priv->duty_input[channel];
279 break;
280 default:
281 return -EOPNOTSUPP;
282 }
283 break;
284 default:
285 return -EOPNOTSUPP; /* unreachable */
286 }
287
288 return 0;
289 }
290
rog_ryujin_read_string(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,const char ** str)291 static int rog_ryujin_read_string(struct device *dev, enum hwmon_sensor_types type,
292 u32 attr, int channel, const char **str)
293 {
294 switch (type) {
295 case hwmon_temp:
296 *str = rog_ryujin_temp_label[channel];
297 break;
298 case hwmon_fan:
299 *str = rog_ryujin_speed_label[channel];
300 break;
301 default:
302 return -EOPNOTSUPP; /* unreachable */
303 }
304
305 return 0;
306 }
307
rog_ryujin_write_fixed_duty(struct rog_ryujin_data * priv,int channel,int val)308 static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel, int val)
309 {
310 u8 set_cmd[SET_CMD_LENGTH];
311 int ret;
312
313 if (channel < 2) {
314 /*
315 * Retrieve cooler duty since both pump and internal fan are set
316 * together, then write back with one of them modified.
317 */
318 ret =
319 rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
320 &priv->cooler_duty_received);
321 if (ret < 0)
322 return ret;
323
324 memcpy(set_cmd, set_cooler_duty_cmd, SET_CMD_LENGTH);
325 set_cmd[2] = priv->info->duty_channel;
326
327 /* Cooler duties are set as 0-100% */
328 val = rog_ryujin_pwm_to_percent(val);
329
330 if (channel == 0) {
331 /* Cooler pump duty */
332 set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] = val;
333 set_cmd[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET] =
334 rog_ryujin_pwm_to_percent(priv->duty_input[1]);
335 } else if (channel == 1) {
336 /* Cooler internal fan duty */
337 set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] =
338 rog_ryujin_pwm_to_percent(priv->duty_input[0]);
339 set_cmd[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET] = val;
340 }
341
342 return rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
343 } else {
344 /*
345 * Controller fan duty (channel == 2). No need to retrieve current
346 * duty, so just send the command.
347 */
348 memcpy(set_cmd, set_controller_duty_cmd, SET_CMD_LENGTH);
349 set_cmd[RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET] = val;
350
351 ret =
352 rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH,
353 &priv->controller_duty_set);
354 if (ret < 0)
355 return ret;
356 }
357
358 /* Lock onto this value until next refresh cycle */
359 priv->duty_input[channel] = val;
360
361 return 0;
362 }
363
rog_ryujin_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)364 static int rog_ryujin_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
365 long val)
366 {
367 struct rog_ryujin_data *priv = dev_get_drvdata(dev);
368 int ret;
369
370 switch (type) {
371 case hwmon_pwm:
372 switch (attr) {
373 case hwmon_pwm_input:
374 if (val < 0 || val > 255)
375 return -EINVAL;
376
377 ret = rog_ryujin_write_fixed_duty(priv, channel, val);
378 if (ret < 0)
379 return ret;
380 break;
381 default:
382 return -EOPNOTSUPP;
383 }
384 break;
385 default:
386 return -EOPNOTSUPP;
387 }
388
389 return 0;
390 }
391
392 static const struct hwmon_ops rog_ryujin_hwmon_ops = {
393 .is_visible = rog_ryujin_is_visible,
394 .read = rog_ryujin_read,
395 .read_string = rog_ryujin_read_string,
396 .write = rog_ryujin_write
397 };
398
399 static const struct hwmon_channel_info *rog_ryujin_info[] = {
400 HWMON_CHANNEL_INFO(temp,
401 HWMON_T_INPUT | HWMON_T_LABEL),
402 HWMON_CHANNEL_INFO(fan,
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_F_INPUT | HWMON_F_LABEL),
409 HWMON_CHANNEL_INFO(pwm,
410 HWMON_PWM_INPUT,
411 HWMON_PWM_INPUT,
412 HWMON_PWM_INPUT),
413 NULL
414 };
415
416 static const struct hwmon_chip_info rog_ryujin_chip_info = {
417 .ops = &rog_ryujin_hwmon_ops,
418 .info = rog_ryujin_info,
419 };
420
rog_ryujin_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)421 static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
422 int size)
423 {
424 struct rog_ryujin_data *priv = hid_get_drvdata(hdev);
425 unsigned long flags;
426
427 if (size < 2 || data[0] != RYUJIN_CMD_PREFIX)
428 return 0;
429
430 spin_lock_irqsave(&priv->status_report_request_lock, flags);
431
432 if (data[1] == RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE) {
433 if (size <= priv->info->temp_offset + 1 ||
434 size <= priv->info->pump_speed_offset + 1 ||
435 size <= priv->info->fan_speed_offset + 1)
436 goto unlock;
437
438 /* Received coolant temp and speeds of pump and internal fan */
439 priv->temp_input[0] = data[priv->info->temp_offset] * 1000 +
440 data[priv->info->temp_offset + 1] * 100;
441 priv->speed_input[0] =
442 get_unaligned_le16(data + priv->info->pump_speed_offset);
443 priv->speed_input[1] =
444 get_unaligned_le16(data + priv->info->fan_speed_offset);
445
446 if (!completion_done(&priv->cooler_status_received))
447 complete_all(&priv->cooler_status_received);
448 } else if (data[1] == RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE) {
449 if (size <= RYUJIN_CONTROLLER_SPEED_3 + 1)
450 goto unlock;
451
452 /* Received speeds of four fans attached to the controller */
453 priv->speed_input[2] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_1);
454 priv->speed_input[3] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_2);
455 priv->speed_input[4] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_3);
456 priv->speed_input[5] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_4);
457
458 if (!completion_done(&priv->controller_status_received))
459 complete_all(&priv->controller_status_received);
460 } else if (data[1] == RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE) {
461 if (size <= RYUJIN_INTERNAL_FAN_DUTY)
462 goto unlock;
463
464 /* Received report for pump and internal fan duties (in %) */
465 if (data[RYUJIN_PUMP_DUTY] == 0 && data[RYUJIN_INTERNAL_FAN_DUTY] == 0) {
466 /*
467 * We received a report with zeroes for duty in both places.
468 * The device returns this as a confirmation that setting values
469 * is successful. If we initiated a write, mark it as complete.
470 */
471 if (!completion_done(&priv->cooler_duty_set))
472 complete_all(&priv->cooler_duty_set);
473 else if (!completion_done(&priv->cooler_duty_received))
474 /*
475 * We didn't initiate a write, but received both zeroes.
476 * This means that either both duties are actually zero,
477 * or that we received a success report caused by userspace.
478 * We're expecting a report, so parse it.
479 */
480 goto read_cooler_duty;
481 goto unlock;
482 }
483 read_cooler_duty:
484 priv->duty_input[0] = rog_ryujin_percent_to_pwm(data[RYUJIN_PUMP_DUTY]);
485 priv->duty_input[1] = rog_ryujin_percent_to_pwm(data[RYUJIN_INTERNAL_FAN_DUTY]);
486
487 if (!completion_done(&priv->cooler_duty_received))
488 complete_all(&priv->cooler_duty_received);
489 } else if (data[1] == RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE) {
490 if (size <= RYUJIN_CONTROLLER_DUTY)
491 goto unlock;
492
493 /* Received report for controller duty for fans (in PWM) */
494 if (data[RYUJIN_CONTROLLER_DUTY] == 0) {
495 /*
496 * We received a report with a zero for duty. The device returns this as
497 * a confirmation that setting the controller duty value was successful.
498 * If we initiated a write, mark it as complete.
499 */
500 if (!completion_done(&priv->controller_duty_set))
501 complete_all(&priv->controller_duty_set);
502 else if (!completion_done(&priv->controller_duty_received))
503 /*
504 * We didn't initiate a write, but received a zero for duty.
505 * This means that either the duty is actually zero, or that
506 * we received a success report caused by userspace.
507 * We're expecting a report, so parse it.
508 */
509 goto read_controller_duty;
510 goto unlock;
511 }
512 read_controller_duty:
513 priv->duty_input[2] = data[RYUJIN_CONTROLLER_DUTY];
514
515 if (!completion_done(&priv->controller_duty_received))
516 complete_all(&priv->controller_duty_received);
517 }
518
519 unlock:
520 spin_unlock_irqrestore(&priv->status_report_request_lock, flags);
521 return 0;
522 }
523
rog_ryujin_probe(struct hid_device * hdev,const struct hid_device_id * id)524 static int rog_ryujin_probe(struct hid_device *hdev, const struct hid_device_id *id)
525 {
526 struct rog_ryujin_data *priv;
527 int ret;
528
529 if (!id->driver_data)
530 return -EINVAL;
531
532 priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
533 if (!priv)
534 return -ENOMEM;
535
536 priv->hdev = hdev;
537 priv->info = (const struct rog_ryujin_device_info *)id->driver_data;
538 hid_set_drvdata(hdev, priv);
539
540 /*
541 * Initialize priv->updated to STATUS_VALIDITY seconds in the past, making
542 * the initial empty data invalid for rog_ryujin_read() without the need for
543 * a special case there.
544 */
545 priv->updated = jiffies - msecs_to_jiffies(STATUS_VALIDITY);
546
547 ret = hid_parse(hdev);
548 if (ret) {
549 hid_err(hdev, "hid parse failed with %d\n", ret);
550 return ret;
551 }
552
553 /* Enable hidraw so existing user-space tools can continue to work */
554 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
555 if (ret) {
556 hid_err(hdev, "hid hw start failed with %d\n", ret);
557 return ret;
558 }
559
560 ret = hid_hw_open(hdev);
561 if (ret) {
562 hid_err(hdev, "hid hw open failed with %d\n", ret);
563 goto fail_and_stop;
564 }
565
566 priv->buffer = devm_kzalloc(&hdev->dev, MAX_REPORT_LENGTH, GFP_KERNEL);
567 if (!priv->buffer) {
568 ret = -ENOMEM;
569 goto fail_and_close;
570 }
571
572 spin_lock_init(&priv->status_report_request_lock);
573 init_completion(&priv->cooler_status_received);
574 init_completion(&priv->controller_status_received);
575 init_completion(&priv->cooler_duty_received);
576 init_completion(&priv->controller_duty_received);
577 init_completion(&priv->cooler_duty_set);
578 init_completion(&priv->controller_duty_set);
579
580 priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "rog_ryujin",
581 priv, &rog_ryujin_chip_info, NULL);
582 if (IS_ERR(priv->hwmon_dev)) {
583 ret = PTR_ERR(priv->hwmon_dev);
584 hid_err(hdev, "hwmon registration failed with %d\n", ret);
585 goto fail_and_close;
586 }
587
588 return 0;
589
590 fail_and_close:
591 hid_hw_close(hdev);
592 fail_and_stop:
593 hid_hw_stop(hdev);
594 return ret;
595 }
596
rog_ryujin_remove(struct hid_device * hdev)597 static void rog_ryujin_remove(struct hid_device *hdev)
598 {
599 struct rog_ryujin_data *priv = hid_get_drvdata(hdev);
600
601 hwmon_device_unregister(priv->hwmon_dev);
602
603 hid_hw_close(hdev);
604 hid_hw_stop(hdev);
605 }
606
607 static const struct hid_device_id rog_ryujin_table[] = {
608 { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_AIO),
609 .driver_data = (kernel_ulong_t)&rog_ryujin_ii_360_info },
610 { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_III_EXTREME),
611 .driver_data = (kernel_ulong_t)&rog_ryujin_iii_info },
612 { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_III_EVA),
613 .driver_data = (kernel_ulong_t)&rog_ryujin_iii_info },
614 { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_III_WHITE),
615 .driver_data = (kernel_ulong_t)&rog_ryujin_iii_info },
616 { }
617 };
618
619 MODULE_DEVICE_TABLE(hid, rog_ryujin_table);
620
621 static struct hid_driver rog_ryujin_driver = {
622 .name = "rog_ryujin",
623 .id_table = rog_ryujin_table,
624 .probe = rog_ryujin_probe,
625 .remove = rog_ryujin_remove,
626 .raw_event = rog_ryujin_raw_event,
627 };
628
rog_ryujin_init(void)629 static int __init rog_ryujin_init(void)
630 {
631 return hid_register_driver(&rog_ryujin_driver);
632 }
633
rog_ryujin_exit(void)634 static void __exit rog_ryujin_exit(void)
635 {
636 hid_unregister_driver(&rog_ryujin_driver);
637 }
638
639 /* When compiled into the kernel, initialize after the HID bus */
640 late_initcall(rog_ryujin_init);
641 module_exit(rog_ryujin_exit);
642
643 MODULE_LICENSE("GPL");
644 MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");
645 MODULE_DESCRIPTION("Hwmon driver for Asus ROG Ryujin AIO coolers");
646