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 if (intf) {
386 ep = intf->cur_altsetting->endpoint;
387 if (ep)
388 return ep->desc.bEndpointAddress;
389 }
390
391 return -ENODEV;
392 }
393
hid_gos_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)394 static int hid_gos_raw_event(struct hid_device *hdev, struct hid_report *report,
395 u8 *data, int size)
396 {
397 struct command_report *cmd_rep;
398 int ep, ret;
399
400 ep = get_endpoint_address(hdev);
401 if (ep != GO_S_CFG_INTF_IN)
402 return 0;
403
404 if (size != GO_S_PACKET_SIZE)
405 return -EINVAL;
406
407 cmd_rep = (struct command_report *)data;
408
409 switch (cmd_rep->cmd) {
410 case GET_VERSION:
411 ret = hid_gos_version_event(data);
412 break;
413 case GET_MCU_ID:
414 ret = hid_gos_mcu_id_event(cmd_rep);
415 break;
416 case GET_GAMEPAD_CFG:
417 ret = hid_gos_gamepad_cfg_event(cmd_rep);
418 break;
419 case GET_TP_PARAM:
420 ret = hid_gos_touchpad_event(cmd_rep);
421 break;
422 case GET_PL_TEST:
423 ret = hid_gos_pl_test_event(cmd_rep);
424 break;
425 case GET_RGB_CFG:
426 ret = hid_gos_light_event(cmd_rep);
427 break;
428 case SET_GAMEPAD_CFG:
429 case SET_RGB_CFG:
430 case SET_TP_PARAM:
431 ret = hid_gos_set_event_return(cmd_rep);
432 break;
433 default:
434 ret = -EINVAL;
435 break;
436 }
437 dev_dbg(&hdev->dev, "Rx data as raw input report: [%*ph]\n",
438 GO_S_PACKET_SIZE, data);
439
440 complete(&drvdata.send_cmd_complete);
441 return ret;
442 }
443
mcu_property_out(struct hid_device * hdev,u8 command,u8 index,u8 * data,size_t len)444 static int mcu_property_out(struct hid_device *hdev, u8 command, u8 index,
445 u8 *data, size_t len)
446 {
447 unsigned char *dmabuf __free(kfree) = NULL;
448 u8 header[] = { command, index };
449 size_t header_size = ARRAY_SIZE(header);
450 int timeout, ret;
451
452 if (header_size + len > GO_S_PACKET_SIZE)
453 return -EINVAL;
454
455 guard(mutex)(&drvdata.cfg_mutex);
456 /* We can't use a devm_alloc reusable buffer without side effects during suspend */
457 dmabuf = kzalloc(GO_S_PACKET_SIZE, GFP_KERNEL);
458 if (!dmabuf)
459 return -ENOMEM;
460
461 memcpy(dmabuf, header, header_size);
462 memcpy(dmabuf + header_size, data, len);
463
464 dev_dbg(&hdev->dev, "Send data as raw output report: [%*ph]\n",
465 GO_S_PACKET_SIZE, dmabuf);
466
467 ret = hid_hw_output_report(hdev, dmabuf, GO_S_PACKET_SIZE);
468 if (ret < 0)
469 return ret;
470
471 ret = ret == GO_S_PACKET_SIZE ? 0 : -EINVAL;
472 if (ret)
473 return ret;
474
475 /* PL_TEST commands can take longer because they go out to another device */
476 timeout = (command == GET_PL_TEST) ? 200 : 5;
477 ret = wait_for_completion_interruptible_timeout(&drvdata.send_cmd_complete,
478 msecs_to_jiffies(timeout));
479
480 if (ret == 0) /* timeout occurred */
481 ret = -EBUSY;
482
483 reinit_completion(&drvdata.send_cmd_complete);
484 return 0;
485 }
486
gamepad_property_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count,enum feature_status_index index)487 static ssize_t gamepad_property_store(struct device *dev,
488 struct device_attribute *attr,
489 const char *buf, size_t count,
490 enum feature_status_index index)
491 {
492 size_t size = 1;
493 u8 val = 0;
494 int ret;
495
496 switch (index) {
497 case FEATURE_GAMEPAD_MODE:
498 ret = sysfs_match_string(gamepad_mode_text, buf);
499 if (ret < 0)
500 return ret;
501 val = ret;
502 break;
503 case FEATURE_AUTO_SLEEP_TIME:
504 ret = kstrtou8(buf, 10, &val);
505 if (ret)
506 return ret;
507 break;
508 case FEATURE_IMU_ENABLE:
509 ret = sysfs_match_string(feature_enabled_text, buf);
510 if (ret < 0)
511 return ret;
512 val = ret;
513 break;
514 case FEATURE_IMU_BYPASS:
515 ret = sysfs_match_string(feature_enabled_text, buf);
516 if (ret < 0)
517 return ret;
518 val = ret;
519 break;
520 case FEATURE_RGB_ENABLE:
521 ret = sysfs_match_string(feature_enabled_text, buf);
522 if (ret < 0)
523 return ret;
524 val = ret;
525 break;
526 case FEATURE_TOUCHPAD_ENABLE:
527 ret = sysfs_match_string(feature_enabled_text, buf);
528 if (ret < 0)
529 return ret;
530 val = ret;
531 break;
532 case FEATURE_OS_MODE:
533 ret = sysfs_match_string(os_type_text, buf);
534 if (ret < 0)
535 return ret;
536 val = ret;
537 break;
538 case FEATURE_POLL_RATE:
539 ret = sysfs_match_string(poll_rate_text, buf);
540 if (ret < 0)
541 return ret;
542 val = ret;
543 break;
544 case FEATURE_DPAD_MODE:
545 ret = sysfs_match_string(dpad_mode_text, buf);
546 if (ret < 0)
547 return ret;
548 val = ret;
549 break;
550 case FEATURE_MOUSE_WHEEL_STEP:
551 ret = kstrtou8(buf, 10, &val);
552 if (ret)
553 return ret;
554 if (val < 1 || val > 127)
555 return -EINVAL;
556 break;
557 default:
558 return -EINVAL;
559 }
560
561 if (!val)
562 size = 0;
563
564 ret = mcu_property_out(drvdata.hdev, SET_GAMEPAD_CFG, index, &val,
565 size);
566 if (ret < 0)
567 return ret;
568
569 return count;
570 }
571
gamepad_property_show(struct device * dev,struct device_attribute * attr,char * buf,enum feature_status_index index)572 static ssize_t gamepad_property_show(struct device *dev,
573 struct device_attribute *attr, char *buf,
574 enum feature_status_index index)
575 {
576 ssize_t count = 0;
577 u8 i;
578
579 count = mcu_property_out(drvdata.hdev, GET_GAMEPAD_CFG, index, NULL, 0);
580 if (count < 0)
581 return count;
582
583 switch (index) {
584 case FEATURE_GAMEPAD_MODE:
585 i = drvdata.gp_mode;
586 if (i >= ARRAY_SIZE(gamepad_mode_text))
587 return -EINVAL;
588 count = sysfs_emit(buf, "%s\n", gamepad_mode_text[i]);
589 break;
590 case FEATURE_AUTO_SLEEP_TIME:
591 count = sysfs_emit(buf, "%u\n", drvdata.gp_auto_sleep_time);
592 break;
593 case FEATURE_IMU_ENABLE:
594 i = drvdata.imu_sensor_en;
595 if (i >= ARRAY_SIZE(feature_enabled_text))
596 return -EINVAL;
597 count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]);
598 break;
599 case FEATURE_IMU_BYPASS:
600 i = drvdata.imu_bypass_en;
601 if (i >= ARRAY_SIZE(feature_enabled_text))
602 return -EINVAL;
603 count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]);
604 break;
605 case FEATURE_RGB_ENABLE:
606 i = drvdata.rgb_en;
607 if (i >= ARRAY_SIZE(feature_enabled_text))
608 return -EINVAL;
609 count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]);
610 break;
611 case FEATURE_TOUCHPAD_ENABLE:
612 i = drvdata.tp_en;
613 if (i >= ARRAY_SIZE(feature_enabled_text))
614 return -EINVAL;
615 count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]);
616 break;
617 case FEATURE_OS_MODE:
618 i = drvdata.os_mode;
619 if (i >= ARRAY_SIZE(os_type_text))
620 return -EINVAL;
621 count = sysfs_emit(buf, "%s\n", os_type_text[i]);
622 break;
623 case FEATURE_POLL_RATE:
624 i = drvdata.gp_poll_rate;
625 if (i >= ARRAY_SIZE(poll_rate_text))
626 return -EINVAL;
627 count = sysfs_emit(buf, "%s\n", poll_rate_text[i]);
628 break;
629 case FEATURE_DPAD_MODE:
630 i = drvdata.gp_dpad_mode;
631 if (i >= ARRAY_SIZE(dpad_mode_text))
632 return -EINVAL;
633 count = sysfs_emit(buf, "%s\n", dpad_mode_text[i]);
634 break;
635 case FEATURE_MOUSE_WHEEL_STEP:
636 i = drvdata.mouse_step;
637 if (i < 1 || i > 127)
638 return -EINVAL;
639 count = sysfs_emit(buf, "%u\n", i);
640 break;
641 default:
642 return -EINVAL;
643 }
644
645 return count;
646 }
647
gamepad_property_options(struct device * dev,struct device_attribute * attr,char * buf,enum feature_status_index index)648 static ssize_t gamepad_property_options(struct device *dev,
649 struct device_attribute *attr,
650 char *buf,
651 enum feature_status_index index)
652 {
653 size_t count = 0;
654 unsigned int i;
655
656 switch (index) {
657 case FEATURE_GAMEPAD_MODE:
658 for (i = 0; i < ARRAY_SIZE(gamepad_mode_text); i++) {
659 count += sysfs_emit_at(buf, count, "%s ",
660 gamepad_mode_text[i]);
661 }
662 break;
663 case FEATURE_AUTO_SLEEP_TIME:
664 return sysfs_emit(buf, "0-255\n");
665 case FEATURE_IMU_ENABLE:
666 for (i = 0; i < ARRAY_SIZE(feature_enabled_text); i++) {
667 count += sysfs_emit_at(buf, count, "%s ",
668 feature_enabled_text[i]);
669 }
670 break;
671 case FEATURE_IMU_BYPASS:
672 case FEATURE_RGB_ENABLE:
673 case FEATURE_TOUCHPAD_ENABLE:
674 for (i = 0; i < ARRAY_SIZE(feature_enabled_text); i++) {
675 count += sysfs_emit_at(buf, count, "%s ",
676 feature_enabled_text[i]);
677 }
678 break;
679 case FEATURE_OS_MODE:
680 for (i = 0; i < ARRAY_SIZE(os_type_text); i++) {
681 count += sysfs_emit_at(buf, count, "%s ",
682 os_type_text[i]);
683 }
684 break;
685 case FEATURE_POLL_RATE:
686 for (i = 0; i < ARRAY_SIZE(poll_rate_text); i++) {
687 count += sysfs_emit_at(buf, count, "%s ",
688 poll_rate_text[i]);
689 }
690 break;
691 case FEATURE_DPAD_MODE:
692 for (i = 0; i < ARRAY_SIZE(dpad_mode_text); i++) {
693 count += sysfs_emit_at(buf, count, "%s ",
694 dpad_mode_text[i]);
695 }
696 break;
697 case FEATURE_MOUSE_WHEEL_STEP:
698 return sysfs_emit(buf, "1-127\n");
699 default:
700 return count;
701 }
702
703 if (count)
704 buf[count - 1] = '\n';
705
706 return count;
707 }
708
touchpad_property_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count,enum touchpad_config_index index)709 static ssize_t touchpad_property_store(struct device *dev,
710 struct device_attribute *attr,
711 const char *buf, size_t count,
712 enum touchpad_config_index index)
713 {
714 size_t size = 1;
715 u8 val = 0;
716 int ret;
717
718 switch (index) {
719 case CFG_WINDOWS_MODE:
720 ret = sysfs_match_string(touchpad_mode_text, buf);
721 if (ret < 0)
722 return ret;
723 val = ret;
724 break;
725 case CFG_LINUX_MODE:
726 ret = sysfs_match_string(touchpad_mode_text, buf);
727 if (ret < 0)
728 return ret;
729 val = ret;
730 break;
731 default:
732 return -EINVAL;
733 }
734 if (!val)
735 size = 0;
736
737 ret = mcu_property_out(drvdata.hdev, SET_TP_PARAM, index, &val, size);
738 if (ret < 0)
739 return ret;
740
741 return count;
742 }
743
touchpad_property_show(struct device * dev,struct device_attribute * attr,char * buf,enum touchpad_config_index index)744 static ssize_t touchpad_property_show(struct device *dev,
745 struct device_attribute *attr, char *buf,
746 enum touchpad_config_index index)
747 {
748 int ret = 0;
749 u8 i;
750
751 ret = mcu_property_out(drvdata.hdev, GET_TP_PARAM, index, NULL, 0);
752 if (ret < 0)
753 return ret;
754
755 switch (index) {
756 case CFG_WINDOWS_MODE:
757 i = drvdata.tp_windows_mode;
758 break;
759 case CFG_LINUX_MODE:
760 i = drvdata.tp_linux_mode;
761 break;
762 default:
763 return -EINVAL;
764 }
765
766 if (i >= ARRAY_SIZE(touchpad_mode_text))
767 return -EINVAL;
768
769 return sysfs_emit(buf, "%s\n", touchpad_mode_text[i]);
770 }
771
touchpad_property_options(struct device * dev,struct device_attribute * attr,char * buf,enum touchpad_config_index index)772 static ssize_t touchpad_property_options(struct device *dev,
773 struct device_attribute *attr,
774 char *buf,
775 enum touchpad_config_index index)
776 {
777 size_t count = 0;
778 unsigned int i;
779
780 switch (index) {
781 case CFG_WINDOWS_MODE:
782 case CFG_LINUX_MODE:
783 for (i = 0; i < ARRAY_SIZE(touchpad_mode_text); i++) {
784 count += sysfs_emit_at(buf, count, "%s ",
785 touchpad_mode_text[i]);
786 }
787 break;
788 default:
789 return count;
790 }
791
792 if (count)
793 buf[count - 1] = '\n';
794
795 return count;
796 }
797
test_property_show(struct device * dev,struct device_attribute * attr,char * buf,enum test_command_index index)798 static ssize_t test_property_show(struct device *dev,
799 struct device_attribute *attr, char *buf,
800 enum test_command_index index)
801 {
802 size_t count = 0;
803 u8 i;
804
805 switch (index) {
806 case TEST_TP_MFR:
807 i = drvdata.tp_manufacturer;
808 if (i >= ARRAY_SIZE(touchpad_manufacturer_text))
809 return -EINVAL;
810 count = sysfs_emit(buf, "%s\n", touchpad_manufacturer_text[i]);
811 break;
812 case TEST_IMU_MFR:
813 i = drvdata.imu_manufacturer;
814 if (i >= ARRAY_SIZE(imu_manufacturer_text))
815 return -EINVAL;
816 count = sysfs_emit(buf, "%s\n", imu_manufacturer_text[i]);
817 break;
818 case TEST_TP_VER:
819 count = sysfs_emit(buf, "%u\n", drvdata.tp_version);
820 break;
821 default:
822 count = -EINVAL;
823 break;
824 }
825
826 return count;
827 }
828
mcu_id_show(struct device * dev,struct device_attribute * attr,char * buf)829 static ssize_t mcu_id_show(struct device *dev, struct device_attribute *attr,
830 char *buf)
831 {
832 return sysfs_emit(buf, "%*phN\n", 12, &drvdata.mcu_id);
833 }
834
rgb_cfg_call(struct hid_device * hdev,enum mcu_command_index cmd,enum rgb_config_index index,u8 * val,size_t size)835 static int rgb_cfg_call(struct hid_device *hdev, enum mcu_command_index cmd,
836 enum rgb_config_index index, u8 *val, size_t size)
837 {
838 if (cmd != SET_RGB_CFG && cmd != GET_RGB_CFG)
839 return -EINVAL;
840
841 if (index < LIGHT_MODE_SEL || index > USR_LIGHT_PROFILE_3)
842 return -EINVAL;
843
844 return mcu_property_out(hdev, cmd, index, val, size);
845 }
846
rgb_attr_show(void)847 static int rgb_attr_show(void)
848 {
849 enum rgb_config_index index;
850
851 index = drvdata.rgb_profile + 2;
852
853 return rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, index, NULL, 0);
854 };
855
rgb_effect_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)856 static ssize_t rgb_effect_store(struct device *dev,
857 struct device_attribute *attr, const char *buf,
858 size_t count)
859 {
860 struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);
861 enum rgb_config_index index;
862 u8 effect;
863 int ret;
864
865 ret = sysfs_match_string(rgb_effect_text, buf);
866 if (ret < 0)
867 return ret;
868
869 effect = ret;
870 index = drvdata.rgb_profile + 2;
871 u8 rgb_profile[6] = { effect,
872 mc_cdev->subled_info[0].intensity,
873 mc_cdev->subled_info[1].intensity,
874 mc_cdev->subled_info[2].intensity,
875 drvdata.led_cdev->brightness,
876 drvdata.rgb_speed };
877
878 ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);
879 if (ret)
880 return ret;
881
882 drvdata.rgb_effect = effect;
883 return count;
884 };
885
rgb_effect_show(struct device * dev,struct device_attribute * attr,char * buf)886 static ssize_t rgb_effect_show(struct device *dev,
887 struct device_attribute *attr, char *buf)
888 {
889 int ret;
890
891 ret = rgb_attr_show();
892 if (ret)
893 return ret;
894
895 if (drvdata.rgb_effect >= ARRAY_SIZE(rgb_effect_text))
896 return -EINVAL;
897
898 return sysfs_emit(buf, "%s\n", rgb_effect_text[drvdata.rgb_effect]);
899 }
900
rgb_effect_index_show(struct device * dev,struct device_attribute * attr,char * buf)901 static ssize_t rgb_effect_index_show(struct device *dev,
902 struct device_attribute *attr, char *buf)
903 {
904 ssize_t count = 0;
905 unsigned int i;
906
907 for (i = 0; i < ARRAY_SIZE(rgb_effect_text); i++)
908 count += sysfs_emit_at(buf, count, "%s ", rgb_effect_text[i]);
909
910 if (count)
911 buf[count - 1] = '\n';
912
913 return count;
914 }
915
rgb_speed_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)916 static ssize_t rgb_speed_store(struct device *dev,
917 struct device_attribute *attr, const char *buf,
918 size_t count)
919 {
920 struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);
921 enum rgb_config_index index;
922 int val = 0;
923 int ret;
924
925 ret = kstrtoint(buf, 10, &val);
926 if (ret)
927 return ret;
928
929 if (val < 0 || val > 100)
930 return -EINVAL;
931
932 index = drvdata.rgb_profile + 2;
933 u8 rgb_profile[6] = { drvdata.rgb_effect,
934 mc_cdev->subled_info[0].intensity,
935 mc_cdev->subled_info[1].intensity,
936 mc_cdev->subled_info[2].intensity,
937 drvdata.led_cdev->brightness,
938 val };
939
940 ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);
941 if (ret)
942 return ret;
943
944 drvdata.rgb_speed = val;
945
946 return count;
947 };
948
rgb_speed_show(struct device * dev,struct device_attribute * attr,char * buf)949 static ssize_t rgb_speed_show(struct device *dev, struct device_attribute *attr,
950 char *buf)
951 {
952 int ret;
953
954 ret = rgb_attr_show();
955 if (ret)
956 return ret;
957
958 if (drvdata.rgb_speed > 100)
959 return -EINVAL;
960
961 return sysfs_emit(buf, "%hhu\n", drvdata.rgb_speed);
962 }
963
rgb_speed_range_show(struct device * dev,struct device_attribute * attr,char * buf)964 static ssize_t rgb_speed_range_show(struct device *dev,
965 struct device_attribute *attr, char *buf)
966 {
967 return sysfs_emit(buf, "0-100\n");
968 }
969
rgb_mode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)970 static ssize_t rgb_mode_store(struct device *dev, struct device_attribute *attr,
971 const char *buf, size_t count)
972 {
973 int ret;
974 u8 val;
975
976 ret = sysfs_match_string(rgb_mode_text, buf);
977 if (ret <= 0)
978 return ret;
979
980 val = ret;
981
982 ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_MODE_SEL, &val,
983 1);
984 if (ret)
985 return ret;
986
987 drvdata.rgb_mode = val;
988
989 return count;
990 };
991
rgb_mode_show(struct device * dev,struct device_attribute * attr,char * buf)992 static ssize_t rgb_mode_show(struct device *dev, struct device_attribute *attr,
993 char *buf)
994 {
995 int ret;
996
997 ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_MODE_SEL, NULL, 0);
998 if (ret)
999 return ret;
1000
1001 if (drvdata.rgb_mode >= ARRAY_SIZE(rgb_mode_text))
1002 return -EINVAL;
1003
1004 return sysfs_emit(buf, "%s\n", rgb_mode_text[drvdata.rgb_mode]);
1005 };
1006
rgb_mode_index_show(struct device * dev,struct device_attribute * attr,char * buf)1007 static ssize_t rgb_mode_index_show(struct device *dev,
1008 struct device_attribute *attr, char *buf)
1009 {
1010 ssize_t count = 0;
1011 unsigned int i;
1012
1013 for (i = 1; i < ARRAY_SIZE(rgb_mode_text); i++)
1014 count += sysfs_emit_at(buf, count, "%s ", rgb_mode_text[i]);
1015
1016 if (count)
1017 buf[count - 1] = '\n';
1018
1019 return count;
1020 }
1021
rgb_profile_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1022 static ssize_t rgb_profile_store(struct device *dev,
1023 struct device_attribute *attr, const char *buf,
1024 size_t count)
1025 {
1026 size_t size = 1;
1027 int ret;
1028 u8 val;
1029
1030 ret = kstrtou8(buf, 10, &val);
1031 if (ret < 0)
1032 return ret;
1033
1034 if (val < 1 || val > 3)
1035 return -EINVAL;
1036
1037 ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_PROFILE_SEL, &val, size);
1038 if (ret)
1039 return ret;
1040
1041 drvdata.rgb_profile = val;
1042
1043 return count;
1044 };
1045
rgb_profile_show(struct device * dev,struct device_attribute * attr,char * buf)1046 static ssize_t rgb_profile_show(struct device *dev,
1047 struct device_attribute *attr, char *buf)
1048 {
1049 int ret;
1050
1051 ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_PROFILE_SEL, NULL, 0);
1052 if (ret)
1053 return ret;
1054
1055 if (drvdata.rgb_profile < 1 || drvdata.rgb_profile > 3)
1056 return -EINVAL;
1057
1058 return sysfs_emit(buf, "%hhu\n", drvdata.rgb_profile);
1059 };
1060
rgb_profile_range_show(struct device * dev,struct device_attribute * attr,char * buf)1061 static ssize_t rgb_profile_range_show(struct device *dev,
1062 struct device_attribute *attr, char *buf)
1063 {
1064 return sysfs_emit(buf, "1-3\n");
1065 }
1066
hid_gos_brightness_set(struct led_classdev * led_cdev,enum led_brightness brightness)1067 static void hid_gos_brightness_set(struct led_classdev *led_cdev,
1068 enum led_brightness brightness)
1069 {
1070 struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);
1071 enum rgb_config_index index;
1072 int ret;
1073
1074 if (brightness > led_cdev->max_brightness) {
1075 dev_err(led_cdev->dev, "Invalid argument\n");
1076 return;
1077 }
1078
1079 index = drvdata.rgb_profile + 2;
1080 u8 rgb_profile[6] = { drvdata.rgb_effect,
1081 mc_cdev->subled_info[0].intensity,
1082 mc_cdev->subled_info[1].intensity,
1083 mc_cdev->subled_info[2].intensity,
1084 brightness,
1085 drvdata.rgb_speed };
1086
1087 ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);
1088 switch (ret) {
1089 case 0:
1090 led_cdev->brightness = brightness;
1091 break;
1092 case -ENODEV: /* during switch to IAP -ENODEV is expected */
1093 case -ENOSYS: /* during rmmod -ENOSYS is expected */
1094 dev_dbg(led_cdev->dev, "Failed to write RGB profile: %i\n",
1095 ret);
1096 break;
1097 default:
1098 dev_err(led_cdev->dev, "Failed to write RGB profile: %i\n",
1099 ret);
1100 }
1101 }
1102
1103 #define LEGOS_DEVICE_ATTR_RW(_name, _attrname, _rtype, _group) \
1104 static ssize_t _name##_store(struct device *dev, \
1105 struct device_attribute *attr, \
1106 const char *buf, size_t count) \
1107 { \
1108 return _group##_property_store(dev, attr, buf, count, \
1109 _name.index); \
1110 } \
1111 static ssize_t _name##_show(struct device *dev, \
1112 struct device_attribute *attr, char *buf) \
1113 { \
1114 return _group##_property_show(dev, attr, buf, _name.index); \
1115 } \
1116 static ssize_t _name##_##_rtype##_show( \
1117 struct device *dev, struct device_attribute *attr, char *buf) \
1118 { \
1119 return _group##_property_options(dev, attr, buf, _name.index); \
1120 } \
1121 static DEVICE_ATTR_RW_NAMED(_name, _attrname)
1122
1123 #define LEGOS_DEVICE_ATTR_RO(_name, _attrname, _group) \
1124 static ssize_t _name##_show(struct device *dev, \
1125 struct device_attribute *attr, char *buf) \
1126 { \
1127 return _group##_property_show(dev, attr, buf, _name.index); \
1128 } \
1129 static DEVICE_ATTR_RO_NAMED(_name, _attrname)
1130
1131 /* Gamepad */
1132 static struct gos_cfg_attr auto_sleep_time = { FEATURE_AUTO_SLEEP_TIME };
1133 LEGOS_DEVICE_ATTR_RW(auto_sleep_time, "auto_sleep_time", range, gamepad);
1134 static DEVICE_ATTR_RO(auto_sleep_time_range);
1135
1136 static struct gos_cfg_attr dpad_mode = { FEATURE_DPAD_MODE };
1137 LEGOS_DEVICE_ATTR_RW(dpad_mode, "dpad_mode", index, gamepad);
1138 static DEVICE_ATTR_RO(dpad_mode_index);
1139
1140 static struct gos_cfg_attr gamepad_mode = { FEATURE_GAMEPAD_MODE };
1141 LEGOS_DEVICE_ATTR_RW(gamepad_mode, "mode", index, gamepad);
1142 static DEVICE_ATTR_RO_NAMED(gamepad_mode_index, "mode_index");
1143
1144 static struct gos_cfg_attr gamepad_poll_rate = { FEATURE_POLL_RATE };
1145 LEGOS_DEVICE_ATTR_RW(gamepad_poll_rate, "poll_rate", index, gamepad);
1146 static DEVICE_ATTR_RO_NAMED(gamepad_poll_rate_index, "poll_rate_index");
1147
1148 static struct attribute *legos_gamepad_attrs[] = {
1149 &dev_attr_auto_sleep_time.attr,
1150 &dev_attr_auto_sleep_time_range.attr,
1151 &dev_attr_dpad_mode.attr,
1152 &dev_attr_dpad_mode_index.attr,
1153 &dev_attr_gamepad_mode.attr,
1154 &dev_attr_gamepad_mode_index.attr,
1155 &dev_attr_gamepad_poll_rate.attr,
1156 &dev_attr_gamepad_poll_rate_index.attr,
1157 NULL,
1158 };
1159
1160 static const struct attribute_group gamepad_attr_group = {
1161 .name = "gamepad",
1162 .attrs = legos_gamepad_attrs,
1163 };
1164
1165 /* IMU */
1166 static struct gos_cfg_attr imu_bypass_enabled = { FEATURE_IMU_BYPASS };
1167 LEGOS_DEVICE_ATTR_RW(imu_bypass_enabled, "bypass_enabled", index, gamepad);
1168 static DEVICE_ATTR_RO_NAMED(imu_bypass_enabled_index, "bypass_enabled_index");
1169
1170 static struct gos_cfg_attr imu_manufacturer = { TEST_IMU_MFR };
1171 LEGOS_DEVICE_ATTR_RO(imu_manufacturer, "manufacturer", test);
1172
1173 static struct gos_cfg_attr imu_sensor_enabled = { FEATURE_IMU_ENABLE };
1174 LEGOS_DEVICE_ATTR_RW(imu_sensor_enabled, "sensor_enabled", index, gamepad);
1175 static DEVICE_ATTR_RO_NAMED(imu_sensor_enabled_index, "sensor_enabled_index");
1176
1177 static struct attribute *legos_imu_attrs[] = {
1178 &dev_attr_imu_bypass_enabled.attr,
1179 &dev_attr_imu_bypass_enabled_index.attr,
1180 &dev_attr_imu_manufacturer.attr,
1181 &dev_attr_imu_sensor_enabled.attr,
1182 &dev_attr_imu_sensor_enabled_index.attr,
1183 NULL,
1184 };
1185
1186 static const struct attribute_group imu_attr_group = {
1187 .name = "imu",
1188 .attrs = legos_imu_attrs,
1189 };
1190
1191 /* MCU */
1192 static DEVICE_ATTR_RO(mcu_id);
1193
1194 static struct gos_cfg_attr os_mode = { FEATURE_OS_MODE };
1195 LEGOS_DEVICE_ATTR_RW(os_mode, "os_mode", index, gamepad);
1196 static DEVICE_ATTR_RO(os_mode_index);
1197
1198 static struct attribute *legos_mcu_attrs[] = {
1199 &dev_attr_mcu_id.attr,
1200 &dev_attr_os_mode.attr,
1201 &dev_attr_os_mode_index.attr,
1202 NULL,
1203 };
1204
1205 static const struct attribute_group mcu_attr_group = {
1206 .attrs = legos_mcu_attrs,
1207 };
1208
1209 /* Mouse */
1210 static struct gos_cfg_attr mouse_wheel_step = { FEATURE_MOUSE_WHEEL_STEP };
1211 LEGOS_DEVICE_ATTR_RW(mouse_wheel_step, "step", range, gamepad);
1212 static DEVICE_ATTR_RO_NAMED(mouse_wheel_step_range, "step_range");
1213
1214 static struct attribute *legos_mouse_attrs[] = {
1215 &dev_attr_mouse_wheel_step.attr,
1216 &dev_attr_mouse_wheel_step_range.attr,
1217 NULL,
1218 };
1219
1220 static const struct attribute_group mouse_attr_group = {
1221 .name = "mouse",
1222 .attrs = legos_mouse_attrs,
1223 };
1224
1225 /* Touchpad */
1226 static struct gos_cfg_attr touchpad_enabled = { FEATURE_TOUCHPAD_ENABLE };
1227 LEGOS_DEVICE_ATTR_RW(touchpad_enabled, "enabled", index, gamepad);
1228 static DEVICE_ATTR_RO_NAMED(touchpad_enabled_index, "enabled_index");
1229
1230 static struct gos_cfg_attr touchpad_linux_mode = { CFG_LINUX_MODE };
1231 LEGOS_DEVICE_ATTR_RW(touchpad_linux_mode, "linux_mode", index, touchpad);
1232 static DEVICE_ATTR_RO_NAMED(touchpad_linux_mode_index, "linux_mode_index");
1233
1234 static struct gos_cfg_attr touchpad_manufacturer = { TEST_TP_MFR };
1235 LEGOS_DEVICE_ATTR_RO(touchpad_manufacturer, "manufacturer", test);
1236
1237 static struct gos_cfg_attr touchpad_version = { TEST_TP_VER };
1238 LEGOS_DEVICE_ATTR_RO(touchpad_version, "version", test);
1239
1240 static struct gos_cfg_attr touchpad_windows_mode = { CFG_WINDOWS_MODE };
1241 LEGOS_DEVICE_ATTR_RW(touchpad_windows_mode, "windows_mode", index, touchpad);
1242 static DEVICE_ATTR_RO_NAMED(touchpad_windows_mode_index, "windows_mode_index");
1243
1244 static struct attribute *legos_touchpad_attrs[] = {
1245 &dev_attr_touchpad_enabled.attr,
1246 &dev_attr_touchpad_enabled_index.attr,
1247 &dev_attr_touchpad_linux_mode.attr,
1248 &dev_attr_touchpad_linux_mode_index.attr,
1249 &dev_attr_touchpad_manufacturer.attr,
1250 &dev_attr_touchpad_version.attr,
1251 &dev_attr_touchpad_windows_mode.attr,
1252 &dev_attr_touchpad_windows_mode_index.attr,
1253 NULL,
1254 };
1255
1256 static const struct attribute_group touchpad_attr_group = {
1257 .name = "touchpad",
1258 .attrs = legos_touchpad_attrs,
1259 };
1260
1261 static const struct attribute_group *top_level_attr_groups[] = {
1262 &gamepad_attr_group,
1263 &imu_attr_group,
1264 &mcu_attr_group,
1265 &mouse_attr_group,
1266 &touchpad_attr_group,
1267 NULL,
1268 };
1269
1270 /* RGB */
1271 static struct gos_cfg_attr rgb_enabled = { FEATURE_RGB_ENABLE };
1272 LEGOS_DEVICE_ATTR_RW(rgb_enabled, "enabled", index, gamepad);
1273 static DEVICE_ATTR_RO_NAMED(rgb_enabled_index, "enabled_index");
1274
1275 static DEVICE_ATTR_RW_NAMED(rgb_effect, "effect");
1276 static DEVICE_ATTR_RO_NAMED(rgb_effect_index, "effect_index");
1277 static DEVICE_ATTR_RW_NAMED(rgb_mode, "mode");
1278 static DEVICE_ATTR_RO_NAMED(rgb_mode_index, "mode_index");
1279 static DEVICE_ATTR_RW_NAMED(rgb_profile, "profile");
1280 static DEVICE_ATTR_RO_NAMED(rgb_profile_range, "profile_range");
1281 static DEVICE_ATTR_RW_NAMED(rgb_speed, "speed");
1282 static DEVICE_ATTR_RO_NAMED(rgb_speed_range, "speed_range");
1283
1284 static struct attribute *gos_rgb_attrs[] = {
1285 &dev_attr_rgb_enabled.attr,
1286 &dev_attr_rgb_enabled_index.attr,
1287 &dev_attr_rgb_effect.attr,
1288 &dev_attr_rgb_effect_index.attr,
1289 &dev_attr_rgb_mode.attr,
1290 &dev_attr_rgb_mode_index.attr,
1291 &dev_attr_rgb_profile.attr,
1292 &dev_attr_rgb_profile_range.attr,
1293 &dev_attr_rgb_speed.attr,
1294 &dev_attr_rgb_speed_range.attr,
1295 NULL,
1296 };
1297
1298 static struct attribute_group rgb_attr_group = {
1299 .attrs = gos_rgb_attrs,
1300 };
1301
1302 static struct mc_subled gos_rgb_subled_info[] = {
1303 {
1304 .color_index = LED_COLOR_ID_RED,
1305 .brightness = 0x50,
1306 .intensity = 0x24,
1307 .channel = 0x1,
1308 },
1309 {
1310 .color_index = LED_COLOR_ID_GREEN,
1311 .brightness = 0x50,
1312 .intensity = 0x22,
1313 .channel = 0x2,
1314 },
1315 {
1316 .color_index = LED_COLOR_ID_BLUE,
1317 .brightness = 0x50,
1318 .intensity = 0x99,
1319 .channel = 0x3,
1320 },
1321 };
1322
1323 static struct led_classdev_mc gos_cdev_rgb = {
1324 .led_cdev = {
1325 .name = "go_s:rgb:joystick_rings",
1326 .brightness = 0x50,
1327 .max_brightness = 0x64,
1328 .brightness_set = hid_gos_brightness_set,
1329 },
1330 .num_colors = ARRAY_SIZE(gos_rgb_subled_info),
1331 .subled_info = gos_rgb_subled_info,
1332 };
1333
cfg_setup(struct work_struct * work)1334 static void cfg_setup(struct work_struct *work)
1335 {
1336 int ret;
1337
1338 /* MCU */
1339 ret = mcu_property_out(drvdata.hdev, GET_MCU_ID, FEATURE_NONE, NULL, 0);
1340 if (ret) {
1341 dev_err(&drvdata.hdev->dev, "Failed to retrieve MCU ID: %i\n",
1342 ret);
1343 return;
1344 }
1345
1346 ret = mcu_property_out(drvdata.hdev, GET_VERSION, FEATURE_NONE, NULL, 0);
1347 if (ret) {
1348 dev_err(&drvdata.hdev->dev, "Failed to retrieve MCU Version: %i\n", ret);
1349 return;
1350 }
1351
1352 ret = mcu_property_out(drvdata.hdev, GET_PL_TEST, TEST_TP_MFR, NULL, 0);
1353 if (ret) {
1354 dev_err(&drvdata.hdev->dev,
1355 "Failed to retrieve Touchpad Manufacturer: %i\n", ret);
1356 return;
1357 }
1358
1359 ret = mcu_property_out(drvdata.hdev, GET_PL_TEST, TEST_TP_VER, NULL, 0);
1360 if (ret) {
1361 dev_err(&drvdata.hdev->dev,
1362 "Failed to retrieve Touchpad Firmware Version: %i\n", ret);
1363 return;
1364 }
1365
1366 ret = mcu_property_out(drvdata.hdev, GET_PL_TEST, TEST_IMU_MFR, NULL, 0);
1367 if (ret) {
1368 dev_err(&drvdata.hdev->dev,
1369 "Failed to retrieve IMU Manufacturer: %i\n", ret);
1370 return;
1371 }
1372
1373 ret = mcu_property_out(drvdata.hdev, GET_GAMEPAD_CFG, FEATURE_OS_MODE,
1374 NULL, 0);
1375 if (ret) {
1376 dev_err(&drvdata.hdev->dev,
1377 "Failed to retrieve OS Mode: %i\n", ret);
1378 return;
1379 }
1380 }
1381
hid_gos_cfg_probe(struct hid_device * hdev,const struct hid_device_id * _id)1382 static int hid_gos_cfg_probe(struct hid_device *hdev,
1383 const struct hid_device_id *_id)
1384 {
1385 int ret;
1386
1387 hid_set_drvdata(hdev, &drvdata);
1388 drvdata.hdev = hdev;
1389 mutex_init(&drvdata.cfg_mutex);
1390
1391 ret = sysfs_create_groups(&hdev->dev.kobj, top_level_attr_groups);
1392 if (ret) {
1393 dev_err_probe(&hdev->dev, ret,
1394 "Failed to create gamepad configuration attributes\n");
1395 return ret;
1396 }
1397
1398 ret = devm_led_classdev_multicolor_register(&hdev->dev, &gos_cdev_rgb);
1399 if (ret) {
1400 dev_err_probe(&hdev->dev, ret, "Failed to create RGB device\n");
1401 return ret;
1402 }
1403
1404 ret = devm_device_add_group(gos_cdev_rgb.led_cdev.dev, &rgb_attr_group);
1405 if (ret) {
1406 dev_err_probe(&hdev->dev, ret,
1407 "Failed to create RGB configuration attributes\n");
1408 return ret;
1409 }
1410
1411 drvdata.led_cdev = &gos_cdev_rgb.led_cdev;
1412
1413 init_completion(&drvdata.send_cmd_complete);
1414
1415 /* Executing calls prior to returning from probe will lock the MCU. Schedule
1416 * initial data call after probe has completed and MCU can accept calls.
1417 */
1418 INIT_DELAYED_WORK(&drvdata.gos_cfg_setup, &cfg_setup);
1419 ret = schedule_delayed_work(&drvdata.gos_cfg_setup, msecs_to_jiffies(2));
1420 if (!ret) {
1421 dev_err(&hdev->dev, "Failed to schedule startup delayed work\n");
1422 return -ENODEV;
1423 }
1424
1425 return 0;
1426 }
1427
hid_gos_cfg_remove(struct hid_device * hdev)1428 static void hid_gos_cfg_remove(struct hid_device *hdev)
1429 {
1430 guard(mutex)(&drvdata.cfg_mutex);
1431 cancel_delayed_work_sync(&drvdata.gos_cfg_setup);
1432 sysfs_remove_groups(&hdev->dev.kobj, top_level_attr_groups);
1433 hid_hw_close(hdev);
1434 hid_hw_stop(hdev);
1435 hid_set_drvdata(hdev, NULL);
1436 }
1437
hid_gos_cfg_reset_resume(struct hid_device * hdev)1438 static int hid_gos_cfg_reset_resume(struct hid_device *hdev)
1439 {
1440 u8 os_mode = drvdata.os_mode;
1441 int ret;
1442
1443 ret = mcu_property_out(drvdata.hdev, SET_GAMEPAD_CFG,
1444 FEATURE_OS_MODE, &os_mode, 1);
1445 if (ret < 0)
1446 return ret;
1447
1448 ret = mcu_property_out(drvdata.hdev, GET_GAMEPAD_CFG,
1449 FEATURE_OS_MODE, NULL, 0);
1450 if (ret < 0)
1451 return ret;
1452
1453 if (drvdata.os_mode != os_mode)
1454 return -ENODEV;
1455
1456 return 0;
1457 }
1458
hid_gos_probe(struct hid_device * hdev,const struct hid_device_id * id)1459 static int hid_gos_probe(struct hid_device *hdev,
1460 const struct hid_device_id *id)
1461 {
1462 int ret, ep;
1463
1464 ret = hid_parse(hdev);
1465 if (ret) {
1466 hid_err(hdev, "Parse failed\n");
1467 return ret;
1468 }
1469
1470 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
1471 if (ret) {
1472 hid_err(hdev, "Failed to start HID device\n");
1473 return ret;
1474 }
1475
1476 ret = hid_hw_open(hdev);
1477 if (ret) {
1478 hid_err(hdev, "Failed to open HID device\n");
1479 hid_hw_stop(hdev);
1480 return ret;
1481 }
1482
1483 ep = get_endpoint_address(hdev);
1484 if (ep != GO_S_CFG_INTF_IN) {
1485 dev_dbg(&hdev->dev, "Started interface %x as generic HID device.\n", ep);
1486 return 0;
1487 }
1488
1489 ret = hid_gos_cfg_probe(hdev, id);
1490 if (ret)
1491 dev_err_probe(&hdev->dev, ret, "Failed to start configuration interface");
1492
1493 dev_dbg(&hdev->dev, "Started interface %x as Go S configuration interface\n", ep);
1494 return ret;
1495 }
1496
hid_gos_remove(struct hid_device * hdev)1497 static void hid_gos_remove(struct hid_device *hdev)
1498 {
1499 int ep = get_endpoint_address(hdev);
1500
1501 switch (ep) {
1502 case GO_S_CFG_INTF_IN:
1503 hid_gos_cfg_remove(hdev);
1504 break;
1505 default:
1506 hid_hw_close(hdev);
1507 hid_hw_stop(hdev);
1508
1509 break;
1510 }
1511 }
1512
hid_gos_reset_resume(struct hid_device * hdev)1513 static int hid_gos_reset_resume(struct hid_device *hdev)
1514 {
1515 int ep = get_endpoint_address(hdev);
1516
1517 switch (ep) {
1518 case GO_S_CFG_INTF_IN:
1519 return hid_gos_cfg_reset_resume(hdev);
1520 default:
1521 break;
1522 }
1523
1524 return 0;
1525 }
1526
1527 static const struct hid_device_id hid_gos_devices[] = {
1528 { HID_USB_DEVICE(USB_VENDOR_ID_QHE,
1529 USB_DEVICE_ID_LENOVO_LEGION_GO_S_XINPUT) },
1530 { HID_USB_DEVICE(USB_VENDOR_ID_QHE,
1531 USB_DEVICE_ID_LENOVO_LEGION_GO_S_DINPUT) },
1532 {}
1533 };
1534
1535 MODULE_DEVICE_TABLE(hid, hid_gos_devices);
1536 static struct hid_driver hid_lenovo_go_s = {
1537 .name = "hid-lenovo-go-s",
1538 .id_table = hid_gos_devices,
1539 .probe = hid_gos_probe,
1540 .remove = hid_gos_remove,
1541 .raw_event = hid_gos_raw_event,
1542 .reset_resume = hid_gos_reset_resume,
1543 };
1544 module_hid_driver(hid_lenovo_go_s);
1545
1546 MODULE_AUTHOR("Derek J. Clark");
1547 MODULE_DESCRIPTION("HID Driver for Lenovo Legion Go S Series gamepad.");
1548 MODULE_LICENSE("GPL");
1549