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