xref: /linux/drivers/hid/hid-lenovo-go-s.c (revision 70eda68668d1476b459b64e69b8f36659fa9dfa8)
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