1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * HID driver for Lenovo Legion Go S devices.
4 *
5 * Copyright (c) 2026 Derek J. Clark <derekjohn.clark@gmail.com>
6 * Copyright (c) 2026 Valve Corporation
7 */
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9
10 #include <linux/array_size.h>
11 #include <linux/cleanup.h>
12 #include <linux/completion.h>
13 #include <linux/delay.h>
14 #include <linux/dev_printk.h>
15 #include <linux/device.h>
16 #include <linux/hid.h>
17 #include <linux/jiffies.h>
18 #include <linux/kstrtox.h>
19 #include <linux/led-class-multicolor.h>
20 #include <linux/mutex.h>
21 #include <linux/printk.h>
22 #include <linux/string.h>
23 #include <linux/sysfs.h>
24 #include <linux/types.h>
25 #include <linux/unaligned.h>
26 #include <linux/usb.h>
27 #include <linux/workqueue.h>
28 #include <linux/workqueue_types.h>
29
30 #include "hid-ids.h"
31
32 #define GO_S_CFG_INTF_IN 0x84
33 #define GO_S_PACKET_SIZE 64
34
35 static struct hid_gos_cfg {
36 struct delayed_work gos_cfg_setup;
37 struct completion send_cmd_complete;
38 struct led_classdev *led_cdev;
39 struct hid_device *hdev;
40 struct mutex cfg_mutex; /*ensure single synchronous output report*/
41 u8 gp_auto_sleep_time;
42 u8 gp_dpad_mode;
43 u8 gp_mode;
44 u8 gp_poll_rate;
45 u8 imu_bypass_en;
46 u8 imu_manufacturer;
47 u8 imu_sensor_en;
48 u8 mcu_id[12];
49 u8 mouse_step;
50 u8 os_mode;
51 u8 rgb_effect;
52 u8 rgb_en;
53 u8 rgb_mode;
54 u8 rgb_profile;
55 u8 rgb_speed;
56 u8 tp_en;
57 u8 tp_linux_mode;
58 u8 tp_windows_mode;
59 u8 tp_version;
60 u8 tp_manufacturer;
61 } drvdata;
62
63 struct gos_cfg_attr {
64 u8 index;
65 };
66
67 struct command_report {
68 u8 cmd;
69 u8 sub_cmd;
70 u8 data[63];
71 } __packed;
72
73 struct version_report {
74 u8 cmd;
75 u32 version;
76 u8 reserved[59];
77 } __packed;
78
79 enum mcu_command_index {
80 GET_VERSION = 0x01,
81 GET_MCU_ID,
82 GET_GAMEPAD_CFG,
83 SET_GAMEPAD_CFG,
84 GET_TP_PARAM,
85 SET_TP_PARAM,
86 GET_RGB_CFG = 0x0f,
87 SET_RGB_CFG,
88 GET_PL_TEST = 0xdf,
89 };
90
91 enum feature_enabled_index {
92 FEATURE_DISABLED,
93 FEATURE_ENABLED,
94 };
95
96 static const char *const feature_enabled_text[] = {
97 [FEATURE_DISABLED] = "false",
98 [FEATURE_ENABLED] = "true",
99 };
100
101 enum feature_status_index {
102 FEATURE_NONE = 0x00,
103 FEATURE_GAMEPAD_MODE = 0x01,
104 FEATURE_AUTO_SLEEP_TIME = 0x04,
105 FEATURE_IMU_BYPASS,
106 FEATURE_RGB_ENABLE,
107 FEATURE_IMU_ENABLE,
108 FEATURE_TOUCHPAD_ENABLE,
109 FEATURE_OS_MODE = 0x0A,
110 FEATURE_POLL_RATE = 0x10,
111 FEATURE_DPAD_MODE,
112 FEATURE_MOUSE_WHEEL_STEP,
113 };
114
115 enum gamepad_mode_index {
116 XINPUT,
117 DINPUT,
118 };
119
120 static const char *const gamepad_mode_text[] = {
121 [XINPUT] = "xinput",
122 [DINPUT] = "dinput",
123 };
124
125 enum os_type_index {
126 WINDOWS,
127 LINUX,
128 };
129
130 static const char *const os_type_text[] = {
131 [WINDOWS] = "windows",
132 [LINUX] = "linux",
133 };
134
135 enum poll_rate_index {
136 HZ125,
137 HZ250,
138 HZ500,
139 HZ1000,
140 };
141
142 static const char *const poll_rate_text[] = {
143 [HZ125] = "125",
144 [HZ250] = "250",
145 [HZ500] = "500",
146 [HZ1000] = "1000",
147 };
148
149 enum dpad_mode_index {
150 DIR8,
151 DIR4,
152 };
153
154 static const char *const dpad_mode_text[] = {
155 [DIR8] = "8-way",
156 [DIR4] = "4-way",
157 };
158
159 enum touchpad_mode_index {
160 TP_REL,
161 TP_ABS,
162 };
163
164 static const char *const touchpad_mode_text[] = {
165 [TP_REL] = "relative",
166 [TP_ABS] = "absolute",
167 };
168
169 enum touchpad_config_index {
170 CFG_WINDOWS_MODE = 0x03,
171 CFG_LINUX_MODE,
172
173 };
174
175 enum rgb_mode_index {
176 RGB_MODE_DYNAMIC,
177 RGB_MODE_CUSTOM,
178 };
179
180 static const char *const rgb_mode_text[] = {
181 [RGB_MODE_DYNAMIC] = "dynamic",
182 [RGB_MODE_CUSTOM] = "custom",
183 };
184
185 enum rgb_effect_index {
186 RGB_EFFECT_MONO,
187 RGB_EFFECT_BREATHE,
188 RGB_EFFECT_CHROMA,
189 RGB_EFFECT_RAINBOW,
190 };
191
192 static const char *const rgb_effect_text[] = {
193 [RGB_EFFECT_MONO] = "monocolor",
194 [RGB_EFFECT_BREATHE] = "breathe",
195 [RGB_EFFECT_CHROMA] = "chroma",
196 [RGB_EFFECT_RAINBOW] = "rainbow",
197 };
198
199 enum rgb_config_index {
200 LIGHT_MODE_SEL = 0x01,
201 LIGHT_PROFILE_SEL,
202 USR_LIGHT_PROFILE_1,
203 USR_LIGHT_PROFILE_2,
204 USR_LIGHT_PROFILE_3,
205 };
206
207 enum test_command_index {
208 TEST_TP_MFR = 0x02,
209 TEST_IMU_MFR,
210 TEST_TP_VER,
211 };
212
213 enum tp_mfr_index {
214 TP_NONE,
215 TP_BETTERLIFE,
216 TP_SIPO,
217 };
218
219 static const char *const touchpad_manufacturer_text[] = {
220 [TP_NONE] = "none",
221 [TP_BETTERLIFE] = "BetterLife",
222 [TP_SIPO] = "SIPO",
223 };
224
225 enum imu_mfr_index {
226 IMU_NONE,
227 IMU_BOSCH,
228 IMU_ST,
229 };
230
231 static const char *const imu_manufacturer_text[] = {
232 [IMU_NONE] = "none",
233 [IMU_BOSCH] = "Bosch",
234 [IMU_ST] = "ST",
235 };
236
hid_gos_version_event(u8 * data)237 static int hid_gos_version_event(u8 *data)
238 {
239 struct version_report *ver_rep = (struct version_report *)data;
240
241 drvdata.hdev->firmware_version = get_unaligned_le32(&ver_rep->version);
242 return 0;
243 }
244
hid_gos_mcu_id_event(struct command_report * cmd_rep)245 static int hid_gos_mcu_id_event(struct command_report *cmd_rep)
246 {
247 drvdata.mcu_id[0] = cmd_rep->sub_cmd;
248 memcpy(&drvdata.mcu_id[1], cmd_rep->data, 11);
249
250 return 0;
251 }
252
hid_gos_gamepad_cfg_event(struct command_report * cmd_rep)253 static int hid_gos_gamepad_cfg_event(struct command_report *cmd_rep)
254 {
255 int ret = 0;
256
257 switch (cmd_rep->sub_cmd) {
258 case FEATURE_GAMEPAD_MODE:
259 drvdata.gp_mode = cmd_rep->data[0];
260 break;
261 case FEATURE_AUTO_SLEEP_TIME:
262 drvdata.gp_auto_sleep_time = cmd_rep->data[0];
263 break;
264 case FEATURE_IMU_BYPASS:
265 drvdata.imu_bypass_en = cmd_rep->data[0];
266 break;
267 case FEATURE_RGB_ENABLE:
268 drvdata.rgb_en = cmd_rep->data[0];
269 break;
270 case FEATURE_IMU_ENABLE:
271 drvdata.imu_sensor_en = cmd_rep->data[0];
272 break;
273 case FEATURE_TOUCHPAD_ENABLE:
274 drvdata.tp_en = cmd_rep->data[0];
275 break;
276 case FEATURE_OS_MODE:
277 drvdata.os_mode = cmd_rep->data[0];
278 break;
279 case FEATURE_POLL_RATE:
280 drvdata.gp_poll_rate = cmd_rep->data[0];
281 break;
282 case FEATURE_DPAD_MODE:
283 drvdata.gp_dpad_mode = cmd_rep->data[0];
284 break;
285 case FEATURE_MOUSE_WHEEL_STEP:
286 drvdata.mouse_step = cmd_rep->data[0];
287 break;
288 default:
289 ret = -EINVAL;
290 break;
291 }
292
293 return ret;
294 }
295
hid_gos_touchpad_event(struct command_report * cmd_rep)296 static int hid_gos_touchpad_event(struct command_report *cmd_rep)
297 {
298 int ret = 0;
299
300 switch (cmd_rep->sub_cmd) {
301 case CFG_LINUX_MODE:
302 drvdata.tp_linux_mode = cmd_rep->data[0];
303 break;
304 case CFG_WINDOWS_MODE:
305 drvdata.tp_windows_mode = cmd_rep->data[0];
306 break;
307 default:
308 ret = -EINVAL;
309 break;
310 }
311
312 return ret;
313 }
314
hid_gos_pl_test_event(struct command_report * cmd_rep)315 static int hid_gos_pl_test_event(struct command_report *cmd_rep)
316 {
317 int ret = 0;
318
319 switch (cmd_rep->sub_cmd) {
320 case TEST_TP_MFR:
321 drvdata.tp_manufacturer = cmd_rep->data[0];
322 ret = 0;
323 break;
324 case TEST_IMU_MFR:
325 drvdata.imu_manufacturer = cmd_rep->data[0];
326 ret = 0;
327 break;
328 case TEST_TP_VER:
329 drvdata.tp_version = cmd_rep->data[0];
330 ret = 0;
331 break;
332 default:
333 ret = -EINVAL;
334 break;
335 }
336 return ret;
337 }
338
hid_gos_light_event(struct command_report * cmd_rep)339 static int hid_gos_light_event(struct command_report *cmd_rep)
340 {
341 struct led_classdev_mc *mc_cdev;
342 int ret = 0;
343
344 switch (cmd_rep->sub_cmd) {
345 case LIGHT_MODE_SEL:
346 drvdata.rgb_mode = cmd_rep->data[0];
347 ret = 0;
348 break;
349 case LIGHT_PROFILE_SEL:
350 drvdata.rgb_profile = cmd_rep->data[0];
351 ret = 0;
352 break;
353 case USR_LIGHT_PROFILE_1:
354 case USR_LIGHT_PROFILE_2:
355 case USR_LIGHT_PROFILE_3:
356 mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);
357 drvdata.rgb_effect = cmd_rep->data[0];
358 mc_cdev->subled_info[0].intensity = cmd_rep->data[1];
359 mc_cdev->subled_info[1].intensity = cmd_rep->data[2];
360 mc_cdev->subled_info[2].intensity = cmd_rep->data[3];
361 drvdata.led_cdev->brightness = cmd_rep->data[4];
362 drvdata.rgb_speed = cmd_rep->data[5];
363 ret = 0;
364 break;
365 default:
366 ret = -EINVAL;
367 break;
368 }
369 return ret;
370 }
371
hid_gos_set_event_return(struct command_report * cmd_rep)372 static int hid_gos_set_event_return(struct command_report *cmd_rep)
373 {
374 if (cmd_rep->data[0] != 0)
375 return -EIO;
376
377 return 0;
378 }
379
get_endpoint_address(struct hid_device * hdev)380 static int get_endpoint_address(struct hid_device *hdev)
381 {
382 struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
383 struct usb_host_endpoint *ep;
384
385 ep = intf->cur_altsetting->endpoint;
386 if (ep)
387 return ep->desc.bEndpointAddress;
388
389 return -ENODEV;
390 }
391
hid_gos_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)392 static int hid_gos_raw_event(struct hid_device *hdev, struct hid_report *report,
393 u8 *data, int size)
394 {
395 struct command_report *cmd_rep;
396 int ep, ret;
397
398 ep = get_endpoint_address(hdev);
399 if (ep != GO_S_CFG_INTF_IN)
400 return 0;
401
402 if (size != GO_S_PACKET_SIZE)
403 return -EINVAL;
404
405 cmd_rep = (struct command_report *)data;
406
407 switch (cmd_rep->cmd) {
408 case GET_VERSION:
409 ret = hid_gos_version_event(data);
410 break;
411 case GET_MCU_ID:
412 ret = hid_gos_mcu_id_event(cmd_rep);
413 break;
414 case GET_GAMEPAD_CFG:
415 ret = hid_gos_gamepad_cfg_event(cmd_rep);
416 break;
417 case GET_TP_PARAM:
418 ret = hid_gos_touchpad_event(cmd_rep);
419 break;
420 case GET_PL_TEST:
421 ret = hid_gos_pl_test_event(cmd_rep);
422 break;
423 case GET_RGB_CFG:
424 ret = hid_gos_light_event(cmd_rep);
425 break;
426 case SET_GAMEPAD_CFG:
427 case SET_RGB_CFG:
428 case SET_TP_PARAM:
429 ret = hid_gos_set_event_return(cmd_rep);
430 break;
431 default:
432 ret = -EINVAL;
433 break;
434 }
435 dev_dbg(&hdev->dev, "Rx data as raw input report: [%*ph]\n",
436 GO_S_PACKET_SIZE, data);
437
438 complete(&drvdata.send_cmd_complete);
439 return ret;
440 }
441
mcu_property_out(struct hid_device * hdev,u8 command,u8 index,u8 * data,size_t len)442 static int mcu_property_out(struct hid_device *hdev, u8 command, u8 index,
443 u8 *data, size_t len)
444 {
445 unsigned char *dmabuf __free(kfree) = NULL;
446 u8 header[] = { command, index };
447 size_t header_size = ARRAY_SIZE(header);
448 int timeout, ret;
449
450 if (header_size + len > GO_S_PACKET_SIZE)
451 return -EINVAL;
452
453 guard(mutex)(&drvdata.cfg_mutex);
454 /* We can't use a devm_alloc reusable buffer without side effects during suspend */
455 dmabuf = kzalloc(GO_S_PACKET_SIZE, GFP_KERNEL);
456 if (!dmabuf)
457 return -ENOMEM;
458
459 memcpy(dmabuf, header, header_size);
460 memcpy(dmabuf + header_size, data, len);
461
462 dev_dbg(&hdev->dev, "Send data as raw output report: [%*ph]\n",
463 GO_S_PACKET_SIZE, dmabuf);
464
465 ret = hid_hw_output_report(hdev, dmabuf, GO_S_PACKET_SIZE);
466 if (ret < 0)
467 return ret;
468
469 ret = ret == GO_S_PACKET_SIZE ? 0 : -EINVAL;
470 if (ret)
471 return ret;
472
473 /* PL_TEST commands can take longer because they go out to another device */
474 timeout = (command == GET_PL_TEST) ? 200 : 5;
475 ret = wait_for_completion_interruptible_timeout(&drvdata.send_cmd_complete,
476 msecs_to_jiffies(timeout));
477
478 if (ret == 0) /* timeout occurred */
479 ret = -EBUSY;
480
481 reinit_completion(&drvdata.send_cmd_complete);
482 return 0;
483 }
484
gamepad_property_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count,enum feature_status_index index)485 static ssize_t gamepad_property_store(struct device *dev,
486 struct device_attribute *attr,
487 const char *buf, size_t count,
488 enum feature_status_index index)
489 {
490 size_t size = 1;
491 u8 val = 0;
492 int ret;
493
494 switch (index) {
495 case FEATURE_GAMEPAD_MODE:
496 ret = sysfs_match_string(gamepad_mode_text, buf);
497 if (ret < 0)
498 return ret;
499 val = ret;
500 break;
501 case FEATURE_AUTO_SLEEP_TIME:
502 ret = kstrtou8(buf, 10, &val);
503 if (ret)
504 return ret;
505 break;
506 case FEATURE_IMU_ENABLE:
507 ret = sysfs_match_string(feature_enabled_text, buf);
508 if (ret < 0)
509 return ret;
510 val = ret;
511 break;
512 case FEATURE_IMU_BYPASS:
513 ret = sysfs_match_string(feature_enabled_text, buf);
514 if (ret < 0)
515 return ret;
516 val = ret;
517 break;
518 case FEATURE_RGB_ENABLE:
519 ret = sysfs_match_string(feature_enabled_text, buf);
520 if (ret < 0)
521 return ret;
522 val = ret;
523 break;
524 case FEATURE_TOUCHPAD_ENABLE:
525 ret = sysfs_match_string(feature_enabled_text, buf);
526 if (ret < 0)
527 return ret;
528 val = ret;
529 break;
530 case FEATURE_OS_MODE:
531 ret = sysfs_match_string(os_type_text, buf);
532 if (ret < 0)
533 return ret;
534 val = ret;
535 break;
536 case FEATURE_POLL_RATE:
537 ret = sysfs_match_string(poll_rate_text, buf);
538 if (ret < 0)
539 return ret;
540 val = ret;
541 break;
542 case FEATURE_DPAD_MODE:
543 ret = sysfs_match_string(dpad_mode_text, buf);
544 if (ret < 0)
545 return ret;
546 val = ret;
547 break;
548 case FEATURE_MOUSE_WHEEL_STEP:
549 ret = kstrtou8(buf, 10, &val);
550 if (ret)
551 return ret;
552 if (val < 1 || val > 127)
553 return -EINVAL;
554 break;
555 default:
556 return -EINVAL;
557 }
558
559 if (!val)
560 size = 0;
561
562 ret = mcu_property_out(drvdata.hdev, SET_GAMEPAD_CFG, index, &val,
563 size);
564 if (ret < 0)
565 return ret;
566
567 return count;
568 }
569
gamepad_property_show(struct device * dev,struct device_attribute * attr,char * buf,enum feature_status_index index)570 static ssize_t gamepad_property_show(struct device *dev,
571 struct device_attribute *attr, char *buf,
572 enum feature_status_index index)
573 {
574 ssize_t count = 0;
575 u8 i;
576
577 count = mcu_property_out(drvdata.hdev, GET_GAMEPAD_CFG, index, NULL, 0);
578 if (count < 0)
579 return count;
580
581 switch (index) {
582 case FEATURE_GAMEPAD_MODE:
583 i = drvdata.gp_mode;
584 if (i >= ARRAY_SIZE(gamepad_mode_text))
585 return -EINVAL;
586 count = sysfs_emit(buf, "%s\n", gamepad_mode_text[i]);
587 break;
588 case FEATURE_AUTO_SLEEP_TIME:
589 count = sysfs_emit(buf, "%u\n", drvdata.gp_auto_sleep_time);
590 break;
591 case FEATURE_IMU_ENABLE:
592 i = drvdata.imu_sensor_en;
593 if (i >= ARRAY_SIZE(feature_enabled_text))
594 return -EINVAL;
595 count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]);
596 break;
597 case FEATURE_IMU_BYPASS:
598 i = drvdata.imu_bypass_en;
599 if (i >= ARRAY_SIZE(feature_enabled_text))
600 return -EINVAL;
601 count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]);
602 break;
603 case FEATURE_RGB_ENABLE:
604 i = drvdata.rgb_en;
605 if (i >= ARRAY_SIZE(feature_enabled_text))
606 return -EINVAL;
607 count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]);
608 break;
609 case FEATURE_TOUCHPAD_ENABLE:
610 i = drvdata.tp_en;
611 if (i >= ARRAY_SIZE(feature_enabled_text))
612 return -EINVAL;
613 count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]);
614 break;
615 case FEATURE_OS_MODE:
616 i = drvdata.os_mode;
617 if (i >= ARRAY_SIZE(os_type_text))
618 return -EINVAL;
619 count = sysfs_emit(buf, "%s\n", os_type_text[i]);
620 break;
621 case FEATURE_POLL_RATE:
622 i = drvdata.gp_poll_rate;
623 if (i >= ARRAY_SIZE(poll_rate_text))
624 return -EINVAL;
625 count = sysfs_emit(buf, "%s\n", poll_rate_text[i]);
626 break;
627 case FEATURE_DPAD_MODE:
628 i = drvdata.gp_dpad_mode;
629 if (i >= ARRAY_SIZE(dpad_mode_text))
630 return -EINVAL;
631 count = sysfs_emit(buf, "%s\n", dpad_mode_text[i]);
632 break;
633 case FEATURE_MOUSE_WHEEL_STEP:
634 i = drvdata.mouse_step;
635 if (i < 1 || i > 127)
636 return -EINVAL;
637 count = sysfs_emit(buf, "%u\n", i);
638 break;
639 default:
640 return -EINVAL;
641 }
642
643 return count;
644 }
645
gamepad_property_options(struct device * dev,struct device_attribute * attr,char * buf,enum feature_status_index index)646 static ssize_t gamepad_property_options(struct device *dev,
647 struct device_attribute *attr,
648 char *buf,
649 enum feature_status_index index)
650 {
651 size_t count = 0;
652 unsigned int i;
653
654 switch (index) {
655 case FEATURE_GAMEPAD_MODE:
656 for (i = 0; i < ARRAY_SIZE(gamepad_mode_text); i++) {
657 count += sysfs_emit_at(buf, count, "%s ",
658 gamepad_mode_text[i]);
659 }
660 break;
661 case FEATURE_AUTO_SLEEP_TIME:
662 return sysfs_emit(buf, "0-255\n");
663 case FEATURE_IMU_ENABLE:
664 for (i = 0; i < ARRAY_SIZE(feature_enabled_text); i++) {
665 count += sysfs_emit_at(buf, count, "%s ",
666 feature_enabled_text[i]);
667 }
668 break;
669 case FEATURE_IMU_BYPASS:
670 case FEATURE_RGB_ENABLE:
671 case FEATURE_TOUCHPAD_ENABLE:
672 for (i = 0; i < ARRAY_SIZE(feature_enabled_text); i++) {
673 count += sysfs_emit_at(buf, count, "%s ",
674 feature_enabled_text[i]);
675 }
676 break;
677 case FEATURE_OS_MODE:
678 for (i = 0; i < ARRAY_SIZE(os_type_text); i++) {
679 count += sysfs_emit_at(buf, count, "%s ",
680 os_type_text[i]);
681 }
682 break;
683 case FEATURE_POLL_RATE:
684 for (i = 0; i < ARRAY_SIZE(poll_rate_text); i++) {
685 count += sysfs_emit_at(buf, count, "%s ",
686 poll_rate_text[i]);
687 }
688 break;
689 case FEATURE_DPAD_MODE:
690 for (i = 0; i < ARRAY_SIZE(dpad_mode_text); i++) {
691 count += sysfs_emit_at(buf, count, "%s ",
692 dpad_mode_text[i]);
693 }
694 break;
695 case FEATURE_MOUSE_WHEEL_STEP:
696 return sysfs_emit(buf, "1-127\n");
697 default:
698 return count;
699 }
700
701 if (count)
702 buf[count - 1] = '\n';
703
704 return count;
705 }
706
touchpad_property_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count,enum touchpad_config_index index)707 static ssize_t touchpad_property_store(struct device *dev,
708 struct device_attribute *attr,
709 const char *buf, size_t count,
710 enum touchpad_config_index index)
711 {
712 size_t size = 1;
713 u8 val = 0;
714 int ret;
715
716 switch (index) {
717 case CFG_WINDOWS_MODE:
718 ret = sysfs_match_string(touchpad_mode_text, buf);
719 if (ret < 0)
720 return ret;
721 val = ret;
722 break;
723 case CFG_LINUX_MODE:
724 ret = sysfs_match_string(touchpad_mode_text, buf);
725 if (ret < 0)
726 return ret;
727 val = ret;
728 break;
729 default:
730 return -EINVAL;
731 }
732 if (!val)
733 size = 0;
734
735 ret = mcu_property_out(drvdata.hdev, SET_TP_PARAM, index, &val, size);
736 if (ret < 0)
737 return ret;
738
739 return count;
740 }
741
touchpad_property_show(struct device * dev,struct device_attribute * attr,char * buf,enum touchpad_config_index index)742 static ssize_t touchpad_property_show(struct device *dev,
743 struct device_attribute *attr, char *buf,
744 enum touchpad_config_index index)
745 {
746 int ret = 0;
747 u8 i;
748
749 ret = mcu_property_out(drvdata.hdev, GET_TP_PARAM, index, NULL, 0);
750 if (ret < 0)
751 return ret;
752
753 switch (index) {
754 case CFG_WINDOWS_MODE:
755 i = drvdata.tp_windows_mode;
756 break;
757 case CFG_LINUX_MODE:
758 i = drvdata.tp_linux_mode;
759 break;
760 default:
761 return -EINVAL;
762 }
763
764 if (i >= ARRAY_SIZE(touchpad_mode_text))
765 return -EINVAL;
766
767 return sysfs_emit(buf, "%s\n", touchpad_mode_text[i]);
768 }
769
touchpad_property_options(struct device * dev,struct device_attribute * attr,char * buf,enum touchpad_config_index index)770 static ssize_t touchpad_property_options(struct device *dev,
771 struct device_attribute *attr,
772 char *buf,
773 enum touchpad_config_index index)
774 {
775 size_t count = 0;
776 unsigned int i;
777
778 switch (index) {
779 case CFG_WINDOWS_MODE:
780 case CFG_LINUX_MODE:
781 for (i = 0; i < ARRAY_SIZE(touchpad_mode_text); i++) {
782 count += sysfs_emit_at(buf, count, "%s ",
783 touchpad_mode_text[i]);
784 }
785 break;
786 default:
787 return count;
788 }
789
790 if (count)
791 buf[count - 1] = '\n';
792
793 return count;
794 }
795
test_property_show(struct device * dev,struct device_attribute * attr,char * buf,enum test_command_index index)796 static ssize_t test_property_show(struct device *dev,
797 struct device_attribute *attr, char *buf,
798 enum test_command_index index)
799 {
800 size_t count = 0;
801 u8 i;
802
803 switch (index) {
804 case TEST_TP_MFR:
805 i = drvdata.tp_manufacturer;
806 if (i >= ARRAY_SIZE(touchpad_manufacturer_text))
807 return -EINVAL;
808 count = sysfs_emit(buf, "%s\n", touchpad_manufacturer_text[i]);
809 break;
810 case TEST_IMU_MFR:
811 i = drvdata.imu_manufacturer;
812 if (i >= ARRAY_SIZE(imu_manufacturer_text))
813 return -EINVAL;
814 count = sysfs_emit(buf, "%s\n", imu_manufacturer_text[i]);
815 break;
816 case TEST_TP_VER:
817 count = sysfs_emit(buf, "%u\n", drvdata.tp_version);
818 break;
819 default:
820 count = -EINVAL;
821 break;
822 }
823
824 return count;
825 }
826
mcu_id_show(struct device * dev,struct device_attribute * attr,char * buf)827 static ssize_t mcu_id_show(struct device *dev, struct device_attribute *attr,
828 char *buf)
829 {
830 return sysfs_emit(buf, "%*phN\n", 12, &drvdata.mcu_id);
831 }
832
rgb_cfg_call(struct hid_device * hdev,enum mcu_command_index cmd,enum rgb_config_index index,u8 * val,size_t size)833 static int rgb_cfg_call(struct hid_device *hdev, enum mcu_command_index cmd,
834 enum rgb_config_index index, u8 *val, size_t size)
835 {
836 if (cmd != SET_RGB_CFG && cmd != GET_RGB_CFG)
837 return -EINVAL;
838
839 if (index < LIGHT_MODE_SEL || index > USR_LIGHT_PROFILE_3)
840 return -EINVAL;
841
842 return mcu_property_out(hdev, cmd, index, val, size);
843 }
844
rgb_attr_show(void)845 static int rgb_attr_show(void)
846 {
847 enum rgb_config_index index;
848
849 index = drvdata.rgb_profile + 2;
850
851 return rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, index, NULL, 0);
852 };
853
rgb_effect_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)854 static ssize_t rgb_effect_store(struct device *dev,
855 struct device_attribute *attr, const char *buf,
856 size_t count)
857 {
858 struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);
859 enum rgb_config_index index;
860 u8 effect;
861 int ret;
862
863 ret = sysfs_match_string(rgb_effect_text, buf);
864 if (ret < 0)
865 return ret;
866
867 effect = ret;
868 index = drvdata.rgb_profile + 2;
869 u8 rgb_profile[6] = { effect,
870 mc_cdev->subled_info[0].intensity,
871 mc_cdev->subled_info[1].intensity,
872 mc_cdev->subled_info[2].intensity,
873 drvdata.led_cdev->brightness,
874 drvdata.rgb_speed };
875
876 ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);
877 if (ret)
878 return ret;
879
880 drvdata.rgb_effect = effect;
881 return count;
882 };
883
rgb_effect_show(struct device * dev,struct device_attribute * attr,char * buf)884 static ssize_t rgb_effect_show(struct device *dev,
885 struct device_attribute *attr, char *buf)
886 {
887 int ret;
888
889 ret = rgb_attr_show();
890 if (ret)
891 return ret;
892
893 if (drvdata.rgb_effect >= ARRAY_SIZE(rgb_effect_text))
894 return -EINVAL;
895
896 return sysfs_emit(buf, "%s\n", rgb_effect_text[drvdata.rgb_effect]);
897 }
898
rgb_effect_index_show(struct device * dev,struct device_attribute * attr,char * buf)899 static ssize_t rgb_effect_index_show(struct device *dev,
900 struct device_attribute *attr, char *buf)
901 {
902 ssize_t count = 0;
903 unsigned int i;
904
905 for (i = 0; i < ARRAY_SIZE(rgb_effect_text); i++)
906 count += sysfs_emit_at(buf, count, "%s ", rgb_effect_text[i]);
907
908 if (count)
909 buf[count - 1] = '\n';
910
911 return count;
912 }
913
rgb_speed_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)914 static ssize_t rgb_speed_store(struct device *dev,
915 struct device_attribute *attr, const char *buf,
916 size_t count)
917 {
918 struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);
919 enum rgb_config_index index;
920 int val = 0;
921 int ret;
922
923 ret = kstrtoint(buf, 10, &val);
924 if (ret)
925 return ret;
926
927 if (val < 0 || val > 100)
928 return -EINVAL;
929
930 index = drvdata.rgb_profile + 2;
931 u8 rgb_profile[6] = { drvdata.rgb_effect,
932 mc_cdev->subled_info[0].intensity,
933 mc_cdev->subled_info[1].intensity,
934 mc_cdev->subled_info[2].intensity,
935 drvdata.led_cdev->brightness,
936 val };
937
938 ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);
939 if (ret)
940 return ret;
941
942 drvdata.rgb_speed = val;
943
944 return count;
945 };
946
rgb_speed_show(struct device * dev,struct device_attribute * attr,char * buf)947 static ssize_t rgb_speed_show(struct device *dev, struct device_attribute *attr,
948 char *buf)
949 {
950 int ret;
951
952 ret = rgb_attr_show();
953 if (ret)
954 return ret;
955
956 if (drvdata.rgb_speed > 100)
957 return -EINVAL;
958
959 return sysfs_emit(buf, "%hhu\n", drvdata.rgb_speed);
960 }
961
rgb_speed_range_show(struct device * dev,struct device_attribute * attr,char * buf)962 static ssize_t rgb_speed_range_show(struct device *dev,
963 struct device_attribute *attr, char *buf)
964 {
965 return sysfs_emit(buf, "0-100\n");
966 }
967
rgb_mode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)968 static ssize_t rgb_mode_store(struct device *dev, struct device_attribute *attr,
969 const char *buf, size_t count)
970 {
971 int ret;
972 u8 val;
973
974 ret = sysfs_match_string(rgb_mode_text, buf);
975 if (ret <= 0)
976 return ret;
977
978 val = ret;
979
980 ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_MODE_SEL, &val,
981 1);
982 if (ret)
983 return ret;
984
985 drvdata.rgb_mode = val;
986
987 return count;
988 };
989
rgb_mode_show(struct device * dev,struct device_attribute * attr,char * buf)990 static ssize_t rgb_mode_show(struct device *dev, struct device_attribute *attr,
991 char *buf)
992 {
993 int ret;
994
995 ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_MODE_SEL, NULL, 0);
996 if (ret)
997 return ret;
998
999 if (drvdata.rgb_mode >= ARRAY_SIZE(rgb_mode_text))
1000 return -EINVAL;
1001
1002 return sysfs_emit(buf, "%s\n", rgb_mode_text[drvdata.rgb_mode]);
1003 };
1004
rgb_mode_index_show(struct device * dev,struct device_attribute * attr,char * buf)1005 static ssize_t rgb_mode_index_show(struct device *dev,
1006 struct device_attribute *attr, char *buf)
1007 {
1008 ssize_t count = 0;
1009 unsigned int i;
1010
1011 for (i = 1; i < ARRAY_SIZE(rgb_mode_text); i++)
1012 count += sysfs_emit_at(buf, count, "%s ", rgb_mode_text[i]);
1013
1014 if (count)
1015 buf[count - 1] = '\n';
1016
1017 return count;
1018 }
1019
rgb_profile_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1020 static ssize_t rgb_profile_store(struct device *dev,
1021 struct device_attribute *attr, const char *buf,
1022 size_t count)
1023 {
1024 size_t size = 1;
1025 int ret;
1026 u8 val;
1027
1028 ret = kstrtou8(buf, 10, &val);
1029 if (ret < 0)
1030 return ret;
1031
1032 if (val < 1 || val > 3)
1033 return -EINVAL;
1034
1035 ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_PROFILE_SEL, &val, size);
1036 if (ret)
1037 return ret;
1038
1039 drvdata.rgb_profile = val;
1040
1041 return count;
1042 };
1043
rgb_profile_show(struct device * dev,struct device_attribute * attr,char * buf)1044 static ssize_t rgb_profile_show(struct device *dev,
1045 struct device_attribute *attr, char *buf)
1046 {
1047 int ret;
1048
1049 ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_PROFILE_SEL, NULL, 0);
1050 if (ret)
1051 return ret;
1052
1053 if (drvdata.rgb_profile < 1 || drvdata.rgb_profile > 3)
1054 return -EINVAL;
1055
1056 return sysfs_emit(buf, "%hhu\n", drvdata.rgb_profile);
1057 };
1058
rgb_profile_range_show(struct device * dev,struct device_attribute * attr,char * buf)1059 static ssize_t rgb_profile_range_show(struct device *dev,
1060 struct device_attribute *attr, char *buf)
1061 {
1062 return sysfs_emit(buf, "1-3\n");
1063 }
1064
hid_gos_brightness_set(struct led_classdev * led_cdev,enum led_brightness brightness)1065 static void hid_gos_brightness_set(struct led_classdev *led_cdev,
1066 enum led_brightness brightness)
1067 {
1068 struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);
1069 enum rgb_config_index index;
1070 int ret;
1071
1072 if (brightness > led_cdev->max_brightness) {
1073 dev_err(led_cdev->dev, "Invalid argument\n");
1074 return;
1075 }
1076
1077 index = drvdata.rgb_profile + 2;
1078 u8 rgb_profile[6] = { drvdata.rgb_effect,
1079 mc_cdev->subled_info[0].intensity,
1080 mc_cdev->subled_info[1].intensity,
1081 mc_cdev->subled_info[2].intensity,
1082 brightness,
1083 drvdata.rgb_speed };
1084
1085 ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);
1086 switch (ret) {
1087 case 0:
1088 led_cdev->brightness = brightness;
1089 break;
1090 case -ENODEV: /* during switch to IAP -ENODEV is expected */
1091 case -ENOSYS: /* during rmmod -ENOSYS is expected */
1092 dev_dbg(led_cdev->dev, "Failed to write RGB profile: %i\n",
1093 ret);
1094 break;
1095 default:
1096 dev_err(led_cdev->dev, "Failed to write RGB profile: %i\n",
1097 ret);
1098 }
1099 }
1100
1101 #define LEGOS_DEVICE_ATTR_RW(_name, _attrname, _rtype, _group) \
1102 static ssize_t _name##_store(struct device *dev, \
1103 struct device_attribute *attr, \
1104 const char *buf, size_t count) \
1105 { \
1106 return _group##_property_store(dev, attr, buf, count, \
1107 _name.index); \
1108 } \
1109 static ssize_t _name##_show(struct device *dev, \
1110 struct device_attribute *attr, char *buf) \
1111 { \
1112 return _group##_property_show(dev, attr, buf, _name.index); \
1113 } \
1114 static ssize_t _name##_##_rtype##_show( \
1115 struct device *dev, struct device_attribute *attr, char *buf) \
1116 { \
1117 return _group##_property_options(dev, attr, buf, _name.index); \
1118 } \
1119 static DEVICE_ATTR_RW_NAMED(_name, _attrname)
1120
1121 #define LEGOS_DEVICE_ATTR_RO(_name, _attrname, _group) \
1122 static ssize_t _name##_show(struct device *dev, \
1123 struct device_attribute *attr, char *buf) \
1124 { \
1125 return _group##_property_show(dev, attr, buf, _name.index); \
1126 } \
1127 static DEVICE_ATTR_RO_NAMED(_name, _attrname)
1128
1129 /* Gamepad */
1130 static struct gos_cfg_attr auto_sleep_time = { FEATURE_AUTO_SLEEP_TIME };
1131 LEGOS_DEVICE_ATTR_RW(auto_sleep_time, "auto_sleep_time", range, gamepad);
1132 static DEVICE_ATTR_RO(auto_sleep_time_range);
1133
1134 static struct gos_cfg_attr dpad_mode = { FEATURE_DPAD_MODE };
1135 LEGOS_DEVICE_ATTR_RW(dpad_mode, "dpad_mode", index, gamepad);
1136 static DEVICE_ATTR_RO(dpad_mode_index);
1137
1138 static struct gos_cfg_attr gamepad_mode = { FEATURE_GAMEPAD_MODE };
1139 LEGOS_DEVICE_ATTR_RW(gamepad_mode, "mode", index, gamepad);
1140 static DEVICE_ATTR_RO_NAMED(gamepad_mode_index, "mode_index");
1141
1142 static struct gos_cfg_attr gamepad_poll_rate = { FEATURE_POLL_RATE };
1143 LEGOS_DEVICE_ATTR_RW(gamepad_poll_rate, "poll_rate", index, gamepad);
1144 static DEVICE_ATTR_RO_NAMED(gamepad_poll_rate_index, "poll_rate_index");
1145
1146 static struct attribute *legos_gamepad_attrs[] = {
1147 &dev_attr_auto_sleep_time.attr,
1148 &dev_attr_auto_sleep_time_range.attr,
1149 &dev_attr_dpad_mode.attr,
1150 &dev_attr_dpad_mode_index.attr,
1151 &dev_attr_gamepad_mode.attr,
1152 &dev_attr_gamepad_mode_index.attr,
1153 &dev_attr_gamepad_poll_rate.attr,
1154 &dev_attr_gamepad_poll_rate_index.attr,
1155 NULL,
1156 };
1157
1158 static const struct attribute_group gamepad_attr_group = {
1159 .name = "gamepad",
1160 .attrs = legos_gamepad_attrs,
1161 };
1162
1163 /* IMU */
1164 static struct gos_cfg_attr imu_bypass_enabled = { FEATURE_IMU_BYPASS };
1165 LEGOS_DEVICE_ATTR_RW(imu_bypass_enabled, "bypass_enabled", index, gamepad);
1166 static DEVICE_ATTR_RO_NAMED(imu_bypass_enabled_index, "bypass_enabled_index");
1167
1168 static struct gos_cfg_attr imu_manufacturer = { TEST_IMU_MFR };
1169 LEGOS_DEVICE_ATTR_RO(imu_manufacturer, "manufacturer", test);
1170
1171 static struct gos_cfg_attr imu_sensor_enabled = { FEATURE_IMU_ENABLE };
1172 LEGOS_DEVICE_ATTR_RW(imu_sensor_enabled, "sensor_enabled", index, gamepad);
1173 static DEVICE_ATTR_RO_NAMED(imu_sensor_enabled_index, "sensor_enabled_index");
1174
1175 static struct attribute *legos_imu_attrs[] = {
1176 &dev_attr_imu_bypass_enabled.attr,
1177 &dev_attr_imu_bypass_enabled_index.attr,
1178 &dev_attr_imu_manufacturer.attr,
1179 &dev_attr_imu_sensor_enabled.attr,
1180 &dev_attr_imu_sensor_enabled_index.attr,
1181 NULL,
1182 };
1183
1184 static const struct attribute_group imu_attr_group = {
1185 .name = "imu",
1186 .attrs = legos_imu_attrs,
1187 };
1188
1189 /* MCU */
1190 static DEVICE_ATTR_RO(mcu_id);
1191
1192 static struct gos_cfg_attr os_mode = { FEATURE_OS_MODE };
1193 LEGOS_DEVICE_ATTR_RW(os_mode, "os_mode", index, gamepad);
1194 static DEVICE_ATTR_RO(os_mode_index);
1195
1196 static struct attribute *legos_mcu_attrs[] = {
1197 &dev_attr_mcu_id.attr,
1198 &dev_attr_os_mode.attr,
1199 &dev_attr_os_mode_index.attr,
1200 NULL,
1201 };
1202
1203 static const struct attribute_group mcu_attr_group = {
1204 .attrs = legos_mcu_attrs,
1205 };
1206
1207 /* Mouse */
1208 static struct gos_cfg_attr mouse_wheel_step = { FEATURE_MOUSE_WHEEL_STEP };
1209 LEGOS_DEVICE_ATTR_RW(mouse_wheel_step, "step", range, gamepad);
1210 static DEVICE_ATTR_RO_NAMED(mouse_wheel_step_range, "step_range");
1211
1212 static struct attribute *legos_mouse_attrs[] = {
1213 &dev_attr_mouse_wheel_step.attr,
1214 &dev_attr_mouse_wheel_step_range.attr,
1215 NULL,
1216 };
1217
1218 static const struct attribute_group mouse_attr_group = {
1219 .name = "mouse",
1220 .attrs = legos_mouse_attrs,
1221 };
1222
1223 /* Touchpad */
1224 static struct gos_cfg_attr touchpad_enabled = { FEATURE_TOUCHPAD_ENABLE };
1225 LEGOS_DEVICE_ATTR_RW(touchpad_enabled, "enabled", index, gamepad);
1226 static DEVICE_ATTR_RO_NAMED(touchpad_enabled_index, "enabled_index");
1227
1228 static struct gos_cfg_attr touchpad_linux_mode = { CFG_LINUX_MODE };
1229 LEGOS_DEVICE_ATTR_RW(touchpad_linux_mode, "linux_mode", index, touchpad);
1230 static DEVICE_ATTR_RO_NAMED(touchpad_linux_mode_index, "linux_mode_index");
1231
1232 static struct gos_cfg_attr touchpad_manufacturer = { TEST_TP_MFR };
1233 LEGOS_DEVICE_ATTR_RO(touchpad_manufacturer, "manufacturer", test);
1234
1235 static struct gos_cfg_attr touchpad_version = { TEST_TP_VER };
1236 LEGOS_DEVICE_ATTR_RO(touchpad_version, "version", test);
1237
1238 static struct gos_cfg_attr touchpad_windows_mode = { CFG_WINDOWS_MODE };
1239 LEGOS_DEVICE_ATTR_RW(touchpad_windows_mode, "windows_mode", index, touchpad);
1240 static DEVICE_ATTR_RO_NAMED(touchpad_windows_mode_index, "windows_mode_index");
1241
1242 static struct attribute *legos_touchpad_attrs[] = {
1243 &dev_attr_touchpad_enabled.attr,
1244 &dev_attr_touchpad_enabled_index.attr,
1245 &dev_attr_touchpad_linux_mode.attr,
1246 &dev_attr_touchpad_linux_mode_index.attr,
1247 &dev_attr_touchpad_manufacturer.attr,
1248 &dev_attr_touchpad_version.attr,
1249 &dev_attr_touchpad_windows_mode.attr,
1250 &dev_attr_touchpad_windows_mode_index.attr,
1251 NULL,
1252 };
1253
1254 static const struct attribute_group touchpad_attr_group = {
1255 .name = "touchpad",
1256 .attrs = legos_touchpad_attrs,
1257 };
1258
1259 static const struct attribute_group *top_level_attr_groups[] = {
1260 &gamepad_attr_group,
1261 &imu_attr_group,
1262 &mcu_attr_group,
1263 &mouse_attr_group,
1264 &touchpad_attr_group,
1265 NULL,
1266 };
1267
1268 /* RGB */
1269 static struct gos_cfg_attr rgb_enabled = { FEATURE_RGB_ENABLE };
1270 LEGOS_DEVICE_ATTR_RW(rgb_enabled, "enabled", index, gamepad);
1271 static DEVICE_ATTR_RO_NAMED(rgb_enabled_index, "enabled_index");
1272
1273 static DEVICE_ATTR_RW_NAMED(rgb_effect, "effect");
1274 static DEVICE_ATTR_RO_NAMED(rgb_effect_index, "effect_index");
1275 static DEVICE_ATTR_RW_NAMED(rgb_mode, "mode");
1276 static DEVICE_ATTR_RO_NAMED(rgb_mode_index, "mode_index");
1277 static DEVICE_ATTR_RW_NAMED(rgb_profile, "profile");
1278 static DEVICE_ATTR_RO_NAMED(rgb_profile_range, "profile_range");
1279 static DEVICE_ATTR_RW_NAMED(rgb_speed, "speed");
1280 static DEVICE_ATTR_RO_NAMED(rgb_speed_range, "speed_range");
1281
1282 static struct attribute *gos_rgb_attrs[] = {
1283 &dev_attr_rgb_enabled.attr,
1284 &dev_attr_rgb_enabled_index.attr,
1285 &dev_attr_rgb_effect.attr,
1286 &dev_attr_rgb_effect_index.attr,
1287 &dev_attr_rgb_mode.attr,
1288 &dev_attr_rgb_mode_index.attr,
1289 &dev_attr_rgb_profile.attr,
1290 &dev_attr_rgb_profile_range.attr,
1291 &dev_attr_rgb_speed.attr,
1292 &dev_attr_rgb_speed_range.attr,
1293 NULL,
1294 };
1295
1296 static struct attribute_group rgb_attr_group = {
1297 .attrs = gos_rgb_attrs,
1298 };
1299
1300 static struct mc_subled gos_rgb_subled_info[] = {
1301 {
1302 .color_index = LED_COLOR_ID_RED,
1303 .brightness = 0x50,
1304 .intensity = 0x24,
1305 .channel = 0x1,
1306 },
1307 {
1308 .color_index = LED_COLOR_ID_GREEN,
1309 .brightness = 0x50,
1310 .intensity = 0x22,
1311 .channel = 0x2,
1312 },
1313 {
1314 .color_index = LED_COLOR_ID_BLUE,
1315 .brightness = 0x50,
1316 .intensity = 0x99,
1317 .channel = 0x3,
1318 },
1319 };
1320
1321 static struct led_classdev_mc gos_cdev_rgb = {
1322 .led_cdev = {
1323 .name = "go_s:rgb:joystick_rings",
1324 .brightness = 0x50,
1325 .max_brightness = 0x64,
1326 .brightness_set = hid_gos_brightness_set,
1327 },
1328 .num_colors = ARRAY_SIZE(gos_rgb_subled_info),
1329 .subled_info = gos_rgb_subled_info,
1330 };
1331
cfg_setup(struct work_struct * work)1332 static void cfg_setup(struct work_struct *work)
1333 {
1334 int ret;
1335
1336 /* MCU */
1337 ret = mcu_property_out(drvdata.hdev, GET_MCU_ID, FEATURE_NONE, NULL, 0);
1338 if (ret) {
1339 dev_err(&drvdata.hdev->dev, "Failed to retrieve MCU ID: %i\n",
1340 ret);
1341 return;
1342 }
1343
1344 ret = mcu_property_out(drvdata.hdev, GET_VERSION, FEATURE_NONE, NULL, 0);
1345 if (ret) {
1346 dev_err(&drvdata.hdev->dev, "Failed to retrieve MCU Version: %i\n", ret);
1347 return;
1348 }
1349
1350 ret = mcu_property_out(drvdata.hdev, GET_PL_TEST, TEST_TP_MFR, NULL, 0);
1351 if (ret) {
1352 dev_err(&drvdata.hdev->dev,
1353 "Failed to retrieve Touchpad Manufacturer: %i\n", ret);
1354 return;
1355 }
1356
1357 ret = mcu_property_out(drvdata.hdev, GET_PL_TEST, TEST_TP_VER, NULL, 0);
1358 if (ret) {
1359 dev_err(&drvdata.hdev->dev,
1360 "Failed to retrieve Touchpad Firmware Version: %i\n", ret);
1361 return;
1362 }
1363
1364 ret = mcu_property_out(drvdata.hdev, GET_PL_TEST, TEST_IMU_MFR, NULL, 0);
1365 if (ret) {
1366 dev_err(&drvdata.hdev->dev,
1367 "Failed to retrieve IMU Manufacturer: %i\n", ret);
1368 return;
1369 }
1370
1371 ret = mcu_property_out(drvdata.hdev, GET_GAMEPAD_CFG, FEATURE_OS_MODE,
1372 NULL, 0);
1373 if (ret) {
1374 dev_err(&drvdata.hdev->dev,
1375 "Failed to retrieve OS Mode: %i\n", ret);
1376 return;
1377 }
1378 }
1379
hid_gos_cfg_probe(struct hid_device * hdev,const struct hid_device_id * _id)1380 static int hid_gos_cfg_probe(struct hid_device *hdev,
1381 const struct hid_device_id *_id)
1382 {
1383 int ret;
1384
1385 hid_set_drvdata(hdev, &drvdata);
1386 drvdata.hdev = hdev;
1387 mutex_init(&drvdata.cfg_mutex);
1388
1389 ret = sysfs_create_groups(&hdev->dev.kobj, top_level_attr_groups);
1390 if (ret) {
1391 dev_err_probe(&hdev->dev, ret,
1392 "Failed to create gamepad configuration attributes\n");
1393 return ret;
1394 }
1395
1396 ret = devm_led_classdev_multicolor_register(&hdev->dev, &gos_cdev_rgb);
1397 if (ret) {
1398 dev_err_probe(&hdev->dev, ret, "Failed to create RGB device\n");
1399 return ret;
1400 }
1401
1402 ret = devm_device_add_group(gos_cdev_rgb.led_cdev.dev, &rgb_attr_group);
1403 if (ret) {
1404 dev_err_probe(&hdev->dev, ret,
1405 "Failed to create RGB configuration attributes\n");
1406 return ret;
1407 }
1408
1409 drvdata.led_cdev = &gos_cdev_rgb.led_cdev;
1410
1411 init_completion(&drvdata.send_cmd_complete);
1412
1413 /* Executing calls prior to returning from probe will lock the MCU. Schedule
1414 * initial data call after probe has completed and MCU can accept calls.
1415 */
1416 INIT_DELAYED_WORK(&drvdata.gos_cfg_setup, &cfg_setup);
1417 ret = schedule_delayed_work(&drvdata.gos_cfg_setup, msecs_to_jiffies(2));
1418 if (!ret) {
1419 dev_err(&hdev->dev, "Failed to schedule startup delayed work\n");
1420 return -ENODEV;
1421 }
1422
1423 return 0;
1424 }
1425
hid_gos_cfg_remove(struct hid_device * hdev)1426 static void hid_gos_cfg_remove(struct hid_device *hdev)
1427 {
1428 guard(mutex)(&drvdata.cfg_mutex);
1429 cancel_delayed_work_sync(&drvdata.gos_cfg_setup);
1430 sysfs_remove_groups(&hdev->dev.kobj, top_level_attr_groups);
1431 hid_hw_close(hdev);
1432 hid_hw_stop(hdev);
1433 hid_set_drvdata(hdev, NULL);
1434 }
1435
hid_gos_cfg_reset_resume(struct hid_device * hdev)1436 static int hid_gos_cfg_reset_resume(struct hid_device *hdev)
1437 {
1438 u8 os_mode = drvdata.os_mode;
1439 int ret;
1440
1441 ret = mcu_property_out(drvdata.hdev, SET_GAMEPAD_CFG,
1442 FEATURE_OS_MODE, &os_mode, 1);
1443 if (ret < 0)
1444 return ret;
1445
1446 ret = mcu_property_out(drvdata.hdev, GET_GAMEPAD_CFG,
1447 FEATURE_OS_MODE, NULL, 0);
1448 if (ret < 0)
1449 return ret;
1450
1451 if (drvdata.os_mode != os_mode)
1452 return -ENODEV;
1453
1454 return 0;
1455 }
1456
hid_gos_probe(struct hid_device * hdev,const struct hid_device_id * id)1457 static int hid_gos_probe(struct hid_device *hdev,
1458 const struct hid_device_id *id)
1459 {
1460 int ret, ep;
1461
1462 if (!hid_is_usb(hdev))
1463 return -EINVAL;
1464
1465 ret = hid_parse(hdev);
1466 if (ret) {
1467 hid_err(hdev, "Parse failed\n");
1468 return ret;
1469 }
1470
1471 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
1472 if (ret) {
1473 hid_err(hdev, "Failed to start HID device\n");
1474 return ret;
1475 }
1476
1477 ret = hid_hw_open(hdev);
1478 if (ret) {
1479 hid_err(hdev, "Failed to open HID device\n");
1480 hid_hw_stop(hdev);
1481 return ret;
1482 }
1483
1484 ep = get_endpoint_address(hdev);
1485 if (ep != GO_S_CFG_INTF_IN) {
1486 dev_dbg(&hdev->dev, "Started interface %x as generic HID device.\n", ep);
1487 return 0;
1488 }
1489
1490 ret = hid_gos_cfg_probe(hdev, id);
1491 if (ret)
1492 dev_err_probe(&hdev->dev, ret, "Failed to start configuration interface");
1493
1494 dev_dbg(&hdev->dev, "Started interface %x as Go S configuration interface\n", ep);
1495 return ret;
1496 }
1497
hid_gos_remove(struct hid_device * hdev)1498 static void hid_gos_remove(struct hid_device *hdev)
1499 {
1500 int ep = get_endpoint_address(hdev);
1501
1502 switch (ep) {
1503 case GO_S_CFG_INTF_IN:
1504 hid_gos_cfg_remove(hdev);
1505 break;
1506 default:
1507 hid_hw_close(hdev);
1508 hid_hw_stop(hdev);
1509
1510 break;
1511 }
1512 }
1513
hid_gos_reset_resume(struct hid_device * hdev)1514 static int hid_gos_reset_resume(struct hid_device *hdev)
1515 {
1516 int ep = get_endpoint_address(hdev);
1517
1518 switch (ep) {
1519 case GO_S_CFG_INTF_IN:
1520 return hid_gos_cfg_reset_resume(hdev);
1521 default:
1522 break;
1523 }
1524
1525 return 0;
1526 }
1527
1528 static const struct hid_device_id hid_gos_devices[] = {
1529 { HID_USB_DEVICE(USB_VENDOR_ID_QHE,
1530 USB_DEVICE_ID_LENOVO_LEGION_GO_S_XINPUT) },
1531 { HID_USB_DEVICE(USB_VENDOR_ID_QHE,
1532 USB_DEVICE_ID_LENOVO_LEGION_GO_S_DINPUT) },
1533 {}
1534 };
1535
1536 MODULE_DEVICE_TABLE(hid, hid_gos_devices);
1537 static struct hid_driver hid_lenovo_go_s = {
1538 .name = "hid-lenovo-go-s",
1539 .id_table = hid_gos_devices,
1540 .probe = hid_gos_probe,
1541 .remove = hid_gos_remove,
1542 .raw_event = hid_gos_raw_event,
1543 .reset_resume = hid_gos_reset_resume,
1544 };
1545 module_hid_driver(hid_lenovo_go_s);
1546
1547 MODULE_AUTHOR("Derek J. Clark");
1548 MODULE_DESCRIPTION("HID Driver for Lenovo Legion Go S Series gamepad.");
1549 MODULE_LICENSE("GPL");
1550