xref: /linux/drivers/hid/hid-lenovo-go.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
1d69ccfcbSDerek J. Clark // SPDX-License-Identifier: GPL-2.0-or-later
2d69ccfcbSDerek J. Clark /*
3d69ccfcbSDerek J. Clark  *  HID driver for Lenovo Legion Go series gamepads.
4d69ccfcbSDerek J. Clark  *
5d69ccfcbSDerek J. Clark  *  Copyright (c) 2026 Derek J. Clark <derekjohn.clark@gmail.com>
6d69ccfcbSDerek J. Clark  *  Copyright (c) 2026 Valve Corporation
7d69ccfcbSDerek J. Clark  */
8d69ccfcbSDerek J. Clark 
9d69ccfcbSDerek J. Clark #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10d69ccfcbSDerek J. Clark 
11d69ccfcbSDerek J. Clark #include <linux/array_size.h>
12d69ccfcbSDerek J. Clark #include <linux/cleanup.h>
13d69ccfcbSDerek J. Clark #include <linux/completion.h>
14d69ccfcbSDerek J. Clark #include <linux/delay.h>
15d69ccfcbSDerek J. Clark #include <linux/dev_printk.h>
16d69ccfcbSDerek J. Clark #include <linux/device.h>
17d69ccfcbSDerek J. Clark #include <linux/device/devres.h>
18d69ccfcbSDerek J. Clark #include <linux/hid.h>
19d69ccfcbSDerek J. Clark #include <linux/jiffies.h>
20d69ccfcbSDerek J. Clark #include <linux/kstrtox.h>
21325262faSDerek J. Clark #include <linux/led-class-multicolor.h>
22d69ccfcbSDerek J. Clark #include <linux/mutex.h>
23d69ccfcbSDerek J. Clark #include <linux/printk.h>
24d69ccfcbSDerek J. Clark #include <linux/sysfs.h>
25d69ccfcbSDerek J. Clark #include <linux/types.h>
26d69ccfcbSDerek J. Clark #include <linux/unaligned.h>
27d69ccfcbSDerek J. Clark #include <linux/usb.h>
28d69ccfcbSDerek J. Clark #include <linux/workqueue.h>
29d69ccfcbSDerek J. Clark #include <linux/workqueue_types.h>
30d69ccfcbSDerek J. Clark 
31d69ccfcbSDerek J. Clark #include "hid-ids.h"
32d69ccfcbSDerek J. Clark 
33d69ccfcbSDerek J. Clark #define GO_GP_INTF_IN		0x83
34d69ccfcbSDerek J. Clark #define GO_OUTPUT_REPORT_ID	0x05
35d69ccfcbSDerek J. Clark #define GO_GP_RESET_SUCCESS	0x01
36d69ccfcbSDerek J. Clark #define GO_PACKET_SIZE		64
37d69ccfcbSDerek J. Clark 
38d69ccfcbSDerek J. Clark static struct hid_go_cfg {
39d69ccfcbSDerek J. Clark 	struct delayed_work go_cfg_setup;
40d69ccfcbSDerek J. Clark 	struct completion send_cmd_complete;
41325262faSDerek J. Clark 	struct led_classdev *led_cdev;
42d69ccfcbSDerek J. Clark 	struct hid_device *hdev;
43d69ccfcbSDerek J. Clark 	struct mutex cfg_mutex; /*ensure single synchronous output report*/
4482cd9bc8SDerek J. Clark 	u8 fps_mode;
4582cd9bc8SDerek J. Clark 	u8 gp_left_auto_sleep_time;
46995887a1SDerek J. Clark 	u8 gp_left_gyro_cal_status;
47995887a1SDerek J. Clark 	u8 gp_left_joy_cal_status;
4896b20c1fSDerek J. Clark 	u8 gp_left_notify_en;
4996b20c1fSDerek J. Clark 	u8 gp_left_rumble_mode;
50995887a1SDerek J. Clark 	u8 gp_left_trigg_cal_status;
51d69ccfcbSDerek J. Clark 	u32 gp_left_version_firmware;
52d69ccfcbSDerek J. Clark 	u8 gp_left_version_gen;
53d69ccfcbSDerek J. Clark 	u32 gp_left_version_hardware;
54d69ccfcbSDerek J. Clark 	u32 gp_left_version_product;
55d69ccfcbSDerek J. Clark 	u32 gp_left_version_protocol;
5682cd9bc8SDerek J. Clark 	u8 gp_mode;
5782cd9bc8SDerek J. Clark 	u8 gp_right_auto_sleep_time;
58995887a1SDerek J. Clark 	u8 gp_right_gyro_cal_status;
59995887a1SDerek J. Clark 	u8 gp_right_joy_cal_status;
6096b20c1fSDerek J. Clark 	u8 gp_right_notify_en;
6196b20c1fSDerek J. Clark 	u8 gp_right_rumble_mode;
62995887a1SDerek J. Clark 	u8 gp_right_trigg_cal_status;
63d69ccfcbSDerek J. Clark 	u32 gp_right_version_firmware;
64d69ccfcbSDerek J. Clark 	u8 gp_right_version_gen;
65d69ccfcbSDerek J. Clark 	u32 gp_right_version_hardware;
66d69ccfcbSDerek J. Clark 	u32 gp_right_version_product;
67d69ccfcbSDerek J. Clark 	u32 gp_right_version_protocol;
6896b20c1fSDerek J. Clark 	u8 gp_rumble_intensity;
6982cd9bc8SDerek J. Clark 	u8 imu_left_bypass_en;
7082cd9bc8SDerek J. Clark 	u8 imu_left_sensor_en;
7182cd9bc8SDerek J. Clark 	u8 imu_right_bypass_en;
7282cd9bc8SDerek J. Clark 	u8 imu_right_sensor_en;
73d69ccfcbSDerek J. Clark 	u32 mcu_version_firmware;
74d69ccfcbSDerek J. Clark 	u8 mcu_version_gen;
75d69ccfcbSDerek J. Clark 	u32 mcu_version_hardware;
76d69ccfcbSDerek J. Clark 	u32 mcu_version_product;
77d69ccfcbSDerek J. Clark 	u32 mcu_version_protocol;
78f0bedee6SDerek J. Clark 	u32 mouse_dpi;
7951e4270cSDerek J. Clark 	u8 os_mode;
80325262faSDerek J. Clark 	u8 rgb_effect;
8182cd9bc8SDerek J. Clark 	u8 rgb_en;
82325262faSDerek J. Clark 	u8 rgb_mode;
83325262faSDerek J. Clark 	u8 rgb_profile;
84325262faSDerek J. Clark 	u8 rgb_speed;
8582cd9bc8SDerek J. Clark 	u8 tp_en;
8696b20c1fSDerek J. Clark 	u8 tp_vibration_en;
8796b20c1fSDerek J. Clark 	u8 tp_vibration_intensity;
88d69ccfcbSDerek J. Clark 	u32 tx_dongle_version_firmware;
89d69ccfcbSDerek J. Clark 	u8 tx_dongle_version_gen;
90d69ccfcbSDerek J. Clark 	u32 tx_dongle_version_hardware;
91d69ccfcbSDerek J. Clark 	u32 tx_dongle_version_product;
92d69ccfcbSDerek J. Clark 	u32 tx_dongle_version_protocol;
93d69ccfcbSDerek J. Clark } drvdata;
94d69ccfcbSDerek J. Clark 
95d69ccfcbSDerek J. Clark struct go_cfg_attr {
96d69ccfcbSDerek J. Clark 	u8 index;
97d69ccfcbSDerek J. Clark };
98d69ccfcbSDerek J. Clark 
99d69ccfcbSDerek J. Clark struct command_report {
100d69ccfcbSDerek J. Clark 	u8 report_id;
101d69ccfcbSDerek J. Clark 	u8 id;
102d69ccfcbSDerek J. Clark 	u8 cmd;
103d69ccfcbSDerek J. Clark 	u8 sub_cmd;
104d69ccfcbSDerek J. Clark 	u8 device_type;
105d69ccfcbSDerek J. Clark 	u8 data[59];
106d69ccfcbSDerek J. Clark } __packed;
107d69ccfcbSDerek J. Clark 
108d69ccfcbSDerek J. Clark enum command_id {
109d69ccfcbSDerek J. Clark 	MCU_CONFIG_DATA = 0x00,
110d69ccfcbSDerek J. Clark 	OS_MODE_DATA = 0x06,
111d69ccfcbSDerek J. Clark 	GAMEPAD_DATA = 0x3c,
112d69ccfcbSDerek J. Clark };
113d69ccfcbSDerek J. Clark 
114d69ccfcbSDerek J. Clark enum mcu_command_index {
115d69ccfcbSDerek J. Clark 	GET_VERSION_DATA = 0x02,
116d69ccfcbSDerek J. Clark 	GET_FEATURE_STATUS,
117d69ccfcbSDerek J. Clark 	SET_FEATURE_STATUS,
118d69ccfcbSDerek J. Clark 	GET_MOTOR_CFG,
119d69ccfcbSDerek J. Clark 	SET_MOTOR_CFG,
120d69ccfcbSDerek J. Clark 	GET_DPI_CFG,
121d69ccfcbSDerek J. Clark 	SET_DPI_CFG,
122d69ccfcbSDerek J. Clark 	SET_TRIGGER_CFG = 0x0a,
123d69ccfcbSDerek J. Clark 	SET_JOYSTICK_CFG = 0x0c,
124d69ccfcbSDerek J. Clark 	SET_GYRO_CFG = 0x0e,
125d69ccfcbSDerek J. Clark 	GET_RGB_CFG,
126d69ccfcbSDerek J. Clark 	SET_RGB_CFG,
127d69ccfcbSDerek J. Clark 	GET_DEVICE_STATUS = 0xa0,
128d69ccfcbSDerek J. Clark 
129d69ccfcbSDerek J. Clark };
130d69ccfcbSDerek J. Clark 
131d69ccfcbSDerek J. Clark enum dev_type {
132d69ccfcbSDerek J. Clark 	UNSPECIFIED,
133d69ccfcbSDerek J. Clark 	USB_MCU,
134d69ccfcbSDerek J. Clark 	TX_DONGLE,
135d69ccfcbSDerek J. Clark 	LEFT_CONTROLLER,
136d69ccfcbSDerek J. Clark 	RIGHT_CONTROLLER,
137d69ccfcbSDerek J. Clark };
138d69ccfcbSDerek J. Clark 
13982cd9bc8SDerek J. Clark enum enabled_status_index {
14082cd9bc8SDerek J. Clark 	FEATURE_UNKNOWN,
14182cd9bc8SDerek J. Clark 	FEATURE_ENABLED,
14282cd9bc8SDerek J. Clark 	FEATURE_DISABLED,
14382cd9bc8SDerek J. Clark };
14482cd9bc8SDerek J. Clark 
14582cd9bc8SDerek J. Clark static const char *const enabled_status_text[] = {
14682cd9bc8SDerek J. Clark 	[FEATURE_UNKNOWN] = "unknown",
14782cd9bc8SDerek J. Clark 	[FEATURE_ENABLED] = "true",
14882cd9bc8SDerek J. Clark 	[FEATURE_DISABLED] = "false",
14982cd9bc8SDerek J. Clark };
15082cd9bc8SDerek J. Clark 
151d69ccfcbSDerek J. Clark enum version_data_index {
152d69ccfcbSDerek J. Clark 	PRODUCT_VERSION = 0x02,
153d69ccfcbSDerek J. Clark 	PROTOCOL_VERSION,
154d69ccfcbSDerek J. Clark 	FIRMWARE_VERSION,
155d69ccfcbSDerek J. Clark 	HARDWARE_VERSION,
156d69ccfcbSDerek J. Clark 	HARDWARE_GENERATION,
157d69ccfcbSDerek J. Clark };
158d69ccfcbSDerek J. Clark 
15982cd9bc8SDerek J. Clark enum feature_status_index {
16082cd9bc8SDerek J. Clark 	FEATURE_RESET_GAMEPAD = 0x02,
16182cd9bc8SDerek J. Clark 	FEATURE_IMU_BYPASS,
16282cd9bc8SDerek J. Clark 	FEATURE_IMU_ENABLE = 0x05,
16382cd9bc8SDerek J. Clark 	FEATURE_TOUCHPAD_ENABLE = 0x07,
16482cd9bc8SDerek J. Clark 	FEATURE_LIGHT_ENABLE,
16582cd9bc8SDerek J. Clark 	FEATURE_AUTO_SLEEP_TIME,
16682cd9bc8SDerek J. Clark 	FEATURE_FPS_SWITCH_STATUS = 0x0b,
16782cd9bc8SDerek J. Clark 	FEATURE_GAMEPAD_MODE = 0x0e,
16882cd9bc8SDerek J. Clark };
16982cd9bc8SDerek J. Clark 
17051e4270cSDerek J. Clark #define FEATURE_OS_MODE 0x69
17151e4270cSDerek J. Clark 
17282cd9bc8SDerek J. Clark enum fps_switch_status_index {
17382cd9bc8SDerek J. Clark 	FPS_STATUS_UNKNOWN,
17482cd9bc8SDerek J. Clark 	GAMEPAD,
17582cd9bc8SDerek J. Clark 	FPS,
17682cd9bc8SDerek J. Clark };
17782cd9bc8SDerek J. Clark 
17882cd9bc8SDerek J. Clark static const char *const fps_switch_text[] = {
17982cd9bc8SDerek J. Clark 	[FPS_STATUS_UNKNOWN] = "unknown",
18082cd9bc8SDerek J. Clark 	[GAMEPAD] = "gamepad",
18182cd9bc8SDerek J. Clark 	[FPS] = "fps",
18282cd9bc8SDerek J. Clark };
18382cd9bc8SDerek J. Clark 
18482cd9bc8SDerek J. Clark enum gamepad_mode_index {
18582cd9bc8SDerek J. Clark 	GAMEPAD_MODE_UNKNOWN,
18682cd9bc8SDerek J. Clark 	XINPUT,
18782cd9bc8SDerek J. Clark 	DINPUT,
18882cd9bc8SDerek J. Clark };
18982cd9bc8SDerek J. Clark 
19082cd9bc8SDerek J. Clark static const char *const gamepad_mode_text[] = {
19182cd9bc8SDerek J. Clark 	[GAMEPAD_MODE_UNKNOWN] = "unknown",
19282cd9bc8SDerek J. Clark 	[XINPUT] = "xinput",
19382cd9bc8SDerek J. Clark 	[DINPUT] = "dinput",
19482cd9bc8SDerek J. Clark };
19582cd9bc8SDerek J. Clark 
19696b20c1fSDerek J. Clark enum motor_cfg_index {
19796b20c1fSDerek J. Clark 	MOTOR_CFG_ALL = 0x01,
19896b20c1fSDerek J. Clark 	MOTOR_INTENSITY,
19996b20c1fSDerek J. Clark 	VIBRATION_NOTIFY_ENABLE,
20096b20c1fSDerek J. Clark 	RUMBLE_MODE,
20196b20c1fSDerek J. Clark 	TP_VIBRATION_ENABLE,
20296b20c1fSDerek J. Clark 	TP_VIBRATION_INTENSITY,
20396b20c1fSDerek J. Clark };
20496b20c1fSDerek J. Clark 
20596b20c1fSDerek J. Clark enum intensity_index {
20696b20c1fSDerek J. Clark 	INTENSITY_UNKNOWN,
20796b20c1fSDerek J. Clark 	INTENSITY_OFF,
20896b20c1fSDerek J. Clark 	INTENSITY_LOW,
20996b20c1fSDerek J. Clark 	INTENSITY_MEDIUM,
21096b20c1fSDerek J. Clark 	INTENSITY_HIGH,
21196b20c1fSDerek J. Clark };
21296b20c1fSDerek J. Clark 
21396b20c1fSDerek J. Clark static const char *const intensity_text[] = {
21496b20c1fSDerek J. Clark 	[INTENSITY_UNKNOWN] = "unknown",
21596b20c1fSDerek J. Clark 	[INTENSITY_OFF] = "off",
21696b20c1fSDerek J. Clark 	[INTENSITY_LOW] = "low",
21796b20c1fSDerek J. Clark 	[INTENSITY_MEDIUM] = "medium",
21896b20c1fSDerek J. Clark 	[INTENSITY_HIGH] = "high",
21996b20c1fSDerek J. Clark };
22096b20c1fSDerek J. Clark 
22196b20c1fSDerek J. Clark enum rumble_mode_index {
22296b20c1fSDerek J. Clark 	RUMBLE_MODE_UNKNOWN,
22396b20c1fSDerek J. Clark 	RUMBLE_MODE_FPS,
22496b20c1fSDerek J. Clark 	RUMBLE_MODE_RACE,
22596b20c1fSDerek J. Clark 	RUMBLE_MODE_AVERAGE,
22696b20c1fSDerek J. Clark 	RUMBLE_MODE_SPG,
22796b20c1fSDerek J. Clark 	RUMBLE_MODE_RPG,
22896b20c1fSDerek J. Clark };
22996b20c1fSDerek J. Clark 
23096b20c1fSDerek J. Clark static const char *const rumble_mode_text[] = {
23196b20c1fSDerek J. Clark 	[RUMBLE_MODE_UNKNOWN] = "unknown",
23296b20c1fSDerek J. Clark 	[RUMBLE_MODE_FPS] = "fps",
23396b20c1fSDerek J. Clark 	[RUMBLE_MODE_RACE] = "racing",
23496b20c1fSDerek J. Clark 	[RUMBLE_MODE_AVERAGE] = "standard",
23596b20c1fSDerek J. Clark 	[RUMBLE_MODE_SPG] = "spg",
23696b20c1fSDerek J. Clark 	[RUMBLE_MODE_RPG] = "rpg",
23796b20c1fSDerek J. Clark };
23896b20c1fSDerek J. Clark 
239f0bedee6SDerek J. Clark #define FPS_MODE_DPI		0x02
240995887a1SDerek J. Clark #define TRIGGER_CALIBRATE	0x04
241995887a1SDerek J. Clark #define JOYSTICK_CALIBRATE	0x04
242995887a1SDerek J. Clark #define GYRO_CALIBRATE		0x06
243995887a1SDerek J. Clark 
244995887a1SDerek J. Clark enum cal_device_type {
245995887a1SDerek J. Clark 	CALDEV_GYROSCOPE = 0x01,
246995887a1SDerek J. Clark 	CALDEV_JOYSTICK,
247995887a1SDerek J. Clark 	CALDEV_TRIGGER,
248995887a1SDerek J. Clark 	CALDEV_JOY_TRIGGER,
249995887a1SDerek J. Clark };
250995887a1SDerek J. Clark 
251995887a1SDerek J. Clark enum cal_enable {
252995887a1SDerek J. Clark 	CAL_UNKNOWN,
253995887a1SDerek J. Clark 	CAL_START,
254995887a1SDerek J. Clark 	CAL_STOP,
255995887a1SDerek J. Clark };
256995887a1SDerek J. Clark 
257995887a1SDerek J. Clark static const char *const cal_enabled_text[] = {
258995887a1SDerek J. Clark 	[CAL_UNKNOWN] = "unknown",
259995887a1SDerek J. Clark 	[CAL_START] = "start",
260995887a1SDerek J. Clark 	[CAL_STOP] = "stop",
261995887a1SDerek J. Clark };
262995887a1SDerek J. Clark 
263995887a1SDerek J. Clark enum cal_status_index {
264995887a1SDerek J. Clark 	CAL_STAT_UNKNOWN,
265995887a1SDerek J. Clark 	CAL_STAT_SUCCESS,
266995887a1SDerek J. Clark 	CAL_STAT_FAILURE,
267995887a1SDerek J. Clark };
268995887a1SDerek J. Clark 
269995887a1SDerek J. Clark static const char *const cal_status_text[] = {
270995887a1SDerek J. Clark 	[CAL_STAT_UNKNOWN] = "unknown",
271995887a1SDerek J. Clark 	[CAL_STAT_SUCCESS] = "success",
272995887a1SDerek J. Clark 	[CAL_STAT_FAILURE] = "failure",
273995887a1SDerek J. Clark };
274f0bedee6SDerek J. Clark 
275325262faSDerek J. Clark enum rgb_config_index {
276325262faSDerek J. Clark 	LIGHT_CFG_ALL = 0x01,
277325262faSDerek J. Clark 	LIGHT_MODE_SEL,
278325262faSDerek J. Clark 	LIGHT_PROFILE_SEL,
279325262faSDerek J. Clark 	USR_LIGHT_PROFILE_1,
280325262faSDerek J. Clark 	USR_LIGHT_PROFILE_2,
281325262faSDerek J. Clark 	USR_LIGHT_PROFILE_3,
282325262faSDerek J. Clark };
283325262faSDerek J. Clark 
284325262faSDerek J. Clark enum rgb_mode_index {
285325262faSDerek J. Clark 	RGB_MODE_UNKNOWN,
286325262faSDerek J. Clark 	RGB_MODE_DYNAMIC,
287325262faSDerek J. Clark 	RGB_MODE_CUSTOM,
288325262faSDerek J. Clark };
289325262faSDerek J. Clark 
290325262faSDerek J. Clark static const char *const rgb_mode_text[] = {
291325262faSDerek J. Clark 	[RGB_MODE_UNKNOWN] = "unknown",
292325262faSDerek J. Clark 	[RGB_MODE_DYNAMIC] = "dynamic",
293325262faSDerek J. Clark 	[RGB_MODE_CUSTOM] = "custom",
294325262faSDerek J. Clark };
295325262faSDerek J. Clark 
296325262faSDerek J. Clark enum rgb_effect_index {
297325262faSDerek J. Clark 	RGB_EFFECT_MONO,
298325262faSDerek J. Clark 	RGB_EFFECT_BREATHE,
299325262faSDerek J. Clark 	RGB_EFFECT_CHROMA,
300325262faSDerek J. Clark 	RGB_EFFECT_RAINBOW,
301325262faSDerek J. Clark };
302325262faSDerek J. Clark 
303325262faSDerek J. Clark static const char *const rgb_effect_text[] = {
304325262faSDerek J. Clark 	[RGB_EFFECT_MONO] = "monocolor",
305325262faSDerek J. Clark 	[RGB_EFFECT_BREATHE] = "breathe",
306325262faSDerek J. Clark 	[RGB_EFFECT_CHROMA] = "chroma",
307325262faSDerek J. Clark 	[RGB_EFFECT_RAINBOW] = "rainbow",
308325262faSDerek J. Clark };
309325262faSDerek J. Clark 
310995887a1SDerek J. Clark enum device_status_index {
311995887a1SDerek J. Clark 	GET_CAL_STATUS = 0x02,
312995887a1SDerek J. Clark 	GET_UPGRADE_STATUS,
313995887a1SDerek J. Clark 	GET_MACRO_REC_STATUS,
314995887a1SDerek J. Clark 	GET_HOTKEY_TRIGG_STATUS,
315995887a1SDerek J. Clark };
316995887a1SDerek J. Clark 
31751e4270cSDerek J. Clark enum os_mode_cfg_index {
31851e4270cSDerek J. Clark 	SET_OS_MODE = 0x09,
31951e4270cSDerek J. Clark 	GET_OS_MODE,
32051e4270cSDerek J. Clark };
32151e4270cSDerek J. Clark 
32251e4270cSDerek J. Clark enum os_mode_type_index {
32351e4270cSDerek J. Clark 	OS_UNKNOWN,
32451e4270cSDerek J. Clark 	WINDOWS,
32551e4270cSDerek J. Clark 	LINUX,
32651e4270cSDerek J. Clark };
32751e4270cSDerek J. Clark 
32851e4270cSDerek J. Clark static const char *const os_mode_text[] = {
32951e4270cSDerek J. Clark 	[OS_UNKNOWN] = "unknown",
33051e4270cSDerek J. Clark 	[WINDOWS] = "windows",
33151e4270cSDerek J. Clark 	[LINUX] = "linux",
33251e4270cSDerek J. Clark };
33351e4270cSDerek J. Clark 
334d69ccfcbSDerek J. Clark static int hid_go_version_event(struct command_report *cmd_rep)
335d69ccfcbSDerek J. Clark {
336d69ccfcbSDerek J. Clark 	switch (cmd_rep->sub_cmd) {
337d69ccfcbSDerek J. Clark 	case PRODUCT_VERSION:
338d69ccfcbSDerek J. Clark 		switch (cmd_rep->device_type) {
339d69ccfcbSDerek J. Clark 		case USB_MCU:
340d69ccfcbSDerek J. Clark 			drvdata.mcu_version_product =
341d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
342d69ccfcbSDerek J. Clark 			return 0;
343d69ccfcbSDerek J. Clark 		case TX_DONGLE:
344d69ccfcbSDerek J. Clark 			drvdata.tx_dongle_version_product =
345d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
346d69ccfcbSDerek J. Clark 			return 0;
347d69ccfcbSDerek J. Clark 		case LEFT_CONTROLLER:
348d69ccfcbSDerek J. Clark 			drvdata.gp_left_version_product =
349d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
350d69ccfcbSDerek J. Clark 			return 0;
351d69ccfcbSDerek J. Clark 		case RIGHT_CONTROLLER:
352d69ccfcbSDerek J. Clark 			drvdata.gp_right_version_product =
353d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
354d69ccfcbSDerek J. Clark 			return 0;
355d69ccfcbSDerek J. Clark 		default:
356d69ccfcbSDerek J. Clark 			return -EINVAL;
357d69ccfcbSDerek J. Clark 		}
358d69ccfcbSDerek J. Clark 	case PROTOCOL_VERSION:
359d69ccfcbSDerek J. Clark 		switch (cmd_rep->device_type) {
360d69ccfcbSDerek J. Clark 		case USB_MCU:
361d69ccfcbSDerek J. Clark 			drvdata.mcu_version_protocol =
362d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
363d69ccfcbSDerek J. Clark 			return 0;
364d69ccfcbSDerek J. Clark 		case TX_DONGLE:
365d69ccfcbSDerek J. Clark 			drvdata.tx_dongle_version_protocol =
366d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
367d69ccfcbSDerek J. Clark 			return 0;
368d69ccfcbSDerek J. Clark 		case LEFT_CONTROLLER:
369d69ccfcbSDerek J. Clark 			drvdata.gp_left_version_protocol =
370d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
371d69ccfcbSDerek J. Clark 			return 0;
372d69ccfcbSDerek J. Clark 		case RIGHT_CONTROLLER:
373d69ccfcbSDerek J. Clark 			drvdata.gp_right_version_protocol =
374d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
375d69ccfcbSDerek J. Clark 			return 0;
376d69ccfcbSDerek J. Clark 		default:
377d69ccfcbSDerek J. Clark 			return -EINVAL;
378d69ccfcbSDerek J. Clark 		}
379d69ccfcbSDerek J. Clark 	case FIRMWARE_VERSION:
380d69ccfcbSDerek J. Clark 		switch (cmd_rep->device_type) {
381d69ccfcbSDerek J. Clark 		case USB_MCU:
382d69ccfcbSDerek J. Clark 			drvdata.mcu_version_firmware =
383d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
384d69ccfcbSDerek J. Clark 			return 0;
385d69ccfcbSDerek J. Clark 		case TX_DONGLE:
386d69ccfcbSDerek J. Clark 			drvdata.tx_dongle_version_firmware =
387d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
388d69ccfcbSDerek J. Clark 			return 0;
389d69ccfcbSDerek J. Clark 		case LEFT_CONTROLLER:
390d69ccfcbSDerek J. Clark 			drvdata.gp_left_version_firmware =
391d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
392d69ccfcbSDerek J. Clark 			return 0;
393d69ccfcbSDerek J. Clark 		case RIGHT_CONTROLLER:
394d69ccfcbSDerek J. Clark 			drvdata.gp_right_version_firmware =
395d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
396d69ccfcbSDerek J. Clark 			return 0;
397d69ccfcbSDerek J. Clark 		default:
398d69ccfcbSDerek J. Clark 			return -EINVAL;
399d69ccfcbSDerek J. Clark 		}
400d69ccfcbSDerek J. Clark 	case HARDWARE_VERSION:
401d69ccfcbSDerek J. Clark 		switch (cmd_rep->device_type) {
402d69ccfcbSDerek J. Clark 		case USB_MCU:
403d69ccfcbSDerek J. Clark 			drvdata.mcu_version_hardware =
404d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
405d69ccfcbSDerek J. Clark 			return 0;
406d69ccfcbSDerek J. Clark 		case TX_DONGLE:
407d69ccfcbSDerek J. Clark 			drvdata.tx_dongle_version_hardware =
408d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
409d69ccfcbSDerek J. Clark 			return 0;
410d69ccfcbSDerek J. Clark 		case LEFT_CONTROLLER:
411d69ccfcbSDerek J. Clark 			drvdata.gp_left_version_hardware =
412d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
413d69ccfcbSDerek J. Clark 			return 0;
414d69ccfcbSDerek J. Clark 		case RIGHT_CONTROLLER:
415d69ccfcbSDerek J. Clark 			drvdata.gp_right_version_hardware =
416d69ccfcbSDerek J. Clark 				get_unaligned_be32(cmd_rep->data);
417d69ccfcbSDerek J. Clark 			return 0;
418d69ccfcbSDerek J. Clark 		default:
419d69ccfcbSDerek J. Clark 			return -EINVAL;
420d69ccfcbSDerek J. Clark 		}
421d69ccfcbSDerek J. Clark 	case HARDWARE_GENERATION:
422d69ccfcbSDerek J. Clark 		switch (cmd_rep->device_type) {
423d69ccfcbSDerek J. Clark 		case USB_MCU:
424d69ccfcbSDerek J. Clark 			drvdata.mcu_version_gen = cmd_rep->data[0];
425d69ccfcbSDerek J. Clark 			return 0;
426d69ccfcbSDerek J. Clark 		case TX_DONGLE:
427d69ccfcbSDerek J. Clark 			drvdata.tx_dongle_version_gen = cmd_rep->data[0];
428d69ccfcbSDerek J. Clark 			return 0;
429d69ccfcbSDerek J. Clark 		case LEFT_CONTROLLER:
430d69ccfcbSDerek J. Clark 			drvdata.gp_left_version_gen = cmd_rep->data[0];
431d69ccfcbSDerek J. Clark 			return 0;
432d69ccfcbSDerek J. Clark 		case RIGHT_CONTROLLER:
433d69ccfcbSDerek J. Clark 			drvdata.gp_right_version_gen = cmd_rep->data[0];
434d69ccfcbSDerek J. Clark 			return 0;
435d69ccfcbSDerek J. Clark 		default:
436d69ccfcbSDerek J. Clark 			return -EINVAL;
437d69ccfcbSDerek J. Clark 		}
438d69ccfcbSDerek J. Clark 	default:
439d69ccfcbSDerek J. Clark 		return -EINVAL;
440d69ccfcbSDerek J. Clark 	}
441d69ccfcbSDerek J. Clark }
442d69ccfcbSDerek J. Clark 
44382cd9bc8SDerek J. Clark static int hid_go_feature_status_event(struct command_report *cmd_rep)
44482cd9bc8SDerek J. Clark {
44582cd9bc8SDerek J. Clark 	switch (cmd_rep->sub_cmd) {
44682cd9bc8SDerek J. Clark 	case FEATURE_RESET_GAMEPAD:
44782cd9bc8SDerek J. Clark 		return 0;
44882cd9bc8SDerek J. Clark 	case FEATURE_IMU_ENABLE:
44982cd9bc8SDerek J. Clark 		switch (cmd_rep->device_type) {
45082cd9bc8SDerek J. Clark 		case LEFT_CONTROLLER:
45182cd9bc8SDerek J. Clark 			drvdata.imu_left_sensor_en = cmd_rep->data[0];
45282cd9bc8SDerek J. Clark 			return 0;
45382cd9bc8SDerek J. Clark 		case RIGHT_CONTROLLER:
45482cd9bc8SDerek J. Clark 			drvdata.imu_right_sensor_en = cmd_rep->data[0];
45582cd9bc8SDerek J. Clark 			return 0;
45682cd9bc8SDerek J. Clark 		default:
45782cd9bc8SDerek J. Clark 			return -EINVAL;
458*54549af8SChen Ni 		}
45982cd9bc8SDerek J. Clark 	case FEATURE_IMU_BYPASS:
46082cd9bc8SDerek J. Clark 		switch (cmd_rep->device_type) {
46182cd9bc8SDerek J. Clark 		case LEFT_CONTROLLER:
46282cd9bc8SDerek J. Clark 			drvdata.imu_left_bypass_en = cmd_rep->data[0];
46382cd9bc8SDerek J. Clark 			return 0;
46482cd9bc8SDerek J. Clark 		case RIGHT_CONTROLLER:
46582cd9bc8SDerek J. Clark 			drvdata.imu_right_bypass_en = cmd_rep->data[0];
46682cd9bc8SDerek J. Clark 			return 0;
46782cd9bc8SDerek J. Clark 		default:
46882cd9bc8SDerek J. Clark 			return -EINVAL;
469*54549af8SChen Ni 		}
47082cd9bc8SDerek J. Clark 		break;
47182cd9bc8SDerek J. Clark 	case FEATURE_LIGHT_ENABLE:
47282cd9bc8SDerek J. Clark 		drvdata.rgb_en = cmd_rep->data[0];
47382cd9bc8SDerek J. Clark 		return 0;
47482cd9bc8SDerek J. Clark 	case FEATURE_AUTO_SLEEP_TIME:
47582cd9bc8SDerek J. Clark 		switch (cmd_rep->device_type) {
47682cd9bc8SDerek J. Clark 		case LEFT_CONTROLLER:
47782cd9bc8SDerek J. Clark 			drvdata.gp_left_auto_sleep_time = cmd_rep->data[0];
47882cd9bc8SDerek J. Clark 			return 0;
47982cd9bc8SDerek J. Clark 		case RIGHT_CONTROLLER:
48082cd9bc8SDerek J. Clark 			drvdata.gp_right_auto_sleep_time = cmd_rep->data[0];
48182cd9bc8SDerek J. Clark 			return 0;
48282cd9bc8SDerek J. Clark 		default:
48382cd9bc8SDerek J. Clark 			return -EINVAL;
484*54549af8SChen Ni 		}
48582cd9bc8SDerek J. Clark 		break;
48682cd9bc8SDerek J. Clark 	case FEATURE_TOUCHPAD_ENABLE:
48782cd9bc8SDerek J. Clark 		drvdata.tp_en = cmd_rep->data[0];
48882cd9bc8SDerek J. Clark 		return 0;
48982cd9bc8SDerek J. Clark 	case FEATURE_GAMEPAD_MODE:
49082cd9bc8SDerek J. Clark 		drvdata.gp_mode = cmd_rep->data[0];
49182cd9bc8SDerek J. Clark 		return 0;
49282cd9bc8SDerek J. Clark 	case FEATURE_FPS_SWITCH_STATUS:
49382cd9bc8SDerek J. Clark 		drvdata.fps_mode = cmd_rep->data[0];
49482cd9bc8SDerek J. Clark 		return 0;
49582cd9bc8SDerek J. Clark 	default:
49682cd9bc8SDerek J. Clark 		return -EINVAL;
49782cd9bc8SDerek J. Clark 	}
49882cd9bc8SDerek J. Clark }
49982cd9bc8SDerek J. Clark 
50096b20c1fSDerek J. Clark static int hid_go_motor_event(struct command_report *cmd_rep)
50196b20c1fSDerek J. Clark {
50296b20c1fSDerek J. Clark 	switch (cmd_rep->sub_cmd) {
50396b20c1fSDerek J. Clark 	case MOTOR_CFG_ALL:
50496b20c1fSDerek J. Clark 		return -EINVAL;
50596b20c1fSDerek J. Clark 	case MOTOR_INTENSITY:
50696b20c1fSDerek J. Clark 		drvdata.gp_rumble_intensity = cmd_rep->data[0];
50796b20c1fSDerek J. Clark 		return 0;
50896b20c1fSDerek J. Clark 	case VIBRATION_NOTIFY_ENABLE:
50996b20c1fSDerek J. Clark 		switch (cmd_rep->device_type) {
51096b20c1fSDerek J. Clark 		case LEFT_CONTROLLER:
51196b20c1fSDerek J. Clark 			drvdata.gp_left_notify_en = cmd_rep->data[0];
51296b20c1fSDerek J. Clark 			return 0;
51396b20c1fSDerek J. Clark 		case RIGHT_CONTROLLER:
51496b20c1fSDerek J. Clark 			drvdata.gp_right_notify_en = cmd_rep->data[0];
51596b20c1fSDerek J. Clark 			return 0;
51696b20c1fSDerek J. Clark 		default:
51796b20c1fSDerek J. Clark 			return -EINVAL;
518*54549af8SChen Ni 		}
51996b20c1fSDerek J. Clark 		break;
52096b20c1fSDerek J. Clark 	case RUMBLE_MODE:
52196b20c1fSDerek J. Clark 		switch (cmd_rep->device_type) {
52296b20c1fSDerek J. Clark 		case LEFT_CONTROLLER:
52396b20c1fSDerek J. Clark 			drvdata.gp_left_rumble_mode = cmd_rep->data[0];
52496b20c1fSDerek J. Clark 			return 0;
52596b20c1fSDerek J. Clark 		case RIGHT_CONTROLLER:
52696b20c1fSDerek J. Clark 			drvdata.gp_right_rumble_mode = cmd_rep->data[0];
52796b20c1fSDerek J. Clark 			return 0;
52896b20c1fSDerek J. Clark 		default:
52996b20c1fSDerek J. Clark 			return -EINVAL;
530*54549af8SChen Ni 		}
53196b20c1fSDerek J. Clark 	case TP_VIBRATION_ENABLE:
53296b20c1fSDerek J. Clark 		drvdata.tp_vibration_en = cmd_rep->data[0];
53396b20c1fSDerek J. Clark 		return 0;
53496b20c1fSDerek J. Clark 	case TP_VIBRATION_INTENSITY:
53596b20c1fSDerek J. Clark 		drvdata.tp_vibration_intensity = cmd_rep->data[0];
53696b20c1fSDerek J. Clark 		return 0;
53796b20c1fSDerek J. Clark 	}
53896b20c1fSDerek J. Clark 	return -EINVAL;
53996b20c1fSDerek J. Clark }
54096b20c1fSDerek J. Clark 
541f0bedee6SDerek J. Clark static int hid_go_fps_dpi_event(struct command_report *cmd_rep)
542f0bedee6SDerek J. Clark {
543f0bedee6SDerek J. Clark 	if (cmd_rep->sub_cmd != FPS_MODE_DPI)
544f0bedee6SDerek J. Clark 		return -EINVAL;
545f0bedee6SDerek J. Clark 
546f0bedee6SDerek J. Clark 	drvdata.mouse_dpi = get_unaligned_le32(cmd_rep->data);
547f0bedee6SDerek J. Clark 
548f0bedee6SDerek J. Clark 	return 0;
549f0bedee6SDerek J. Clark }
550f0bedee6SDerek J. Clark 
551325262faSDerek J. Clark static int hid_go_light_event(struct command_report *cmd_rep)
552325262faSDerek J. Clark {
553325262faSDerek J. Clark 	struct led_classdev_mc *mc_cdev;
554325262faSDerek J. Clark 
555325262faSDerek J. Clark 	switch (cmd_rep->sub_cmd) {
556325262faSDerek J. Clark 	case LIGHT_MODE_SEL:
557325262faSDerek J. Clark 		drvdata.rgb_mode = cmd_rep->data[0];
558325262faSDerek J. Clark 		return 0;
559325262faSDerek J. Clark 	case LIGHT_PROFILE_SEL:
560325262faSDerek J. Clark 		drvdata.rgb_profile = cmd_rep->data[0];
561325262faSDerek J. Clark 		return 0;
562325262faSDerek J. Clark 	case USR_LIGHT_PROFILE_1:
563325262faSDerek J. Clark 	case USR_LIGHT_PROFILE_2:
564325262faSDerek J. Clark 	case USR_LIGHT_PROFILE_3:
565325262faSDerek J. Clark 		mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);
566325262faSDerek J. Clark 		drvdata.rgb_effect = cmd_rep->data[0];
567325262faSDerek J. Clark 		mc_cdev->subled_info[0].intensity = cmd_rep->data[1];
568325262faSDerek J. Clark 		mc_cdev->subled_info[1].intensity = cmd_rep->data[2];
569325262faSDerek J. Clark 		mc_cdev->subled_info[2].intensity = cmd_rep->data[3];
570325262faSDerek J. Clark 		drvdata.led_cdev->brightness = cmd_rep->data[4];
571325262faSDerek J. Clark 		drvdata.rgb_speed = 100 - cmd_rep->data[5];
572325262faSDerek J. Clark 		return 0;
573325262faSDerek J. Clark 	default:
574325262faSDerek J. Clark 		return -EINVAL;
575325262faSDerek J. Clark 	}
576325262faSDerek J. Clark }
577325262faSDerek J. Clark 
578995887a1SDerek J. Clark static int hid_go_device_status_event(struct command_report *cmd_rep)
579995887a1SDerek J. Clark {
580995887a1SDerek J. Clark 	switch (cmd_rep->device_type) {
581995887a1SDerek J. Clark 	case LEFT_CONTROLLER:
582995887a1SDerek J. Clark 		switch (cmd_rep->data[0]) {
583995887a1SDerek J. Clark 		case CALDEV_GYROSCOPE:
584995887a1SDerek J. Clark 			drvdata.gp_left_gyro_cal_status = cmd_rep->data[1];
585995887a1SDerek J. Clark 			return 0;
586995887a1SDerek J. Clark 		case CALDEV_JOYSTICK:
587995887a1SDerek J. Clark 			drvdata.gp_left_joy_cal_status = cmd_rep->data[1];
588995887a1SDerek J. Clark 			return 0;
589995887a1SDerek J. Clark 		case CALDEV_TRIGGER:
590995887a1SDerek J. Clark 			drvdata.gp_left_trigg_cal_status = cmd_rep->data[1];
591995887a1SDerek J. Clark 			return 0;
592995887a1SDerek J. Clark 		default:
593995887a1SDerek J. Clark 			return -EINVAL;
594995887a1SDerek J. Clark 		}
595995887a1SDerek J. Clark 		break;
596995887a1SDerek J. Clark 	case RIGHT_CONTROLLER:
597995887a1SDerek J. Clark 		switch (cmd_rep->data[0]) {
598995887a1SDerek J. Clark 		case CALDEV_GYROSCOPE:
599995887a1SDerek J. Clark 			drvdata.gp_right_gyro_cal_status = cmd_rep->data[1];
600995887a1SDerek J. Clark 			return 0;
601995887a1SDerek J. Clark 		case CALDEV_JOYSTICK:
602995887a1SDerek J. Clark 			drvdata.gp_right_joy_cal_status = cmd_rep->data[1];
603995887a1SDerek J. Clark 			return 0;
604995887a1SDerek J. Clark 		case CALDEV_TRIGGER:
605995887a1SDerek J. Clark 			drvdata.gp_right_trigg_cal_status = cmd_rep->data[1];
606995887a1SDerek J. Clark 			return 0;
607995887a1SDerek J. Clark 		default:
608995887a1SDerek J. Clark 			return -EINVAL;
609995887a1SDerek J. Clark 		}
610995887a1SDerek J. Clark 		break;
611995887a1SDerek J. Clark 	default:
612995887a1SDerek J. Clark 		return -EINVAL;
613995887a1SDerek J. Clark 	}
614995887a1SDerek J. Clark }
615995887a1SDerek J. Clark 
61651e4270cSDerek J. Clark static int hid_go_os_mode_cfg_event(struct command_report *cmd_rep)
61751e4270cSDerek J. Clark {
61851e4270cSDerek J. Clark 	switch (cmd_rep->sub_cmd) {
61951e4270cSDerek J. Clark 	case SET_OS_MODE:
62051e4270cSDerek J. Clark 		if (cmd_rep->data[0] != 1)
62151e4270cSDerek J. Clark 			return -EIO;
62251e4270cSDerek J. Clark 		return 0;
62351e4270cSDerek J. Clark 	case GET_OS_MODE:
62451e4270cSDerek J. Clark 		drvdata.os_mode = cmd_rep->data[0];
62551e4270cSDerek J. Clark 		return 0;
62651e4270cSDerek J. Clark 	default:
62751e4270cSDerek J. Clark 		return -EINVAL;
628*54549af8SChen Ni 	}
62951e4270cSDerek J. Clark }
63051e4270cSDerek J. Clark 
63182cd9bc8SDerek J. Clark static int hid_go_set_event_return(struct command_report *cmd_rep)
63282cd9bc8SDerek J. Clark {
63382cd9bc8SDerek J. Clark 	if (cmd_rep->data[0] != 0)
63482cd9bc8SDerek J. Clark 		return -EIO;
63582cd9bc8SDerek J. Clark 
63682cd9bc8SDerek J. Clark 	return 0;
63782cd9bc8SDerek J. Clark }
63882cd9bc8SDerek J. Clark 
639d69ccfcbSDerek J. Clark static int get_endpoint_address(struct hid_device *hdev)
640d69ccfcbSDerek J. Clark {
641d69ccfcbSDerek J. Clark 	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
642d69ccfcbSDerek J. Clark 	struct usb_host_endpoint *ep;
643d69ccfcbSDerek J. Clark 
644d69ccfcbSDerek J. Clark 	if (!intf)
645d69ccfcbSDerek J. Clark 		return -ENODEV;
646d69ccfcbSDerek J. Clark 
647d69ccfcbSDerek J. Clark 	ep = intf->cur_altsetting->endpoint;
648d69ccfcbSDerek J. Clark 	if (!ep)
649d69ccfcbSDerek J. Clark 		return -ENODEV;
650d69ccfcbSDerek J. Clark 
651d69ccfcbSDerek J. Clark 	return ep->desc.bEndpointAddress;
652d69ccfcbSDerek J. Clark }
653d69ccfcbSDerek J. Clark 
654d69ccfcbSDerek J. Clark static int hid_go_raw_event(struct hid_device *hdev, struct hid_report *report,
655d69ccfcbSDerek J. Clark 			    u8 *data, int size)
656d69ccfcbSDerek J. Clark {
657d69ccfcbSDerek J. Clark 	struct command_report *cmd_rep;
658d69ccfcbSDerek J. Clark 	int ep, ret;
659d69ccfcbSDerek J. Clark 
660d69ccfcbSDerek J. Clark 	if (size != GO_PACKET_SIZE)
661d69ccfcbSDerek J. Clark 		goto passthrough;
662d69ccfcbSDerek J. Clark 
663d69ccfcbSDerek J. Clark 	ep = get_endpoint_address(hdev);
664d69ccfcbSDerek J. Clark 	if (ep != GO_GP_INTF_IN)
665d69ccfcbSDerek J. Clark 		goto passthrough;
666d69ccfcbSDerek J. Clark 
667d69ccfcbSDerek J. Clark 	cmd_rep = (struct command_report *)data;
668d69ccfcbSDerek J. Clark 
669d69ccfcbSDerek J. Clark 	switch (cmd_rep->id) {
670d69ccfcbSDerek J. Clark 	case MCU_CONFIG_DATA:
671d69ccfcbSDerek J. Clark 		switch (cmd_rep->cmd) {
672d69ccfcbSDerek J. Clark 		case GET_VERSION_DATA:
673d69ccfcbSDerek J. Clark 			ret = hid_go_version_event(cmd_rep);
674d69ccfcbSDerek J. Clark 			break;
67582cd9bc8SDerek J. Clark 		case GET_FEATURE_STATUS:
67682cd9bc8SDerek J. Clark 			ret = hid_go_feature_status_event(cmd_rep);
67782cd9bc8SDerek J. Clark 			break;
67896b20c1fSDerek J. Clark 		case GET_MOTOR_CFG:
67996b20c1fSDerek J. Clark 			ret = hid_go_motor_event(cmd_rep);
68096b20c1fSDerek J. Clark 			break;
681f0bedee6SDerek J. Clark 		case GET_DPI_CFG:
682f0bedee6SDerek J. Clark 			ret = hid_go_fps_dpi_event(cmd_rep);
683f0bedee6SDerek J. Clark 			break;
684325262faSDerek J. Clark 		case GET_RGB_CFG:
685325262faSDerek J. Clark 			ret = hid_go_light_event(cmd_rep);
686325262faSDerek J. Clark 			break;
687995887a1SDerek J. Clark 		case GET_DEVICE_STATUS:
688995887a1SDerek J. Clark 			ret = hid_go_device_status_event(cmd_rep);
689995887a1SDerek J. Clark 			break;
69082cd9bc8SDerek J. Clark 		case SET_FEATURE_STATUS:
69196b20c1fSDerek J. Clark 		case SET_MOTOR_CFG:
692f0bedee6SDerek J. Clark 		case SET_DPI_CFG:
693325262faSDerek J. Clark 		case SET_RGB_CFG:
694995887a1SDerek J. Clark 		case SET_TRIGGER_CFG:
695995887a1SDerek J. Clark 		case SET_JOYSTICK_CFG:
696995887a1SDerek J. Clark 		case SET_GYRO_CFG:
69782cd9bc8SDerek J. Clark 			ret = hid_go_set_event_return(cmd_rep);
69882cd9bc8SDerek J. Clark 			break;
699d69ccfcbSDerek J. Clark 		default:
700d69ccfcbSDerek J. Clark 			ret = -EINVAL;
701d69ccfcbSDerek J. Clark 			break;
702*54549af8SChen Ni 		}
703d69ccfcbSDerek J. Clark 		break;
70451e4270cSDerek J. Clark 	case OS_MODE_DATA:
70551e4270cSDerek J. Clark 		ret = hid_go_os_mode_cfg_event(cmd_rep);
70651e4270cSDerek J. Clark 		break;
707d69ccfcbSDerek J. Clark 	default:
708d69ccfcbSDerek J. Clark 		goto passthrough;
709*54549af8SChen Ni 	}
710d69ccfcbSDerek J. Clark 	dev_dbg(&hdev->dev, "Rx data as raw input report: [%*ph]\n",
711d69ccfcbSDerek J. Clark 		GO_PACKET_SIZE, data);
712d69ccfcbSDerek J. Clark 
713d69ccfcbSDerek J. Clark 	complete(&drvdata.send_cmd_complete);
714d69ccfcbSDerek J. Clark 	return ret;
715d69ccfcbSDerek J. Clark 
716d69ccfcbSDerek J. Clark passthrough:
717d69ccfcbSDerek J. Clark 	/* Forward other HID reports so they generate events */
718d69ccfcbSDerek J. Clark 	hid_input_report(hdev, HID_INPUT_REPORT, data, size, 1);
719d69ccfcbSDerek J. Clark 	return 0;
720d69ccfcbSDerek J. Clark }
721d69ccfcbSDerek J. Clark 
722d69ccfcbSDerek J. Clark static int mcu_property_out(struct hid_device *hdev, u8 id, u8 command,
723d69ccfcbSDerek J. Clark 			    u8 index, enum dev_type device, u8 *data, size_t len)
724d69ccfcbSDerek J. Clark {
725d69ccfcbSDerek J. Clark 	unsigned char *dmabuf __free(kfree) = NULL;
726d69ccfcbSDerek J. Clark 	u8 header[] = { GO_OUTPUT_REPORT_ID, id, command, index, device };
727d69ccfcbSDerek J. Clark 	size_t header_size = ARRAY_SIZE(header);
728d69ccfcbSDerek J. Clark 	int timeout = 50;
729d69ccfcbSDerek J. Clark 	int ret;
730d69ccfcbSDerek J. Clark 
731d69ccfcbSDerek J. Clark 	if (header_size + len > GO_PACKET_SIZE)
732d69ccfcbSDerek J. Clark 		return -EINVAL;
733d69ccfcbSDerek J. Clark 
734d69ccfcbSDerek J. Clark 	guard(mutex)(&drvdata.cfg_mutex);
735d69ccfcbSDerek J. Clark 	/* We can't use a devm_alloc reusable buffer without side effects during suspend */
736d69ccfcbSDerek J. Clark 	dmabuf = kzalloc(GO_PACKET_SIZE, GFP_KERNEL);
737d69ccfcbSDerek J. Clark 	if (!dmabuf)
738d69ccfcbSDerek J. Clark 		return -ENOMEM;
739d69ccfcbSDerek J. Clark 
740d69ccfcbSDerek J. Clark 	memcpy(dmabuf, header, header_size);
741d69ccfcbSDerek J. Clark 	memcpy(dmabuf + header_size, data, len);
742d69ccfcbSDerek J. Clark 
743d69ccfcbSDerek J. Clark 	dev_dbg(&hdev->dev, "Send data as raw output report: [%*ph]\n",
744d69ccfcbSDerek J. Clark 		GO_PACKET_SIZE, dmabuf);
745d69ccfcbSDerek J. Clark 
746d69ccfcbSDerek J. Clark 	ret = hid_hw_output_report(hdev, dmabuf, GO_PACKET_SIZE);
747d69ccfcbSDerek J. Clark 	if (ret < 0)
748d69ccfcbSDerek J. Clark 		return ret;
749d69ccfcbSDerek J. Clark 
750d69ccfcbSDerek J. Clark 	ret = ret == GO_PACKET_SIZE ? 0 : -EINVAL;
751d69ccfcbSDerek J. Clark 	if (ret)
752d69ccfcbSDerek J. Clark 		return ret;
753d69ccfcbSDerek J. Clark 
754d69ccfcbSDerek J. Clark 	ret = wait_for_completion_interruptible_timeout(&drvdata.send_cmd_complete,
755d69ccfcbSDerek J. Clark 							msecs_to_jiffies(timeout));
756d69ccfcbSDerek J. Clark 
757d69ccfcbSDerek J. Clark 	if (ret == 0) /* timeout occurred */
758d69ccfcbSDerek J. Clark 		ret = -EBUSY;
759d69ccfcbSDerek J. Clark 
760d69ccfcbSDerek J. Clark 	reinit_completion(&drvdata.send_cmd_complete);
761d69ccfcbSDerek J. Clark 	return 0;
762d69ccfcbSDerek J. Clark }
763d69ccfcbSDerek J. Clark 
764d69ccfcbSDerek J. Clark static ssize_t version_show(struct device *dev, struct device_attribute *attr,
765d69ccfcbSDerek J. Clark 			    char *buf, enum version_data_index index,
766d69ccfcbSDerek J. Clark 			    enum dev_type device_type)
767d69ccfcbSDerek J. Clark {
768d69ccfcbSDerek J. Clark 	ssize_t count = 0;
769325262faSDerek J. Clark 	int ret;
770325262faSDerek J. Clark 
771325262faSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
772325262faSDerek J. Clark 			       index, device_type, NULL, 0);
773325262faSDerek J. Clark 	if (ret)
774325262faSDerek J. Clark 		return ret;
775d69ccfcbSDerek J. Clark 
776d69ccfcbSDerek J. Clark 	switch (index) {
777d69ccfcbSDerek J. Clark 	case PRODUCT_VERSION:
778d69ccfcbSDerek J. Clark 		switch (device_type) {
779d69ccfcbSDerek J. Clark 		case USB_MCU:
780d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
781d69ccfcbSDerek J. Clark 					   drvdata.mcu_version_product);
782d69ccfcbSDerek J. Clark 			break;
783d69ccfcbSDerek J. Clark 		case TX_DONGLE:
784d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
785d69ccfcbSDerek J. Clark 					   drvdata.tx_dongle_version_product);
786d69ccfcbSDerek J. Clark 			break;
787d69ccfcbSDerek J. Clark 		case LEFT_CONTROLLER:
788d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
789d69ccfcbSDerek J. Clark 					   drvdata.gp_left_version_product);
790d69ccfcbSDerek J. Clark 			break;
791d69ccfcbSDerek J. Clark 		case RIGHT_CONTROLLER:
792d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
793d69ccfcbSDerek J. Clark 					   drvdata.gp_right_version_product);
794d69ccfcbSDerek J. Clark 			break;
795d69ccfcbSDerek J. Clark 		default:
796d69ccfcbSDerek J. Clark 			return -EINVAL;
797d69ccfcbSDerek J. Clark 		}
798d69ccfcbSDerek J. Clark 		break;
799d69ccfcbSDerek J. Clark 	case PROTOCOL_VERSION:
800d69ccfcbSDerek J. Clark 		switch (device_type) {
801d69ccfcbSDerek J. Clark 		case USB_MCU:
802d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
803d69ccfcbSDerek J. Clark 					   drvdata.mcu_version_protocol);
804d69ccfcbSDerek J. Clark 			break;
805d69ccfcbSDerek J. Clark 		case TX_DONGLE:
806d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
807d69ccfcbSDerek J. Clark 					   drvdata.tx_dongle_version_protocol);
808d69ccfcbSDerek J. Clark 			break;
809d69ccfcbSDerek J. Clark 		case LEFT_CONTROLLER:
810d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
811d69ccfcbSDerek J. Clark 					   drvdata.gp_left_version_protocol);
812d69ccfcbSDerek J. Clark 			break;
813d69ccfcbSDerek J. Clark 		case RIGHT_CONTROLLER:
814d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
815d69ccfcbSDerek J. Clark 					   drvdata.gp_right_version_protocol);
816d69ccfcbSDerek J. Clark 			break;
817d69ccfcbSDerek J. Clark 		default:
818d69ccfcbSDerek J. Clark 			return -EINVAL;
819d69ccfcbSDerek J. Clark 		}
820d69ccfcbSDerek J. Clark 		break;
821d69ccfcbSDerek J. Clark 	case FIRMWARE_VERSION:
822d69ccfcbSDerek J. Clark 		switch (device_type) {
823d69ccfcbSDerek J. Clark 		case USB_MCU:
824d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
825d69ccfcbSDerek J. Clark 					   drvdata.mcu_version_firmware);
826d69ccfcbSDerek J. Clark 			break;
827d69ccfcbSDerek J. Clark 		case TX_DONGLE:
828d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
829d69ccfcbSDerek J. Clark 					   drvdata.tx_dongle_version_firmware);
830d69ccfcbSDerek J. Clark 			break;
831d69ccfcbSDerek J. Clark 		case LEFT_CONTROLLER:
832d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
833d69ccfcbSDerek J. Clark 					   drvdata.gp_left_version_firmware);
834d69ccfcbSDerek J. Clark 			break;
835d69ccfcbSDerek J. Clark 		case RIGHT_CONTROLLER:
836d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
837d69ccfcbSDerek J. Clark 					   drvdata.gp_right_version_firmware);
838d69ccfcbSDerek J. Clark 			break;
839d69ccfcbSDerek J. Clark 		default:
840d69ccfcbSDerek J. Clark 			return -EINVAL;
841d69ccfcbSDerek J. Clark 		}
842d69ccfcbSDerek J. Clark 		break;
843d69ccfcbSDerek J. Clark 	case HARDWARE_VERSION:
844d69ccfcbSDerek J. Clark 		switch (device_type) {
845d69ccfcbSDerek J. Clark 		case USB_MCU:
846d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
847d69ccfcbSDerek J. Clark 					   drvdata.mcu_version_hardware);
848d69ccfcbSDerek J. Clark 			break;
849d69ccfcbSDerek J. Clark 		case TX_DONGLE:
850d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
851d69ccfcbSDerek J. Clark 					   drvdata.tx_dongle_version_hardware);
852d69ccfcbSDerek J. Clark 			break;
853d69ccfcbSDerek J. Clark 		case LEFT_CONTROLLER:
854d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
855d69ccfcbSDerek J. Clark 					   drvdata.gp_left_version_hardware);
856d69ccfcbSDerek J. Clark 			break;
857d69ccfcbSDerek J. Clark 		case RIGHT_CONTROLLER:
858d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
859d69ccfcbSDerek J. Clark 					   drvdata.gp_right_version_hardware);
860d69ccfcbSDerek J. Clark 			break;
861d69ccfcbSDerek J. Clark 		default:
862d69ccfcbSDerek J. Clark 			return -EINVAL;
863d69ccfcbSDerek J. Clark 		}
864d69ccfcbSDerek J. Clark 		break;
865d69ccfcbSDerek J. Clark 	case HARDWARE_GENERATION:
866d69ccfcbSDerek J. Clark 		switch (device_type) {
867d69ccfcbSDerek J. Clark 		case USB_MCU:
868d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
869d69ccfcbSDerek J. Clark 					   drvdata.mcu_version_gen);
870d69ccfcbSDerek J. Clark 			break;
871d69ccfcbSDerek J. Clark 		case TX_DONGLE:
872d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
873d69ccfcbSDerek J. Clark 					   drvdata.tx_dongle_version_gen);
874d69ccfcbSDerek J. Clark 			break;
875d69ccfcbSDerek J. Clark 		case LEFT_CONTROLLER:
876d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
877d69ccfcbSDerek J. Clark 					   drvdata.gp_left_version_gen);
878d69ccfcbSDerek J. Clark 			break;
879d69ccfcbSDerek J. Clark 		case RIGHT_CONTROLLER:
880d69ccfcbSDerek J. Clark 			count = sysfs_emit(buf, "%x\n",
881d69ccfcbSDerek J. Clark 					   drvdata.gp_right_version_gen);
882d69ccfcbSDerek J. Clark 			break;
883d69ccfcbSDerek J. Clark 		default:
884d69ccfcbSDerek J. Clark 			return -EINVAL;
885d69ccfcbSDerek J. Clark 		}
886d69ccfcbSDerek J. Clark 		break;
887d69ccfcbSDerek J. Clark 	}
888d69ccfcbSDerek J. Clark 
889d69ccfcbSDerek J. Clark 	return count;
890d69ccfcbSDerek J. Clark }
891d69ccfcbSDerek J. Clark 
89282cd9bc8SDerek J. Clark static ssize_t feature_status_store(struct device *dev,
89382cd9bc8SDerek J. Clark 				    struct device_attribute *attr,
89482cd9bc8SDerek J. Clark 				    const char *buf, size_t count,
89582cd9bc8SDerek J. Clark 				    enum feature_status_index index,
89682cd9bc8SDerek J. Clark 				    enum dev_type device_type)
89782cd9bc8SDerek J. Clark {
89882cd9bc8SDerek J. Clark 	size_t size = 1;
89982cd9bc8SDerek J. Clark 	u8 val = 0;
90082cd9bc8SDerek J. Clark 	int ret;
90182cd9bc8SDerek J. Clark 
90282cd9bc8SDerek J. Clark 	switch (index) {
90382cd9bc8SDerek J. Clark 	case FEATURE_IMU_ENABLE:
90482cd9bc8SDerek J. Clark 	case FEATURE_IMU_BYPASS:
90582cd9bc8SDerek J. Clark 	case FEATURE_LIGHT_ENABLE:
90682cd9bc8SDerek J. Clark 	case FEATURE_TOUCHPAD_ENABLE:
90782cd9bc8SDerek J. Clark 		ret = sysfs_match_string(enabled_status_text, buf);
90882cd9bc8SDerek J. Clark 		val = ret;
90982cd9bc8SDerek J. Clark 		break;
91082cd9bc8SDerek J. Clark 	case FEATURE_AUTO_SLEEP_TIME:
91182cd9bc8SDerek J. Clark 		ret = kstrtou8(buf, 10, &val);
91282cd9bc8SDerek J. Clark 		break;
91382cd9bc8SDerek J. Clark 	case FEATURE_RESET_GAMEPAD:
91482cd9bc8SDerek J. Clark 		ret = kstrtou8(buf, 10, &val);
91582cd9bc8SDerek J. Clark 		if (val != GO_GP_RESET_SUCCESS)
91682cd9bc8SDerek J. Clark 			return -EINVAL;
91782cd9bc8SDerek J. Clark 		break;
91882cd9bc8SDerek J. Clark 	case FEATURE_FPS_SWITCH_STATUS:
91982cd9bc8SDerek J. Clark 		ret = sysfs_match_string(fps_switch_text, buf);
92082cd9bc8SDerek J. Clark 		val = ret;
92182cd9bc8SDerek J. Clark 		break;
92282cd9bc8SDerek J. Clark 	case FEATURE_GAMEPAD_MODE:
92382cd9bc8SDerek J. Clark 		ret = sysfs_match_string(gamepad_mode_text, buf);
92482cd9bc8SDerek J. Clark 		val = ret;
92582cd9bc8SDerek J. Clark 		break;
92682cd9bc8SDerek J. Clark 	default:
92782cd9bc8SDerek J. Clark 		return -EINVAL;
928*54549af8SChen Ni 	}
92982cd9bc8SDerek J. Clark 
93082cd9bc8SDerek J. Clark 	if (ret < 0)
93182cd9bc8SDerek J. Clark 		return ret;
93282cd9bc8SDerek J. Clark 
93382cd9bc8SDerek J. Clark 	if (!val)
93482cd9bc8SDerek J. Clark 		size = 0;
93582cd9bc8SDerek J. Clark 
93682cd9bc8SDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA,
93782cd9bc8SDerek J. Clark 			       SET_FEATURE_STATUS, index, device_type, &val,
93882cd9bc8SDerek J. Clark 			       size);
93982cd9bc8SDerek J. Clark 	if (ret < 0)
94082cd9bc8SDerek J. Clark 		return ret;
94182cd9bc8SDerek J. Clark 
94282cd9bc8SDerek J. Clark 	return count;
94382cd9bc8SDerek J. Clark }
94482cd9bc8SDerek J. Clark 
94582cd9bc8SDerek J. Clark static ssize_t feature_status_show(struct device *dev,
94682cd9bc8SDerek J. Clark 				   struct device_attribute *attr, char *buf,
94782cd9bc8SDerek J. Clark 				   enum feature_status_index index,
94882cd9bc8SDerek J. Clark 				   enum dev_type device_type)
94982cd9bc8SDerek J. Clark {
95082cd9bc8SDerek J. Clark 	ssize_t count = 0;
95182cd9bc8SDerek J. Clark 	int ret;
95282cd9bc8SDerek J. Clark 	u8 i;
95382cd9bc8SDerek J. Clark 
95482cd9bc8SDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA,
95582cd9bc8SDerek J. Clark 			       GET_FEATURE_STATUS, index, device_type, NULL, 0);
95682cd9bc8SDerek J. Clark 	if (ret)
95782cd9bc8SDerek J. Clark 		return ret;
95882cd9bc8SDerek J. Clark 
95982cd9bc8SDerek J. Clark 	switch (index) {
96082cd9bc8SDerek J. Clark 	case FEATURE_IMU_ENABLE:
96182cd9bc8SDerek J. Clark 		switch (device_type) {
96282cd9bc8SDerek J. Clark 		case LEFT_CONTROLLER:
96382cd9bc8SDerek J. Clark 			i = drvdata.imu_left_sensor_en;
96482cd9bc8SDerek J. Clark 			break;
96582cd9bc8SDerek J. Clark 		case RIGHT_CONTROLLER:
96682cd9bc8SDerek J. Clark 			i = drvdata.imu_right_sensor_en;
96782cd9bc8SDerek J. Clark 			break;
96882cd9bc8SDerek J. Clark 		default:
96982cd9bc8SDerek J. Clark 			return -EINVAL;
97082cd9bc8SDerek J. Clark 		}
97182cd9bc8SDerek J. Clark 		if (i >= ARRAY_SIZE(enabled_status_text))
97282cd9bc8SDerek J. Clark 			return -EINVAL;
97382cd9bc8SDerek J. Clark 
97482cd9bc8SDerek J. Clark 		count = sysfs_emit(buf, "%s\n", enabled_status_text[i]);
97582cd9bc8SDerek J. Clark 		break;
97682cd9bc8SDerek J. Clark 	case FEATURE_IMU_BYPASS:
97782cd9bc8SDerek J. Clark 		switch (device_type) {
97882cd9bc8SDerek J. Clark 		case LEFT_CONTROLLER:
97982cd9bc8SDerek J. Clark 			i = drvdata.imu_left_bypass_en;
98082cd9bc8SDerek J. Clark 			break;
98182cd9bc8SDerek J. Clark 		case RIGHT_CONTROLLER:
98282cd9bc8SDerek J. Clark 			i = drvdata.imu_right_bypass_en;
98382cd9bc8SDerek J. Clark 			break;
98482cd9bc8SDerek J. Clark 		default:
98582cd9bc8SDerek J. Clark 			return -EINVAL;
98682cd9bc8SDerek J. Clark 		}
98782cd9bc8SDerek J. Clark 		if (i >= ARRAY_SIZE(enabled_status_text))
98882cd9bc8SDerek J. Clark 			return -EINVAL;
98982cd9bc8SDerek J. Clark 
99082cd9bc8SDerek J. Clark 		count = sysfs_emit(buf, "%s\n", enabled_status_text[i]);
99182cd9bc8SDerek J. Clark 		break;
99282cd9bc8SDerek J. Clark 	case FEATURE_LIGHT_ENABLE:
99382cd9bc8SDerek J. Clark 		i = drvdata.rgb_en;
99482cd9bc8SDerek J. Clark 		if (i >= ARRAY_SIZE(enabled_status_text))
99582cd9bc8SDerek J. Clark 			return -EINVAL;
99682cd9bc8SDerek J. Clark 
99782cd9bc8SDerek J. Clark 		count = sysfs_emit(buf, "%s\n", enabled_status_text[i]);
99882cd9bc8SDerek J. Clark 		break;
99982cd9bc8SDerek J. Clark 	case FEATURE_TOUCHPAD_ENABLE:
100082cd9bc8SDerek J. Clark 		i = drvdata.tp_en;
100182cd9bc8SDerek J. Clark 		if (i >= ARRAY_SIZE(enabled_status_text))
100282cd9bc8SDerek J. Clark 			return -EINVAL;
100382cd9bc8SDerek J. Clark 
100482cd9bc8SDerek J. Clark 		count = sysfs_emit(buf, "%s\n", enabled_status_text[i]);
100582cd9bc8SDerek J. Clark 		break;
100682cd9bc8SDerek J. Clark 	case FEATURE_AUTO_SLEEP_TIME:
100782cd9bc8SDerek J. Clark 		switch (device_type) {
100882cd9bc8SDerek J. Clark 		case LEFT_CONTROLLER:
100982cd9bc8SDerek J. Clark 			i = drvdata.gp_left_auto_sleep_time;
101082cd9bc8SDerek J. Clark 			break;
101182cd9bc8SDerek J. Clark 		case RIGHT_CONTROLLER:
101282cd9bc8SDerek J. Clark 			i = drvdata.gp_right_auto_sleep_time;
101382cd9bc8SDerek J. Clark 			break;
101482cd9bc8SDerek J. Clark 		default:
101582cd9bc8SDerek J. Clark 			return -EINVAL;
1016*54549af8SChen Ni 		}
101782cd9bc8SDerek J. Clark 		count = sysfs_emit(buf, "%u\n", i);
101882cd9bc8SDerek J. Clark 		break;
101982cd9bc8SDerek J. Clark 	case FEATURE_FPS_SWITCH_STATUS:
102082cd9bc8SDerek J. Clark 		i = drvdata.fps_mode;
102182cd9bc8SDerek J. Clark 		if (i >= ARRAY_SIZE(fps_switch_text))
102282cd9bc8SDerek J. Clark 			return -EINVAL;
102382cd9bc8SDerek J. Clark 
102482cd9bc8SDerek J. Clark 		count = sysfs_emit(buf, "%s\n", fps_switch_text[i]);
102582cd9bc8SDerek J. Clark 		break;
102682cd9bc8SDerek J. Clark 	case FEATURE_GAMEPAD_MODE:
102782cd9bc8SDerek J. Clark 		i = drvdata.gp_mode;
102882cd9bc8SDerek J. Clark 		if (i >= ARRAY_SIZE(gamepad_mode_text))
102982cd9bc8SDerek J. Clark 			return -EINVAL;
103082cd9bc8SDerek J. Clark 
103182cd9bc8SDerek J. Clark 		count = sysfs_emit(buf, "%s\n", gamepad_mode_text[i]);
103282cd9bc8SDerek J. Clark 		break;
103382cd9bc8SDerek J. Clark 	default:
103482cd9bc8SDerek J. Clark 		return -EINVAL;
1035*54549af8SChen Ni 	}
103682cd9bc8SDerek J. Clark 
103782cd9bc8SDerek J. Clark 	return count;
103882cd9bc8SDerek J. Clark }
103982cd9bc8SDerek J. Clark 
104082cd9bc8SDerek J. Clark static ssize_t feature_status_options(struct device *dev,
104182cd9bc8SDerek J. Clark 				      struct device_attribute *attr, char *buf,
104282cd9bc8SDerek J. Clark 				      enum feature_status_index index)
104382cd9bc8SDerek J. Clark {
104482cd9bc8SDerek J. Clark 	ssize_t count = 0;
104582cd9bc8SDerek J. Clark 	unsigned int i;
104682cd9bc8SDerek J. Clark 
104782cd9bc8SDerek J. Clark 	switch (index) {
104882cd9bc8SDerek J. Clark 	case FEATURE_IMU_ENABLE:
104982cd9bc8SDerek J. Clark 	case FEATURE_IMU_BYPASS:
105082cd9bc8SDerek J. Clark 	case FEATURE_LIGHT_ENABLE:
105182cd9bc8SDerek J. Clark 	case FEATURE_TOUCHPAD_ENABLE:
105282cd9bc8SDerek J. Clark 		for (i = 1; i < ARRAY_SIZE(enabled_status_text); i++) {
105382cd9bc8SDerek J. Clark 			count += sysfs_emit_at(buf, count, "%s ",
105482cd9bc8SDerek J. Clark 					       enabled_status_text[i]);
105582cd9bc8SDerek J. Clark 		}
105682cd9bc8SDerek J. Clark 		break;
105782cd9bc8SDerek J. Clark 	case FEATURE_AUTO_SLEEP_TIME:
105882cd9bc8SDerek J. Clark 		return sysfs_emit(buf, "0-255\n");
105982cd9bc8SDerek J. Clark 	case FEATURE_FPS_SWITCH_STATUS:
106082cd9bc8SDerek J. Clark 		for (i = 1; i < ARRAY_SIZE(fps_switch_text); i++) {
106182cd9bc8SDerek J. Clark 			count += sysfs_emit_at(buf, count, "%s ",
106282cd9bc8SDerek J. Clark 					       fps_switch_text[i]);
106382cd9bc8SDerek J. Clark 		}
106482cd9bc8SDerek J. Clark 		break;
106582cd9bc8SDerek J. Clark 	case FEATURE_GAMEPAD_MODE:
106682cd9bc8SDerek J. Clark 		for (i = 1; i < ARRAY_SIZE(gamepad_mode_text); i++) {
106782cd9bc8SDerek J. Clark 			count += sysfs_emit_at(buf, count, "%s ",
106882cd9bc8SDerek J. Clark 					       gamepad_mode_text[i]);
106982cd9bc8SDerek J. Clark 		}
107082cd9bc8SDerek J. Clark 		break;
107182cd9bc8SDerek J. Clark 	default:
107282cd9bc8SDerek J. Clark 		return -EINVAL;
1073*54549af8SChen Ni 	}
107482cd9bc8SDerek J. Clark 
107582cd9bc8SDerek J. Clark 	if (count)
107682cd9bc8SDerek J. Clark 		buf[count - 1] = '\n';
107782cd9bc8SDerek J. Clark 
107882cd9bc8SDerek J. Clark 	return count;
107982cd9bc8SDerek J. Clark }
108082cd9bc8SDerek J. Clark 
108196b20c1fSDerek J. Clark static ssize_t motor_config_store(struct device *dev,
108296b20c1fSDerek J. Clark 				  struct device_attribute *attr,
108396b20c1fSDerek J. Clark 				  const char *buf, size_t count,
108496b20c1fSDerek J. Clark 				  enum motor_cfg_index index,
108596b20c1fSDerek J. Clark 				  enum dev_type device_type)
108696b20c1fSDerek J. Clark {
108796b20c1fSDerek J. Clark 	size_t size = 1;
108896b20c1fSDerek J. Clark 	u8 val = 0;
108996b20c1fSDerek J. Clark 	int ret;
109096b20c1fSDerek J. Clark 
109196b20c1fSDerek J. Clark 	switch (index) {
109296b20c1fSDerek J. Clark 	case MOTOR_CFG_ALL:
109396b20c1fSDerek J. Clark 		return -EINVAL;
109496b20c1fSDerek J. Clark 	case MOTOR_INTENSITY:
109596b20c1fSDerek J. Clark 		ret = sysfs_match_string(intensity_text, buf);
109696b20c1fSDerek J. Clark 		val = ret;
109796b20c1fSDerek J. Clark 		break;
109896b20c1fSDerek J. Clark 	case VIBRATION_NOTIFY_ENABLE:
109996b20c1fSDerek J. Clark 		ret = sysfs_match_string(enabled_status_text, buf);
110096b20c1fSDerek J. Clark 		val = ret;
110196b20c1fSDerek J. Clark 		break;
110296b20c1fSDerek J. Clark 	case RUMBLE_MODE:
110396b20c1fSDerek J. Clark 		ret = sysfs_match_string(rumble_mode_text, buf);
110496b20c1fSDerek J. Clark 		val = ret;
110596b20c1fSDerek J. Clark 		break;
110696b20c1fSDerek J. Clark 	case TP_VIBRATION_ENABLE:
110796b20c1fSDerek J. Clark 		ret = sysfs_match_string(enabled_status_text, buf);
110896b20c1fSDerek J. Clark 		val = ret;
110996b20c1fSDerek J. Clark 		break;
111096b20c1fSDerek J. Clark 	case TP_VIBRATION_INTENSITY:
111196b20c1fSDerek J. Clark 		ret = sysfs_match_string(intensity_text, buf);
111296b20c1fSDerek J. Clark 		val = ret;
111396b20c1fSDerek J. Clark 		break;
1114*54549af8SChen Ni 	}
111596b20c1fSDerek J. Clark 
111696b20c1fSDerek J. Clark 	if (ret < 0)
111796b20c1fSDerek J. Clark 		return ret;
111896b20c1fSDerek J. Clark 
111996b20c1fSDerek J. Clark 	if (!val)
112096b20c1fSDerek J. Clark 		size = 0;
112196b20c1fSDerek J. Clark 
112296b20c1fSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, SET_MOTOR_CFG,
112396b20c1fSDerek J. Clark 			       index, device_type, &val, size);
112496b20c1fSDerek J. Clark 	if (ret < 0)
112596b20c1fSDerek J. Clark 		return ret;
112696b20c1fSDerek J. Clark 
112796b20c1fSDerek J. Clark 	return count;
112896b20c1fSDerek J. Clark }
112996b20c1fSDerek J. Clark 
113096b20c1fSDerek J. Clark static ssize_t motor_config_show(struct device *dev,
113196b20c1fSDerek J. Clark 				 struct device_attribute *attr, char *buf,
113296b20c1fSDerek J. Clark 				 enum motor_cfg_index index,
113396b20c1fSDerek J. Clark 				 enum dev_type device_type)
113496b20c1fSDerek J. Clark {
113596b20c1fSDerek J. Clark 	ssize_t count = 0;
113696b20c1fSDerek J. Clark 	int ret;
113796b20c1fSDerek J. Clark 	u8 i;
113896b20c1fSDerek J. Clark 
113996b20c1fSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_MOTOR_CFG,
114096b20c1fSDerek J. Clark 			       index, device_type, NULL, 0);
114196b20c1fSDerek J. Clark 	if (ret)
114296b20c1fSDerek J. Clark 		return ret;
114396b20c1fSDerek J. Clark 
114496b20c1fSDerek J. Clark 	switch (index) {
114596b20c1fSDerek J. Clark 	case MOTOR_CFG_ALL:
114696b20c1fSDerek J. Clark 		return -EINVAL;
114796b20c1fSDerek J. Clark 	case MOTOR_INTENSITY:
114896b20c1fSDerek J. Clark 		i = drvdata.gp_rumble_intensity;
114996b20c1fSDerek J. Clark 		if (i >= ARRAY_SIZE(intensity_text))
115096b20c1fSDerek J. Clark 			return -EINVAL;
115196b20c1fSDerek J. Clark 
115296b20c1fSDerek J. Clark 		count = sysfs_emit(buf, "%s\n", intensity_text[i]);
115396b20c1fSDerek J. Clark 		break;
115496b20c1fSDerek J. Clark 	case VIBRATION_NOTIFY_ENABLE:
115596b20c1fSDerek J. Clark 		switch (device_type) {
115696b20c1fSDerek J. Clark 		case LEFT_CONTROLLER:
115796b20c1fSDerek J. Clark 			i = drvdata.gp_left_notify_en;
115896b20c1fSDerek J. Clark 			break;
115996b20c1fSDerek J. Clark 		case RIGHT_CONTROLLER:
116096b20c1fSDerek J. Clark 			i = drvdata.gp_right_notify_en;
116196b20c1fSDerek J. Clark 			break;
116296b20c1fSDerek J. Clark 		default:
116396b20c1fSDerek J. Clark 			return -EINVAL;
1164*54549af8SChen Ni 		}
116596b20c1fSDerek J. Clark 		if (i >= ARRAY_SIZE(enabled_status_text))
116696b20c1fSDerek J. Clark 			return -EINVAL;
116796b20c1fSDerek J. Clark 
116896b20c1fSDerek J. Clark 		count = sysfs_emit(buf, "%s\n", enabled_status_text[i]);
116996b20c1fSDerek J. Clark 		break;
117096b20c1fSDerek J. Clark 	case RUMBLE_MODE:
117196b20c1fSDerek J. Clark 		switch (device_type) {
117296b20c1fSDerek J. Clark 		case LEFT_CONTROLLER:
117396b20c1fSDerek J. Clark 			i = drvdata.gp_left_rumble_mode;
117496b20c1fSDerek J. Clark 			break;
117596b20c1fSDerek J. Clark 		case RIGHT_CONTROLLER:
117696b20c1fSDerek J. Clark 			i = drvdata.gp_right_rumble_mode;
117796b20c1fSDerek J. Clark 			break;
117896b20c1fSDerek J. Clark 		default:
117996b20c1fSDerek J. Clark 			return -EINVAL;
1180*54549af8SChen Ni 		}
118196b20c1fSDerek J. Clark 		if (i >= ARRAY_SIZE(rumble_mode_text))
118296b20c1fSDerek J. Clark 			return -EINVAL;
118396b20c1fSDerek J. Clark 
118496b20c1fSDerek J. Clark 		count = sysfs_emit(buf, "%s\n", rumble_mode_text[i]);
118596b20c1fSDerek J. Clark 		break;
118696b20c1fSDerek J. Clark 	case TP_VIBRATION_ENABLE:
118796b20c1fSDerek J. Clark 		i = drvdata.tp_vibration_en;
118896b20c1fSDerek J. Clark 		if (i >= ARRAY_SIZE(enabled_status_text))
118996b20c1fSDerek J. Clark 			return -EINVAL;
119096b20c1fSDerek J. Clark 
119196b20c1fSDerek J. Clark 		count = sysfs_emit(buf, "%s\n", enabled_status_text[i]);
119296b20c1fSDerek J. Clark 		break;
119396b20c1fSDerek J. Clark 	case TP_VIBRATION_INTENSITY:
119496b20c1fSDerek J. Clark 		i = drvdata.tp_vibration_intensity;
119596b20c1fSDerek J. Clark 		if (i >= ARRAY_SIZE(intensity_text))
119696b20c1fSDerek J. Clark 			return -EINVAL;
119796b20c1fSDerek J. Clark 
119896b20c1fSDerek J. Clark 		count = sysfs_emit(buf, "%s\n", intensity_text[i]);
119996b20c1fSDerek J. Clark 		break;
1200*54549af8SChen Ni 	}
120196b20c1fSDerek J. Clark 
120296b20c1fSDerek J. Clark 	return count;
120396b20c1fSDerek J. Clark }
120496b20c1fSDerek J. Clark 
120596b20c1fSDerek J. Clark static ssize_t motor_config_options(struct device *dev,
120696b20c1fSDerek J. Clark 				    struct device_attribute *attr, char *buf,
120796b20c1fSDerek J. Clark 				    enum motor_cfg_index index)
120896b20c1fSDerek J. Clark {
120996b20c1fSDerek J. Clark 	ssize_t count = 0;
121096b20c1fSDerek J. Clark 	unsigned int i;
121196b20c1fSDerek J. Clark 
121296b20c1fSDerek J. Clark 	switch (index) {
121396b20c1fSDerek J. Clark 	case MOTOR_CFG_ALL:
121496b20c1fSDerek J. Clark 		break;
121596b20c1fSDerek J. Clark 	case RUMBLE_MODE:
121696b20c1fSDerek J. Clark 		for (i = 1; i < ARRAY_SIZE(rumble_mode_text); i++) {
121796b20c1fSDerek J. Clark 			count += sysfs_emit_at(buf, count, "%s ",
121896b20c1fSDerek J. Clark 					       rumble_mode_text[i]);
121996b20c1fSDerek J. Clark 		}
122096b20c1fSDerek J. Clark 		break;
122196b20c1fSDerek J. Clark 	case MOTOR_INTENSITY:
122296b20c1fSDerek J. Clark 	case TP_VIBRATION_INTENSITY:
122396b20c1fSDerek J. Clark 		for (i = 1; i < ARRAY_SIZE(intensity_text); i++) {
122496b20c1fSDerek J. Clark 			count += sysfs_emit_at(buf, count, "%s ",
122596b20c1fSDerek J. Clark 					       intensity_text[i]);
122696b20c1fSDerek J. Clark 		}
122796b20c1fSDerek J. Clark 		break;
122896b20c1fSDerek J. Clark 	case VIBRATION_NOTIFY_ENABLE:
122996b20c1fSDerek J. Clark 	case TP_VIBRATION_ENABLE:
123096b20c1fSDerek J. Clark 		for (i = 1; i < ARRAY_SIZE(enabled_status_text); i++) {
123196b20c1fSDerek J. Clark 			count += sysfs_emit_at(buf, count, "%s ",
123296b20c1fSDerek J. Clark 					       enabled_status_text[i]);
123396b20c1fSDerek J. Clark 		}
123496b20c1fSDerek J. Clark 		break;
1235*54549af8SChen Ni 	}
123696b20c1fSDerek J. Clark 
123796b20c1fSDerek J. Clark 	if (count)
123896b20c1fSDerek J. Clark 		buf[count - 1] = '\n';
123996b20c1fSDerek J. Clark 
124096b20c1fSDerek J. Clark 	return count;
124196b20c1fSDerek J. Clark }
124296b20c1fSDerek J. Clark 
1243f0bedee6SDerek J. Clark static ssize_t fps_mode_dpi_store(struct device *dev,
1244f0bedee6SDerek J. Clark 				  struct device_attribute *attr,
1245f0bedee6SDerek J. Clark 				  const char *buf, size_t count)
1246f0bedee6SDerek J. Clark 
1247f0bedee6SDerek J. Clark {
1248f0bedee6SDerek J. Clark 	size_t size = 4;
1249f0bedee6SDerek J. Clark 	u32 value;
1250f0bedee6SDerek J. Clark 	u8 val[4];
1251f0bedee6SDerek J. Clark 	int ret;
1252f0bedee6SDerek J. Clark 
1253f0bedee6SDerek J. Clark 	ret = kstrtou32(buf, 10, &value);
1254f0bedee6SDerek J. Clark 	if (ret)
1255f0bedee6SDerek J. Clark 		return ret;
1256f0bedee6SDerek J. Clark 
1257f0bedee6SDerek J. Clark 	if (value != 500 && value != 800 && value != 1200 && value != 1800)
1258f0bedee6SDerek J. Clark 		return -EINVAL;
1259f0bedee6SDerek J. Clark 
1260f0bedee6SDerek J. Clark 	put_unaligned_le32(value, val);
1261f0bedee6SDerek J. Clark 
1262f0bedee6SDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, SET_DPI_CFG,
1263f0bedee6SDerek J. Clark 			       FPS_MODE_DPI, UNSPECIFIED, val, size);
1264f0bedee6SDerek J. Clark 	if (ret < 0)
1265f0bedee6SDerek J. Clark 		return ret;
1266f0bedee6SDerek J. Clark 
1267f0bedee6SDerek J. Clark 	return count;
1268f0bedee6SDerek J. Clark }
1269f0bedee6SDerek J. Clark 
1270f0bedee6SDerek J. Clark static ssize_t fps_mode_dpi_show(struct device *dev,
1271f0bedee6SDerek J. Clark 				 struct device_attribute *attr, char *buf)
1272f0bedee6SDerek J. Clark {
1273f0bedee6SDerek J. Clark 	int ret;
1274f0bedee6SDerek J. Clark 
1275f0bedee6SDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_DPI_CFG,
1276f0bedee6SDerek J. Clark 			       FPS_MODE_DPI, UNSPECIFIED, NULL, 0);
1277f0bedee6SDerek J. Clark 	if (ret < 0)
1278f0bedee6SDerek J. Clark 		return ret;
1279f0bedee6SDerek J. Clark 
1280f0bedee6SDerek J. Clark 	return sysfs_emit(buf, "%u\n", drvdata.mouse_dpi);
1281f0bedee6SDerek J. Clark }
1282f0bedee6SDerek J. Clark 
1283f0bedee6SDerek J. Clark static ssize_t fps_mode_dpi_index_show(struct device *dev,
1284f0bedee6SDerek J. Clark 				       struct device_attribute *attr, char *buf)
1285f0bedee6SDerek J. Clark {
1286f0bedee6SDerek J. Clark 	return sysfs_emit(buf, "500 800 1200 1800\n");
1287f0bedee6SDerek J. Clark }
1288f0bedee6SDerek J. Clark 
1289995887a1SDerek J. Clark static ssize_t device_status_show(struct device *dev,
1290995887a1SDerek J. Clark 				  struct device_attribute *attr, char *buf,
1291995887a1SDerek J. Clark 				  enum device_status_index index,
1292995887a1SDerek J. Clark 				  enum dev_type device_type,
1293995887a1SDerek J. Clark 				  enum cal_device_type cal_type)
1294995887a1SDerek J. Clark {
1295995887a1SDerek J. Clark 	u8 i;
1296995887a1SDerek J. Clark 
1297995887a1SDerek J. Clark 	switch (index) {
1298995887a1SDerek J. Clark 	case GET_CAL_STATUS:
1299995887a1SDerek J. Clark 		switch (device_type) {
1300995887a1SDerek J. Clark 		case LEFT_CONTROLLER:
1301995887a1SDerek J. Clark 			switch (cal_type) {
1302995887a1SDerek J. Clark 			case CALDEV_GYROSCOPE:
1303995887a1SDerek J. Clark 				i = drvdata.gp_left_gyro_cal_status;
1304995887a1SDerek J. Clark 				break;
1305995887a1SDerek J. Clark 			case CALDEV_JOYSTICK:
1306995887a1SDerek J. Clark 				i = drvdata.gp_left_joy_cal_status;
1307995887a1SDerek J. Clark 				break;
1308995887a1SDerek J. Clark 			case CALDEV_TRIGGER:
1309995887a1SDerek J. Clark 				i = drvdata.gp_left_trigg_cal_status;
1310995887a1SDerek J. Clark 				break;
1311995887a1SDerek J. Clark 			default:
1312995887a1SDerek J. Clark 				return -EINVAL;
1313995887a1SDerek J. Clark 			}
1314995887a1SDerek J. Clark 			break;
1315995887a1SDerek J. Clark 		case RIGHT_CONTROLLER:
1316995887a1SDerek J. Clark 			switch (cal_type) {
1317995887a1SDerek J. Clark 			case CALDEV_GYROSCOPE:
1318995887a1SDerek J. Clark 				i = drvdata.gp_right_gyro_cal_status;
1319995887a1SDerek J. Clark 				break;
1320995887a1SDerek J. Clark 			case CALDEV_JOYSTICK:
1321995887a1SDerek J. Clark 				i = drvdata.gp_right_joy_cal_status;
1322995887a1SDerek J. Clark 				break;
1323995887a1SDerek J. Clark 			case CALDEV_TRIGGER:
1324995887a1SDerek J. Clark 				i = drvdata.gp_right_trigg_cal_status;
1325995887a1SDerek J. Clark 				break;
1326995887a1SDerek J. Clark 			default:
1327995887a1SDerek J. Clark 				return -EINVAL;
1328995887a1SDerek J. Clark 			}
1329995887a1SDerek J. Clark 			break;
1330995887a1SDerek J. Clark 		default:
1331995887a1SDerek J. Clark 			return -EINVAL;
1332995887a1SDerek J. Clark 		}
1333995887a1SDerek J. Clark 		break;
1334995887a1SDerek J. Clark 	default:
1335995887a1SDerek J. Clark 		return -EINVAL;
1336*54549af8SChen Ni 	}
1337995887a1SDerek J. Clark 
1338995887a1SDerek J. Clark 	if (i >= ARRAY_SIZE(cal_status_text))
1339995887a1SDerek J. Clark 		return -EINVAL;
1340995887a1SDerek J. Clark 
1341995887a1SDerek J. Clark 	return sysfs_emit(buf, "%s\n", cal_status_text[i]);
1342995887a1SDerek J. Clark }
1343995887a1SDerek J. Clark 
1344995887a1SDerek J. Clark static ssize_t calibrate_config_store(struct device *dev,
1345995887a1SDerek J. Clark 				      struct device_attribute *attr,
1346995887a1SDerek J. Clark 				      const char *buf, u8 cmd, u8 sub_cmd,
1347995887a1SDerek J. Clark 				      size_t count, enum dev_type device_type)
1348995887a1SDerek J. Clark {
1349995887a1SDerek J. Clark 	size_t size = 1;
1350995887a1SDerek J. Clark 	u8 val = 0;
1351995887a1SDerek J. Clark 	int ret;
1352995887a1SDerek J. Clark 
1353995887a1SDerek J. Clark 	ret = sysfs_match_string(cal_enabled_text, buf);
1354995887a1SDerek J. Clark 	if (ret < 0)
1355995887a1SDerek J. Clark 		return ret;
1356995887a1SDerek J. Clark 
1357995887a1SDerek J. Clark 	val = ret;
1358995887a1SDerek J. Clark 	if (!val)
1359995887a1SDerek J. Clark 		size = 0;
1360995887a1SDerek J. Clark 
1361995887a1SDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, cmd, sub_cmd,
1362995887a1SDerek J. Clark 			       device_type, &val, size);
1363995887a1SDerek J. Clark 	if (ret < 0)
1364995887a1SDerek J. Clark 		return ret;
1365995887a1SDerek J. Clark 
1366995887a1SDerek J. Clark 	return count;
1367995887a1SDerek J. Clark }
1368995887a1SDerek J. Clark 
1369995887a1SDerek J. Clark static ssize_t calibrate_config_options(struct device *dev,
1370995887a1SDerek J. Clark 					struct device_attribute *attr,
1371995887a1SDerek J. Clark 					char *buf)
1372995887a1SDerek J. Clark {
1373995887a1SDerek J. Clark 	ssize_t count = 0;
1374995887a1SDerek J. Clark 	unsigned int i;
1375995887a1SDerek J. Clark 
1376995887a1SDerek J. Clark 	for (i = 1; i < ARRAY_SIZE(cal_enabled_text); i++)
1377995887a1SDerek J. Clark 		count += sysfs_emit_at(buf, count, "%s ", cal_enabled_text[i]);
1378995887a1SDerek J. Clark 
1379995887a1SDerek J. Clark 	buf[count - 1] = '\n';
1380995887a1SDerek J. Clark 
1381995887a1SDerek J. Clark 	return count;
1382995887a1SDerek J. Clark }
1383995887a1SDerek J. Clark 
138451e4270cSDerek J. Clark static ssize_t os_mode_store(struct device *dev, struct device_attribute *attr,
138551e4270cSDerek J. Clark 			     const char *buf, size_t count)
138651e4270cSDerek J. Clark {
138751e4270cSDerek J. Clark 	size_t size = 1;
138851e4270cSDerek J. Clark 	int ret;
138951e4270cSDerek J. Clark 	u8 val;
139051e4270cSDerek J. Clark 
139151e4270cSDerek J. Clark 	ret = sysfs_match_string(os_mode_text, buf);
139251e4270cSDerek J. Clark 	if (ret <= 0)
139351e4270cSDerek J. Clark 		return ret;
139451e4270cSDerek J. Clark 
139551e4270cSDerek J. Clark 	val = ret;
139651e4270cSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, OS_MODE_DATA, FEATURE_OS_MODE,
139751e4270cSDerek J. Clark 			       SET_OS_MODE, USB_MCU, &val, size);
139851e4270cSDerek J. Clark 	if (ret < 0)
139951e4270cSDerek J. Clark 		return ret;
140051e4270cSDerek J. Clark 
140151e4270cSDerek J. Clark 	drvdata.os_mode = val;
140251e4270cSDerek J. Clark 
140351e4270cSDerek J. Clark 	return count;
140451e4270cSDerek J. Clark }
140551e4270cSDerek J. Clark 
140651e4270cSDerek J. Clark static ssize_t os_mode_show(struct device *dev, struct device_attribute *attr,
140751e4270cSDerek J. Clark 			    char *buf)
140851e4270cSDerek J. Clark {
140951e4270cSDerek J. Clark 	ssize_t count = 0;
141051e4270cSDerek J. Clark 	int ret;
141151e4270cSDerek J. Clark 	u8 i;
141251e4270cSDerek J. Clark 
141351e4270cSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, OS_MODE_DATA, FEATURE_OS_MODE,
141451e4270cSDerek J. Clark 			       GET_OS_MODE, USB_MCU, NULL, 0);
141551e4270cSDerek J. Clark 	if (ret)
141651e4270cSDerek J. Clark 		return ret;
141751e4270cSDerek J. Clark 
141851e4270cSDerek J. Clark 	i = drvdata.os_mode;
141951e4270cSDerek J. Clark 	if (i >= ARRAY_SIZE(os_mode_text))
142051e4270cSDerek J. Clark 		return -EINVAL;
142151e4270cSDerek J. Clark 
142251e4270cSDerek J. Clark 	count = sysfs_emit(buf, "%s\n", os_mode_text[i]);
142351e4270cSDerek J. Clark 
142451e4270cSDerek J. Clark 	return count;
142551e4270cSDerek J. Clark }
142651e4270cSDerek J. Clark 
142751e4270cSDerek J. Clark static ssize_t os_mode_index_show(struct device *dev,
142851e4270cSDerek J. Clark 				  struct device_attribute *attr, char *buf)
142951e4270cSDerek J. Clark {
143051e4270cSDerek J. Clark 	ssize_t count = 0;
143151e4270cSDerek J. Clark 	unsigned int i;
143251e4270cSDerek J. Clark 
143351e4270cSDerek J. Clark 	for (i = 1; i < ARRAY_SIZE(os_mode_text); i++)
143451e4270cSDerek J. Clark 		count += sysfs_emit_at(buf, count, "%s ", os_mode_text[i]);
143551e4270cSDerek J. Clark 
143651e4270cSDerek J. Clark 	if (count)
143751e4270cSDerek J. Clark 		buf[count - 1] = '\n';
143851e4270cSDerek J. Clark 
143951e4270cSDerek J. Clark 	return count;
144051e4270cSDerek J. Clark }
144151e4270cSDerek J. Clark 
1442325262faSDerek J. Clark static int rgb_cfg_call(struct hid_device *hdev, enum mcu_command_index cmd,
1443325262faSDerek J. Clark 			enum rgb_config_index index, u8 *val, size_t size)
1444325262faSDerek J. Clark {
1445325262faSDerek J. Clark 	if (cmd != SET_RGB_CFG && cmd != GET_RGB_CFG)
1446325262faSDerek J. Clark 		return -EINVAL;
1447325262faSDerek J. Clark 
1448325262faSDerek J. Clark 	if (index < LIGHT_CFG_ALL || index > USR_LIGHT_PROFILE_3)
1449325262faSDerek J. Clark 		return -EINVAL;
1450325262faSDerek J. Clark 
1451325262faSDerek J. Clark 	return mcu_property_out(hdev, MCU_CONFIG_DATA, cmd, index, UNSPECIFIED,
1452325262faSDerek J. Clark 				val, size);
1453325262faSDerek J. Clark }
1454325262faSDerek J. Clark 
1455325262faSDerek J. Clark static int rgb_attr_show(void)
1456325262faSDerek J. Clark {
1457325262faSDerek J. Clark 	enum rgb_config_index index;
1458325262faSDerek J. Clark 
1459325262faSDerek J. Clark 	index = drvdata.rgb_profile + 3;
1460325262faSDerek J. Clark 
1461325262faSDerek J. Clark 	return rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, index, NULL, 0);
1462*54549af8SChen Ni }
1463325262faSDerek J. Clark 
1464325262faSDerek J. Clark static ssize_t rgb_effect_store(struct device *dev,
1465325262faSDerek J. Clark 				struct device_attribute *attr, const char *buf,
1466325262faSDerek J. Clark 				size_t count)
1467325262faSDerek J. Clark {
1468325262faSDerek J. Clark 	struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);
1469325262faSDerek J. Clark 	enum rgb_config_index index;
1470325262faSDerek J. Clark 	u8 effect;
1471325262faSDerek J. Clark 	int ret;
1472325262faSDerek J. Clark 
1473325262faSDerek J. Clark 	ret = sysfs_match_string(rgb_effect_text, buf);
1474325262faSDerek J. Clark 	if (ret < 0)
1475325262faSDerek J. Clark 		return ret;
1476325262faSDerek J. Clark 
1477325262faSDerek J. Clark 	effect = ret;
1478325262faSDerek J. Clark 	index = drvdata.rgb_profile + 3;
1479325262faSDerek J. Clark 	u8 rgb_profile[6] = { effect,
1480325262faSDerek J. Clark 			      mc_cdev->subled_info[0].intensity,
1481325262faSDerek J. Clark 			      mc_cdev->subled_info[1].intensity,
1482325262faSDerek J. Clark 			      mc_cdev->subled_info[2].intensity,
1483325262faSDerek J. Clark 			      drvdata.led_cdev->brightness,
1484325262faSDerek J. Clark 			      drvdata.rgb_speed };
1485325262faSDerek J. Clark 
1486325262faSDerek J. Clark 	ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);
1487325262faSDerek J. Clark 	if (ret)
1488325262faSDerek J. Clark 		return ret;
1489325262faSDerek J. Clark 
1490325262faSDerek J. Clark 	drvdata.rgb_effect = effect;
1491325262faSDerek J. Clark 	return count;
1492*54549af8SChen Ni }
1493325262faSDerek J. Clark 
1494325262faSDerek J. Clark static ssize_t rgb_effect_show(struct device *dev,
1495325262faSDerek J. Clark 			       struct device_attribute *attr, char *buf)
1496325262faSDerek J. Clark {
1497325262faSDerek J. Clark 	int ret;
1498325262faSDerek J. Clark 
1499325262faSDerek J. Clark 	ret = rgb_attr_show();
1500325262faSDerek J. Clark 	if (ret)
1501325262faSDerek J. Clark 		return ret;
1502325262faSDerek J. Clark 
1503325262faSDerek J. Clark 	if (drvdata.rgb_effect >= ARRAY_SIZE(rgb_effect_text))
1504325262faSDerek J. Clark 		return -EINVAL;
1505325262faSDerek J. Clark 
1506325262faSDerek J. Clark 	return sysfs_emit(buf, "%s\n", rgb_effect_text[drvdata.rgb_effect]);
1507325262faSDerek J. Clark }
1508325262faSDerek J. Clark 
1509325262faSDerek J. Clark static ssize_t rgb_effect_index_show(struct device *dev,
1510325262faSDerek J. Clark 				     struct device_attribute *attr, char *buf)
1511325262faSDerek J. Clark {
1512325262faSDerek J. Clark 	ssize_t count = 0;
1513325262faSDerek J. Clark 	unsigned int i;
1514325262faSDerek J. Clark 
1515325262faSDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(rgb_effect_text); i++)
1516325262faSDerek J. Clark 		count += sysfs_emit_at(buf, count, "%s ", rgb_effect_text[i]);
1517325262faSDerek J. Clark 
1518325262faSDerek J. Clark 	if (count)
1519325262faSDerek J. Clark 		buf[count - 1] = '\n';
1520325262faSDerek J. Clark 
1521325262faSDerek J. Clark 	return count;
1522325262faSDerek J. Clark }
1523325262faSDerek J. Clark 
1524325262faSDerek J. Clark static ssize_t rgb_speed_store(struct device *dev,
1525325262faSDerek J. Clark 			       struct device_attribute *attr, const char *buf,
1526325262faSDerek J. Clark 			       size_t count)
1527325262faSDerek J. Clark {
1528325262faSDerek J. Clark 	struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);
1529325262faSDerek J. Clark 	enum rgb_config_index index;
1530325262faSDerek J. Clark 	int val = 0;
1531325262faSDerek J. Clark 	int ret;
1532325262faSDerek J. Clark 
1533325262faSDerek J. Clark 	ret = kstrtoint(buf, 10, &val);
1534325262faSDerek J. Clark 	if (ret)
1535325262faSDerek J. Clark 		return ret;
1536325262faSDerek J. Clark 
1537325262faSDerek J. Clark 	if (val < 0 || val > 100)
1538325262faSDerek J. Clark 		return -EINVAL;
1539325262faSDerek J. Clark 
1540325262faSDerek J. Clark 	/* This is a delay setting, invert logic for consistency with other drivers */
1541325262faSDerek J. Clark 	val = 100 - val;
1542325262faSDerek J. Clark 
1543325262faSDerek J. Clark 	index = drvdata.rgb_profile + 3;
1544325262faSDerek J. Clark 	u8 rgb_profile[6] = { drvdata.rgb_effect,
1545325262faSDerek J. Clark 			      mc_cdev->subled_info[0].intensity,
1546325262faSDerek J. Clark 			      mc_cdev->subled_info[1].intensity,
1547325262faSDerek J. Clark 			      mc_cdev->subled_info[2].intensity,
1548325262faSDerek J. Clark 			      drvdata.led_cdev->brightness,
1549325262faSDerek J. Clark 			      val };
1550325262faSDerek J. Clark 
1551325262faSDerek J. Clark 	ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);
1552325262faSDerek J. Clark 	if (ret)
1553325262faSDerek J. Clark 		return ret;
1554325262faSDerek J. Clark 
1555325262faSDerek J. Clark 	drvdata.rgb_speed = val;
1556325262faSDerek J. Clark 
1557325262faSDerek J. Clark 	return count;
1558*54549af8SChen Ni }
1559325262faSDerek J. Clark 
1560325262faSDerek J. Clark static ssize_t rgb_speed_show(struct device *dev, struct device_attribute *attr,
1561325262faSDerek J. Clark 			      char *buf)
1562325262faSDerek J. Clark {
1563325262faSDerek J. Clark 	int ret, val;
1564325262faSDerek J. Clark 
1565325262faSDerek J. Clark 	ret = rgb_attr_show();
1566325262faSDerek J. Clark 	if (ret)
1567325262faSDerek J. Clark 		return ret;
1568325262faSDerek J. Clark 
1569325262faSDerek J. Clark 	if (drvdata.rgb_speed > 100)
1570325262faSDerek J. Clark 		return -EINVAL;
1571325262faSDerek J. Clark 
1572325262faSDerek J. Clark 	val = drvdata.rgb_speed;
1573325262faSDerek J. Clark 
1574325262faSDerek J. Clark 	return sysfs_emit(buf, "%hhu\n", val);
1575325262faSDerek J. Clark }
1576325262faSDerek J. Clark 
1577325262faSDerek J. Clark static ssize_t rgb_speed_range_show(struct device *dev,
1578325262faSDerek J. Clark 				    struct device_attribute *attr, char *buf)
1579325262faSDerek J. Clark {
1580325262faSDerek J. Clark 	return sysfs_emit(buf, "0-100\n");
1581325262faSDerek J. Clark }
1582325262faSDerek J. Clark 
1583325262faSDerek J. Clark static ssize_t rgb_mode_store(struct device *dev, struct device_attribute *attr,
1584325262faSDerek J. Clark 			      const char *buf, size_t count)
1585325262faSDerek J. Clark {
1586325262faSDerek J. Clark 	int ret;
1587325262faSDerek J. Clark 	u8 val;
1588325262faSDerek J. Clark 
1589325262faSDerek J. Clark 	ret = sysfs_match_string(rgb_mode_text, buf);
1590325262faSDerek J. Clark 	if (ret <= 0)
1591325262faSDerek J. Clark 		return ret;
1592325262faSDerek J. Clark 
1593325262faSDerek J. Clark 	val = ret;
1594325262faSDerek J. Clark 
1595325262faSDerek J. Clark 	ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_MODE_SEL, &val, 1);
1596325262faSDerek J. Clark 	if (ret)
1597325262faSDerek J. Clark 		return ret;
1598325262faSDerek J. Clark 
1599325262faSDerek J. Clark 	drvdata.rgb_mode = val;
1600325262faSDerek J. Clark 
1601325262faSDerek J. Clark 	return count;
1602*54549af8SChen Ni }
1603325262faSDerek J. Clark 
1604325262faSDerek J. Clark static ssize_t rgb_mode_show(struct device *dev, struct device_attribute *attr,
1605325262faSDerek J. Clark 			     char *buf)
1606325262faSDerek J. Clark {
1607325262faSDerek J. Clark 	int ret;
1608325262faSDerek J. Clark 
1609325262faSDerek J. Clark 	ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_MODE_SEL, NULL, 0);
1610325262faSDerek J. Clark 	if (ret)
1611325262faSDerek J. Clark 		return ret;
1612325262faSDerek J. Clark 
1613325262faSDerek J. Clark 	if (drvdata.rgb_mode >= ARRAY_SIZE(rgb_mode_text))
1614325262faSDerek J. Clark 		return -EINVAL;
1615325262faSDerek J. Clark 
1616325262faSDerek J. Clark 	return sysfs_emit(buf, "%s\n", rgb_mode_text[drvdata.rgb_mode]);
1617*54549af8SChen Ni }
1618325262faSDerek J. Clark 
1619325262faSDerek J. Clark static ssize_t rgb_mode_index_show(struct device *dev,
1620325262faSDerek J. Clark 				   struct device_attribute *attr, char *buf)
1621325262faSDerek J. Clark {
1622325262faSDerek J. Clark 	ssize_t count = 0;
1623325262faSDerek J. Clark 	unsigned int i;
1624325262faSDerek J. Clark 
1625325262faSDerek J. Clark 	for (i = 1; i < ARRAY_SIZE(rgb_mode_text); i++)
1626325262faSDerek J. Clark 		count += sysfs_emit_at(buf, count, "%s ", rgb_mode_text[i]);
1627325262faSDerek J. Clark 
1628325262faSDerek J. Clark 	if (count)
1629325262faSDerek J. Clark 		buf[count - 1] = '\n';
1630325262faSDerek J. Clark 
1631325262faSDerek J. Clark 	return count;
1632325262faSDerek J. Clark }
1633325262faSDerek J. Clark 
1634325262faSDerek J. Clark static ssize_t rgb_profile_store(struct device *dev,
1635325262faSDerek J. Clark 				 struct device_attribute *attr, const char *buf,
1636325262faSDerek J. Clark 				 size_t count)
1637325262faSDerek J. Clark {
1638325262faSDerek J. Clark 	size_t size = 1;
1639325262faSDerek J. Clark 	int ret;
1640325262faSDerek J. Clark 	u8 val;
1641325262faSDerek J. Clark 
1642325262faSDerek J. Clark 	ret = kstrtou8(buf, 10, &val);
1643325262faSDerek J. Clark 	if (ret < 0)
1644325262faSDerek J. Clark 		return ret;
1645325262faSDerek J. Clark 
1646325262faSDerek J. Clark 	if (val < 1 || val > 3)
1647325262faSDerek J. Clark 		return -EINVAL;
1648325262faSDerek J. Clark 
1649325262faSDerek J. Clark 	ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_PROFILE_SEL, &val, size);
1650325262faSDerek J. Clark 	if (ret)
1651325262faSDerek J. Clark 		return ret;
1652325262faSDerek J. Clark 
1653325262faSDerek J. Clark 	drvdata.rgb_profile = val;
1654325262faSDerek J. Clark 
1655325262faSDerek J. Clark 	return count;
1656*54549af8SChen Ni }
1657325262faSDerek J. Clark 
1658325262faSDerek J. Clark static ssize_t rgb_profile_show(struct device *dev,
1659325262faSDerek J. Clark 				struct device_attribute *attr, char *buf)
1660325262faSDerek J. Clark {
1661325262faSDerek J. Clark 	int ret;
1662325262faSDerek J. Clark 
1663325262faSDerek J. Clark 	ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_PROFILE_SEL, NULL, 0);
1664325262faSDerek J. Clark 	if (ret)
1665325262faSDerek J. Clark 		return ret;
1666325262faSDerek J. Clark 
1667325262faSDerek J. Clark 	if (drvdata.rgb_profile < 1 || drvdata.rgb_profile > 3)
1668325262faSDerek J. Clark 		return -EINVAL;
1669325262faSDerek J. Clark 
1670325262faSDerek J. Clark 	return sysfs_emit(buf, "%hhu\n", drvdata.rgb_profile);
1671*54549af8SChen Ni }
1672325262faSDerek J. Clark 
1673325262faSDerek J. Clark static ssize_t rgb_profile_range_show(struct device *dev,
1674325262faSDerek J. Clark 				      struct device_attribute *attr, char *buf)
1675325262faSDerek J. Clark {
1676325262faSDerek J. Clark 	return sysfs_emit(buf, "1-3\n");
1677325262faSDerek J. Clark }
1678325262faSDerek J. Clark 
1679325262faSDerek J. Clark static void hid_go_brightness_set(struct led_classdev *led_cdev,
1680325262faSDerek J. Clark 				  enum led_brightness brightness)
1681325262faSDerek J. Clark {
1682325262faSDerek J. Clark 	struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);
1683325262faSDerek J. Clark 	enum rgb_config_index index;
1684325262faSDerek J. Clark 	int ret;
1685325262faSDerek J. Clark 
1686325262faSDerek J. Clark 	if (brightness > led_cdev->max_brightness) {
1687325262faSDerek J. Clark 		dev_err(led_cdev->dev, "Invalid argument\n");
1688325262faSDerek J. Clark 		return;
1689325262faSDerek J. Clark 	}
1690325262faSDerek J. Clark 
1691325262faSDerek J. Clark 	index = drvdata.rgb_profile + 3;
1692325262faSDerek J. Clark 	u8 rgb_profile[6] = { drvdata.rgb_effect,
1693325262faSDerek J. Clark 			      mc_cdev->subled_info[0].intensity,
1694325262faSDerek J. Clark 			      mc_cdev->subled_info[1].intensity,
1695325262faSDerek J. Clark 			      mc_cdev->subled_info[2].intensity,
1696325262faSDerek J. Clark 			      brightness,
1697325262faSDerek J. Clark 			      drvdata.rgb_speed };
1698325262faSDerek J. Clark 
1699325262faSDerek J. Clark 	ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);
1700325262faSDerek J. Clark 	switch (ret) {
1701325262faSDerek J. Clark 	case 0:
1702325262faSDerek J. Clark 		led_cdev->brightness = brightness;
1703325262faSDerek J. Clark 		break;
1704325262faSDerek J. Clark 	case -ENODEV: /* during switch to IAP -ENODEV is expected */
1705325262faSDerek J. Clark 	case -ENOSYS: /* during rmmod -ENOSYS is expected */
1706325262faSDerek J. Clark 		dev_dbg(led_cdev->dev, "Failed to write RGB profile: %i\n", ret);
1707325262faSDerek J. Clark 		break;
1708325262faSDerek J. Clark 	default:
1709325262faSDerek J. Clark 		dev_err(led_cdev->dev, "Failed to write RGB profile: %i\n", ret);
1710*54549af8SChen Ni 	}
1711325262faSDerek J. Clark }
1712325262faSDerek J. Clark 
1713d69ccfcbSDerek J. Clark #define LEGO_DEVICE_ATTR_RW(_name, _attrname, _dtype, _rtype, _group)         \
1714d69ccfcbSDerek J. Clark 	static ssize_t _name##_store(struct device *dev,                      \
1715d69ccfcbSDerek J. Clark 				     struct device_attribute *attr,           \
1716d69ccfcbSDerek J. Clark 				     const char *buf, size_t count)           \
1717d69ccfcbSDerek J. Clark 	{                                                                     \
1718d69ccfcbSDerek J. Clark 		return _group##_store(dev, attr, buf, count, _name.index,     \
1719d69ccfcbSDerek J. Clark 				      _dtype);                                \
1720d69ccfcbSDerek J. Clark 	}                                                                     \
1721d69ccfcbSDerek J. Clark 	static ssize_t _name##_show(struct device *dev,                       \
1722d69ccfcbSDerek J. Clark 				    struct device_attribute *attr, char *buf) \
1723d69ccfcbSDerek J. Clark 	{                                                                     \
1724d69ccfcbSDerek J. Clark 		return _group##_show(dev, attr, buf, _name.index, _dtype);    \
1725d69ccfcbSDerek J. Clark 	}                                                                     \
1726d69ccfcbSDerek J. Clark 	static ssize_t _name##_##_rtype##_show(                               \
1727d69ccfcbSDerek J. Clark 		struct device *dev, struct device_attribute *attr, char *buf) \
1728d69ccfcbSDerek J. Clark 	{                                                                     \
1729d69ccfcbSDerek J. Clark 		return _group##_options(dev, attr, buf, _name.index);         \
1730d69ccfcbSDerek J. Clark 	}                                                                     \
1731d69ccfcbSDerek J. Clark 	static DEVICE_ATTR_RW_NAMED(_name, _attrname)
1732d69ccfcbSDerek J. Clark 
1733d69ccfcbSDerek J. Clark #define LEGO_DEVICE_ATTR_WO(_name, _attrname, _dtype, _group)             \
1734d69ccfcbSDerek J. Clark 	static ssize_t _name##_store(struct device *dev,                  \
1735d69ccfcbSDerek J. Clark 				     struct device_attribute *attr,       \
1736d69ccfcbSDerek J. Clark 				     const char *buf, size_t count)       \
1737d69ccfcbSDerek J. Clark 	{                                                                 \
1738d69ccfcbSDerek J. Clark 		return _group##_store(dev, attr, buf, count, _name.index, \
1739d69ccfcbSDerek J. Clark 				      _dtype);                            \
1740d69ccfcbSDerek J. Clark 	}                                                                 \
1741d69ccfcbSDerek J. Clark 	static DEVICE_ATTR_WO_NAMED(_name, _attrname)
1742d69ccfcbSDerek J. Clark 
1743d69ccfcbSDerek J. Clark #define LEGO_DEVICE_ATTR_RO(_name, _attrname, _dtype, _group)                 \
1744d69ccfcbSDerek J. Clark 	static ssize_t _name##_show(struct device *dev,                       \
1745d69ccfcbSDerek J. Clark 				    struct device_attribute *attr, char *buf) \
1746d69ccfcbSDerek J. Clark 	{                                                                     \
1747d69ccfcbSDerek J. Clark 		return _group##_show(dev, attr, buf, _name.index, _dtype);    \
1748d69ccfcbSDerek J. Clark 	}                                                                     \
1749d69ccfcbSDerek J. Clark 	static DEVICE_ATTR_RO_NAMED(_name, _attrname)
1750d69ccfcbSDerek J. Clark 
1751995887a1SDerek J. Clark #define LEGO_CAL_DEVICE_ATTR(_name, _attrname, _scmd, _dtype, _rtype)         \
1752995887a1SDerek J. Clark 	static ssize_t _name##_store(struct device *dev,                      \
1753995887a1SDerek J. Clark 				     struct device_attribute *attr,           \
1754995887a1SDerek J. Clark 				     const char *buf, size_t count)           \
1755995887a1SDerek J. Clark 	{                                                                     \
1756995887a1SDerek J. Clark 		return calibrate_config_store(dev, attr, buf, _name.index,    \
1757995887a1SDerek J. Clark 					      _scmd, count, _dtype);          \
1758995887a1SDerek J. Clark 	}                                                                     \
1759995887a1SDerek J. Clark 	static ssize_t _name##_##_rtype##_show(                               \
1760995887a1SDerek J. Clark 		struct device *dev, struct device_attribute *attr, char *buf) \
1761995887a1SDerek J. Clark 	{                                                                     \
1762995887a1SDerek J. Clark 		return calibrate_config_options(dev, attr, buf);              \
1763995887a1SDerek J. Clark 	}                                                                     \
1764995887a1SDerek J. Clark 	static DEVICE_ATTR_WO_NAMED(_name, _attrname)
1765995887a1SDerek J. Clark 
1766995887a1SDerek J. Clark #define LEGO_DEVICE_STATUS_ATTR(_name, _attrname, _scmd, _dtype)              \
1767995887a1SDerek J. Clark 	static ssize_t _name##_show(struct device *dev,                       \
1768995887a1SDerek J. Clark 				    struct device_attribute *attr, char *buf) \
1769995887a1SDerek J. Clark 	{                                                                     \
1770995887a1SDerek J. Clark 		return device_status_show(dev, attr, buf, _name.index, _scmd, \
1771995887a1SDerek J. Clark 					  _dtype);                            \
1772995887a1SDerek J. Clark 	}                                                                     \
1773995887a1SDerek J. Clark 	static DEVICE_ATTR_RO_NAMED(_name, _attrname)
1774995887a1SDerek J. Clark 
1775d69ccfcbSDerek J. Clark /* Gamepad - MCU */
1776d69ccfcbSDerek J. Clark static struct go_cfg_attr version_product_mcu = { PRODUCT_VERSION };
1777d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_product_mcu, "product_version", USB_MCU, version);
1778d69ccfcbSDerek J. Clark 
1779d69ccfcbSDerek J. Clark static struct go_cfg_attr version_protocol_mcu = { PROTOCOL_VERSION };
1780d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_protocol_mcu, "protocol_version", USB_MCU, version);
1781d69ccfcbSDerek J. Clark 
1782d69ccfcbSDerek J. Clark static struct go_cfg_attr version_firmware_mcu = { FIRMWARE_VERSION };
1783d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_firmware_mcu, "firmware_version", USB_MCU, version);
1784d69ccfcbSDerek J. Clark 
1785d69ccfcbSDerek J. Clark static struct go_cfg_attr version_hardware_mcu = { HARDWARE_VERSION };
1786d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_hardware_mcu, "hardware_version", USB_MCU, version);
1787d69ccfcbSDerek J. Clark 
1788d69ccfcbSDerek J. Clark static struct go_cfg_attr version_gen_mcu = { HARDWARE_GENERATION };
1789d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_gen_mcu, "hardware_generation", USB_MCU, version);
1790d69ccfcbSDerek J. Clark 
179182cd9bc8SDerek J. Clark static struct go_cfg_attr fps_switch_status = { FEATURE_FPS_SWITCH_STATUS };
179282cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RO(fps_switch_status, "fps_switch_status", UNSPECIFIED,
179382cd9bc8SDerek J. Clark 		    feature_status);
179482cd9bc8SDerek J. Clark 
179582cd9bc8SDerek J. Clark static struct go_cfg_attr gamepad_mode = { FEATURE_GAMEPAD_MODE };
179682cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(gamepad_mode, "mode", UNSPECIFIED, index, feature_status);
179782cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(gamepad_mode_index, "mode_index");
179882cd9bc8SDerek J. Clark 
179982cd9bc8SDerek J. Clark static struct go_cfg_attr reset_mcu = { FEATURE_RESET_GAMEPAD };
180082cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_WO(reset_mcu, "reset_mcu", USB_MCU, feature_status);
180182cd9bc8SDerek J. Clark 
180296b20c1fSDerek J. Clark static struct go_cfg_attr gamepad_rumble_intensity = { MOTOR_INTENSITY };
180396b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(gamepad_rumble_intensity, "rumble_intensity", UNSPECIFIED,
180496b20c1fSDerek J. Clark 		    index, motor_config);
180596b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(gamepad_rumble_intensity_index,
180696b20c1fSDerek J. Clark 			    "rumble_intensity_index");
180796b20c1fSDerek J. Clark 
1808f0bedee6SDerek J. Clark static DEVICE_ATTR_RW(fps_mode_dpi);
1809f0bedee6SDerek J. Clark static DEVICE_ATTR_RO(fps_mode_dpi_index);
1810f0bedee6SDerek J. Clark 
181151e4270cSDerek J. Clark static DEVICE_ATTR_RW(os_mode);
181251e4270cSDerek J. Clark static DEVICE_ATTR_RO(os_mode_index);
181351e4270cSDerek J. Clark 
1814d69ccfcbSDerek J. Clark static struct attribute *mcu_attrs[] = {
1815f0bedee6SDerek J. Clark 	&dev_attr_fps_mode_dpi.attr,
1816f0bedee6SDerek J. Clark 	&dev_attr_fps_mode_dpi_index.attr,
181782cd9bc8SDerek J. Clark 	&dev_attr_fps_switch_status.attr,
181882cd9bc8SDerek J. Clark 	&dev_attr_gamepad_mode.attr,
181982cd9bc8SDerek J. Clark 	&dev_attr_gamepad_mode_index.attr,
182096b20c1fSDerek J. Clark 	&dev_attr_gamepad_rumble_intensity.attr,
182196b20c1fSDerek J. Clark 	&dev_attr_gamepad_rumble_intensity_index.attr,
182251e4270cSDerek J. Clark 	&dev_attr_os_mode.attr,
182351e4270cSDerek J. Clark 	&dev_attr_os_mode_index.attr,
182482cd9bc8SDerek J. Clark 	&dev_attr_reset_mcu.attr,
1825d69ccfcbSDerek J. Clark 	&dev_attr_version_firmware_mcu.attr,
1826d69ccfcbSDerek J. Clark 	&dev_attr_version_gen_mcu.attr,
1827d69ccfcbSDerek J. Clark 	&dev_attr_version_hardware_mcu.attr,
1828d69ccfcbSDerek J. Clark 	&dev_attr_version_product_mcu.attr,
1829d69ccfcbSDerek J. Clark 	&dev_attr_version_protocol_mcu.attr,
1830d69ccfcbSDerek J. Clark 	NULL,
1831d69ccfcbSDerek J. Clark };
1832d69ccfcbSDerek J. Clark 
1833d69ccfcbSDerek J. Clark static const struct attribute_group mcu_attr_group = {
1834d69ccfcbSDerek J. Clark 	.attrs = mcu_attrs,
1835d69ccfcbSDerek J. Clark };
1836d69ccfcbSDerek J. Clark 
1837d69ccfcbSDerek J. Clark /* Gamepad - TX Dongle */
1838d69ccfcbSDerek J. Clark static struct go_cfg_attr version_product_tx_dongle = { PRODUCT_VERSION };
1839d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_product_tx_dongle, "product_version", TX_DONGLE, version);
1840d69ccfcbSDerek J. Clark 
1841d69ccfcbSDerek J. Clark static struct go_cfg_attr version_protocol_tx_dongle = { PROTOCOL_VERSION };
1842d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_protocol_tx_dongle, "protocol_version", TX_DONGLE, version);
1843d69ccfcbSDerek J. Clark 
1844d69ccfcbSDerek J. Clark static struct go_cfg_attr version_firmware_tx_dongle = { FIRMWARE_VERSION };
1845d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_firmware_tx_dongle, "firmware_version", TX_DONGLE, version);
1846d69ccfcbSDerek J. Clark 
1847d69ccfcbSDerek J. Clark static struct go_cfg_attr version_hardware_tx_dongle = { HARDWARE_VERSION };
1848d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_hardware_tx_dongle, "hardware_version", TX_DONGLE, version);
1849d69ccfcbSDerek J. Clark 
1850d69ccfcbSDerek J. Clark static struct go_cfg_attr version_gen_tx_dongle = { HARDWARE_GENERATION };
1851d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_gen_tx_dongle, "hardware_generation", TX_DONGLE, version);
1852d69ccfcbSDerek J. Clark 
185382cd9bc8SDerek J. Clark static struct go_cfg_attr reset_tx_dongle = { FEATURE_RESET_GAMEPAD };
185482cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RO(reset_tx_dongle, "reset", TX_DONGLE, feature_status);
185582cd9bc8SDerek J. Clark 
1856d69ccfcbSDerek J. Clark static struct attribute *tx_dongle_attrs[] = {
185782cd9bc8SDerek J. Clark 	&dev_attr_reset_tx_dongle.attr,
1858d69ccfcbSDerek J. Clark 	&dev_attr_version_hardware_tx_dongle.attr,
1859d69ccfcbSDerek J. Clark 	&dev_attr_version_firmware_tx_dongle.attr,
1860d69ccfcbSDerek J. Clark 	&dev_attr_version_gen_tx_dongle.attr,
1861d69ccfcbSDerek J. Clark 	&dev_attr_version_product_tx_dongle.attr,
1862d69ccfcbSDerek J. Clark 	&dev_attr_version_protocol_tx_dongle.attr,
1863d69ccfcbSDerek J. Clark 	NULL,
1864d69ccfcbSDerek J. Clark };
1865d69ccfcbSDerek J. Clark 
1866d69ccfcbSDerek J. Clark static const struct attribute_group tx_dongle_attr_group = {
1867d69ccfcbSDerek J. Clark 	.name = "tx_dongle",
1868d69ccfcbSDerek J. Clark 	.attrs = tx_dongle_attrs,
1869d69ccfcbSDerek J. Clark };
1870d69ccfcbSDerek J. Clark 
1871d69ccfcbSDerek J. Clark /* Gamepad - Left */
1872d69ccfcbSDerek J. Clark static struct go_cfg_attr version_product_left = { PRODUCT_VERSION };
1873d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_product_left, "product_version", LEFT_CONTROLLER, version);
1874d69ccfcbSDerek J. Clark 
1875d69ccfcbSDerek J. Clark static struct go_cfg_attr version_protocol_left = { PROTOCOL_VERSION };
1876d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_protocol_left, "protocol_version", LEFT_CONTROLLER, version);
1877d69ccfcbSDerek J. Clark 
1878d69ccfcbSDerek J. Clark static struct go_cfg_attr version_firmware_left = { FIRMWARE_VERSION };
1879d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_firmware_left, "firmware_version", LEFT_CONTROLLER, version);
1880d69ccfcbSDerek J. Clark 
1881d69ccfcbSDerek J. Clark static struct go_cfg_attr version_hardware_left = { HARDWARE_VERSION };
1882d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_hardware_left, "hardware_version", LEFT_CONTROLLER, version);
1883d69ccfcbSDerek J. Clark 
1884d69ccfcbSDerek J. Clark static struct go_cfg_attr version_gen_left = { HARDWARE_GENERATION };
1885d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_gen_left, "hardware_generation", LEFT_CONTROLLER, version);
1886d69ccfcbSDerek J. Clark 
188782cd9bc8SDerek J. Clark static struct go_cfg_attr auto_sleep_time_left = { FEATURE_AUTO_SLEEP_TIME };
188882cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(auto_sleep_time_left, "auto_sleep_time", LEFT_CONTROLLER,
188982cd9bc8SDerek J. Clark 		    range, feature_status);
189082cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(auto_sleep_time_left_range,
189182cd9bc8SDerek J. Clark 			    "auto_sleep_time_range");
189282cd9bc8SDerek J. Clark 
189382cd9bc8SDerek J. Clark static struct go_cfg_attr imu_bypass_left = { FEATURE_IMU_BYPASS };
189482cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(imu_bypass_left, "imu_bypass_enabled", LEFT_CONTROLLER,
189582cd9bc8SDerek J. Clark 		    index, feature_status);
189682cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(imu_bypass_left_index, "imu_bypass_enabled_index");
189782cd9bc8SDerek J. Clark 
189882cd9bc8SDerek J. Clark static struct go_cfg_attr imu_enabled_left = { FEATURE_IMU_ENABLE };
189982cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(imu_enabled_left, "imu_enabled", LEFT_CONTROLLER, index,
190082cd9bc8SDerek J. Clark 		    feature_status);
190182cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(imu_enabled_left_index, "imu_enabled_index");
190282cd9bc8SDerek J. Clark 
190382cd9bc8SDerek J. Clark static struct go_cfg_attr reset_left = { FEATURE_RESET_GAMEPAD };
190482cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_WO(reset_left, "reset", LEFT_CONTROLLER, feature_status);
190582cd9bc8SDerek J. Clark 
190696b20c1fSDerek J. Clark static struct go_cfg_attr rumble_mode_left = { RUMBLE_MODE };
190796b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(rumble_mode_left, "rumble_mode", LEFT_CONTROLLER, index,
190896b20c1fSDerek J. Clark 		    motor_config);
190996b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rumble_mode_left_index, "rumble_mode_index");
191096b20c1fSDerek J. Clark 
191196b20c1fSDerek J. Clark static struct go_cfg_attr rumble_notification_left = { VIBRATION_NOTIFY_ENABLE };
191296b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(rumble_notification_left, "rumble_notification",
191396b20c1fSDerek J. Clark 		    LEFT_CONTROLLER, index, motor_config);
191496b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rumble_notification_left_index,
191596b20c1fSDerek J. Clark 			    "rumble_notification_index");
191696b20c1fSDerek J. Clark 
1917995887a1SDerek J. Clark static struct go_cfg_attr cal_trigg_left = { TRIGGER_CALIBRATE };
1918995887a1SDerek J. Clark LEGO_CAL_DEVICE_ATTR(cal_trigg_left, "calibrate_trigger", SET_TRIGGER_CFG,
1919995887a1SDerek J. Clark 		     LEFT_CONTROLLER, index);
1920995887a1SDerek J. Clark static DEVICE_ATTR_RO_NAMED(cal_trigg_left_index, "calibrate_trigger_index");
1921995887a1SDerek J. Clark 
1922995887a1SDerek J. Clark static struct go_cfg_attr cal_joy_left = { JOYSTICK_CALIBRATE };
1923995887a1SDerek J. Clark LEGO_CAL_DEVICE_ATTR(cal_joy_left, "calibrate_joystick", SET_JOYSTICK_CFG,
1924995887a1SDerek J. Clark 		     LEFT_CONTROLLER, index);
1925995887a1SDerek J. Clark static DEVICE_ATTR_RO_NAMED(cal_joy_left_index, "calibrate_joystick_index");
1926995887a1SDerek J. Clark 
1927995887a1SDerek J. Clark static struct go_cfg_attr cal_gyro_left = { GYRO_CALIBRATE };
1928995887a1SDerek J. Clark LEGO_CAL_DEVICE_ATTR(cal_gyro_left, "calibrate_gyro", SET_GYRO_CFG,
1929995887a1SDerek J. Clark 		     LEFT_CONTROLLER, index);
1930995887a1SDerek J. Clark static DEVICE_ATTR_RO_NAMED(cal_gyro_left_index, "calibrate_gyro_index");
1931995887a1SDerek J. Clark 
1932995887a1SDerek J. Clark static struct go_cfg_attr cal_trigg_left_status = { GET_CAL_STATUS };
1933995887a1SDerek J. Clark LEGO_DEVICE_STATUS_ATTR(cal_trigg_left_status, "calibrate_trigger_status",
1934995887a1SDerek J. Clark 			LEFT_CONTROLLER, CALDEV_TRIGGER);
1935995887a1SDerek J. Clark 
1936995887a1SDerek J. Clark static struct go_cfg_attr cal_joy_left_status = { GET_CAL_STATUS };
1937995887a1SDerek J. Clark LEGO_DEVICE_STATUS_ATTR(cal_joy_left_status, "calibrate_joystick_status",
1938995887a1SDerek J. Clark 			LEFT_CONTROLLER, CALDEV_JOYSTICK);
1939995887a1SDerek J. Clark 
1940995887a1SDerek J. Clark static struct go_cfg_attr cal_gyro_left_status = { GET_CAL_STATUS };
1941995887a1SDerek J. Clark LEGO_DEVICE_STATUS_ATTR(cal_gyro_left_status, "calibrate_gyro_status",
1942995887a1SDerek J. Clark 			LEFT_CONTROLLER, CALDEV_GYROSCOPE);
1943995887a1SDerek J. Clark 
1944d69ccfcbSDerek J. Clark static struct attribute *left_gamepad_attrs[] = {
194582cd9bc8SDerek J. Clark 	&dev_attr_auto_sleep_time_left.attr,
194682cd9bc8SDerek J. Clark 	&dev_attr_auto_sleep_time_left_range.attr,
1947995887a1SDerek J. Clark 	&dev_attr_cal_gyro_left.attr,
1948995887a1SDerek J. Clark 	&dev_attr_cal_gyro_left_index.attr,
1949995887a1SDerek J. Clark 	&dev_attr_cal_gyro_left_status.attr,
1950995887a1SDerek J. Clark 	&dev_attr_cal_joy_left.attr,
1951995887a1SDerek J. Clark 	&dev_attr_cal_joy_left_index.attr,
1952995887a1SDerek J. Clark 	&dev_attr_cal_joy_left_status.attr,
1953995887a1SDerek J. Clark 	&dev_attr_cal_trigg_left.attr,
1954995887a1SDerek J. Clark 	&dev_attr_cal_trigg_left_index.attr,
1955995887a1SDerek J. Clark 	&dev_attr_cal_trigg_left_status.attr,
195682cd9bc8SDerek J. Clark 	&dev_attr_imu_bypass_left.attr,
195782cd9bc8SDerek J. Clark 	&dev_attr_imu_bypass_left_index.attr,
195882cd9bc8SDerek J. Clark 	&dev_attr_imu_enabled_left.attr,
195982cd9bc8SDerek J. Clark 	&dev_attr_imu_enabled_left_index.attr,
196082cd9bc8SDerek J. Clark 	&dev_attr_reset_left.attr,
196196b20c1fSDerek J. Clark 	&dev_attr_rumble_mode_left.attr,
196296b20c1fSDerek J. Clark 	&dev_attr_rumble_mode_left_index.attr,
196396b20c1fSDerek J. Clark 	&dev_attr_rumble_notification_left.attr,
196496b20c1fSDerek J. Clark 	&dev_attr_rumble_notification_left_index.attr,
1965d69ccfcbSDerek J. Clark 	&dev_attr_version_hardware_left.attr,
1966d69ccfcbSDerek J. Clark 	&dev_attr_version_firmware_left.attr,
1967d69ccfcbSDerek J. Clark 	&dev_attr_version_gen_left.attr,
1968d69ccfcbSDerek J. Clark 	&dev_attr_version_product_left.attr,
1969d69ccfcbSDerek J. Clark 	&dev_attr_version_protocol_left.attr,
1970d69ccfcbSDerek J. Clark 	NULL,
1971d69ccfcbSDerek J. Clark };
1972d69ccfcbSDerek J. Clark 
1973d69ccfcbSDerek J. Clark static const struct attribute_group left_gamepad_attr_group = {
1974d69ccfcbSDerek J. Clark 	.name = "left_handle",
1975d69ccfcbSDerek J. Clark 	.attrs = left_gamepad_attrs,
1976d69ccfcbSDerek J. Clark };
1977d69ccfcbSDerek J. Clark 
1978d69ccfcbSDerek J. Clark /* Gamepad - Right */
1979d69ccfcbSDerek J. Clark static struct go_cfg_attr version_product_right = { PRODUCT_VERSION };
1980d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_product_right, "product_version", RIGHT_CONTROLLER, version);
1981d69ccfcbSDerek J. Clark 
1982d69ccfcbSDerek J. Clark static struct go_cfg_attr version_protocol_right = { PROTOCOL_VERSION };
1983d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_protocol_right, "protocol_version", RIGHT_CONTROLLER, version);
1984d69ccfcbSDerek J. Clark 
1985d69ccfcbSDerek J. Clark static struct go_cfg_attr version_firmware_right = { FIRMWARE_VERSION };
1986d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_firmware_right, "firmware_version", RIGHT_CONTROLLER, version);
1987d69ccfcbSDerek J. Clark 
1988d69ccfcbSDerek J. Clark static struct go_cfg_attr version_hardware_right = { HARDWARE_VERSION };
1989d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_hardware_right, "hardware_version", RIGHT_CONTROLLER, version);
1990d69ccfcbSDerek J. Clark 
1991d69ccfcbSDerek J. Clark static struct go_cfg_attr version_gen_right = { HARDWARE_GENERATION };
1992d69ccfcbSDerek J. Clark LEGO_DEVICE_ATTR_RO(version_gen_right, "hardware_generation", RIGHT_CONTROLLER, version);
1993d69ccfcbSDerek J. Clark 
199482cd9bc8SDerek J. Clark static struct go_cfg_attr auto_sleep_time_right = { FEATURE_AUTO_SLEEP_TIME };
199582cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(auto_sleep_time_right, "auto_sleep_time", RIGHT_CONTROLLER,
199682cd9bc8SDerek J. Clark 		    range, feature_status);
199782cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(auto_sleep_time_right_range,
199882cd9bc8SDerek J. Clark 			    "auto_sleep_time_range");
199982cd9bc8SDerek J. Clark 
200082cd9bc8SDerek J. Clark static struct go_cfg_attr imu_bypass_right = { FEATURE_IMU_BYPASS };
200182cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(imu_bypass_right, "imu_bypass_enabled", RIGHT_CONTROLLER,
200282cd9bc8SDerek J. Clark 		    index, feature_status);
200382cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(imu_bypass_right_index, "imu_bypass_enabled_index");
200482cd9bc8SDerek J. Clark 
200582cd9bc8SDerek J. Clark static struct go_cfg_attr imu_enabled_right = { FEATURE_IMU_BYPASS };
200682cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(imu_enabled_right, "imu_enabled", RIGHT_CONTROLLER, index,
200782cd9bc8SDerek J. Clark 		    feature_status);
200882cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(imu_enabled_right_index, "imu_enabled_index");
200982cd9bc8SDerek J. Clark 
201082cd9bc8SDerek J. Clark static struct go_cfg_attr reset_right = { FEATURE_RESET_GAMEPAD };
201182cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_WO(reset_right, "reset", LEFT_CONTROLLER, feature_status);
201282cd9bc8SDerek J. Clark 
201396b20c1fSDerek J. Clark static struct go_cfg_attr rumble_mode_right = { RUMBLE_MODE };
201496b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(rumble_mode_right, "rumble_mode", RIGHT_CONTROLLER, index,
201596b20c1fSDerek J. Clark 		    motor_config);
201696b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rumble_mode_right_index, "rumble_mode_index");
201796b20c1fSDerek J. Clark 
201896b20c1fSDerek J. Clark static struct go_cfg_attr rumble_notification_right = { VIBRATION_NOTIFY_ENABLE };
201996b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(rumble_notification_right, "rumble_notification",
202096b20c1fSDerek J. Clark 		    RIGHT_CONTROLLER, index, motor_config);
202196b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rumble_notification_right_index,
202296b20c1fSDerek J. Clark 			    "rumble_notification_index");
202396b20c1fSDerek J. Clark 
2024995887a1SDerek J. Clark static struct go_cfg_attr cal_trigg_right = { TRIGGER_CALIBRATE };
2025995887a1SDerek J. Clark LEGO_CAL_DEVICE_ATTR(cal_trigg_right, "calibrate_trigger", SET_TRIGGER_CFG,
2026995887a1SDerek J. Clark 		     RIGHT_CONTROLLER, index);
2027995887a1SDerek J. Clark static DEVICE_ATTR_RO_NAMED(cal_trigg_right_index, "calibrate_trigger_index");
2028995887a1SDerek J. Clark 
2029995887a1SDerek J. Clark static struct go_cfg_attr cal_joy_right = { JOYSTICK_CALIBRATE };
2030995887a1SDerek J. Clark LEGO_CAL_DEVICE_ATTR(cal_joy_right, "calibrate_joystick", SET_JOYSTICK_CFG,
2031995887a1SDerek J. Clark 		     RIGHT_CONTROLLER, index);
2032995887a1SDerek J. Clark static DEVICE_ATTR_RO_NAMED(cal_joy_right_index, "calibrate_joystick_index");
2033995887a1SDerek J. Clark 
2034995887a1SDerek J. Clark static struct go_cfg_attr cal_gyro_right = { GYRO_CALIBRATE };
2035995887a1SDerek J. Clark LEGO_CAL_DEVICE_ATTR(cal_gyro_right, "calibrate_gyro", SET_GYRO_CFG,
2036995887a1SDerek J. Clark 		     RIGHT_CONTROLLER, index);
2037995887a1SDerek J. Clark static DEVICE_ATTR_RO_NAMED(cal_gyro_right_index, "calibrate_gyro_index");
2038995887a1SDerek J. Clark 
2039995887a1SDerek J. Clark static struct go_cfg_attr cal_trigg_right_status = { GET_CAL_STATUS };
2040995887a1SDerek J. Clark LEGO_DEVICE_STATUS_ATTR(cal_trigg_right_status, "calibrate_trigger_status",
2041995887a1SDerek J. Clark 			RIGHT_CONTROLLER, CALDEV_TRIGGER);
2042995887a1SDerek J. Clark 
2043995887a1SDerek J. Clark static struct go_cfg_attr cal_joy_right_status = { GET_CAL_STATUS };
2044995887a1SDerek J. Clark LEGO_DEVICE_STATUS_ATTR(cal_joy_right_status, "calibrate_joystick_status",
2045995887a1SDerek J. Clark 			RIGHT_CONTROLLER, CALDEV_JOYSTICK);
2046995887a1SDerek J. Clark 
2047995887a1SDerek J. Clark static struct go_cfg_attr cal_gyro_right_status = { GET_CAL_STATUS };
2048995887a1SDerek J. Clark LEGO_DEVICE_STATUS_ATTR(cal_gyro_right_status, "calibrate_gyro_status",
2049995887a1SDerek J. Clark 			RIGHT_CONTROLLER, CALDEV_GYROSCOPE);
2050995887a1SDerek J. Clark 
2051d69ccfcbSDerek J. Clark static struct attribute *right_gamepad_attrs[] = {
205282cd9bc8SDerek J. Clark 	&dev_attr_auto_sleep_time_right.attr,
205382cd9bc8SDerek J. Clark 	&dev_attr_auto_sleep_time_right_range.attr,
2054995887a1SDerek J. Clark 	&dev_attr_cal_gyro_right.attr,
2055995887a1SDerek J. Clark 	&dev_attr_cal_gyro_right_index.attr,
2056995887a1SDerek J. Clark 	&dev_attr_cal_gyro_right_status.attr,
2057995887a1SDerek J. Clark 	&dev_attr_cal_joy_right.attr,
2058995887a1SDerek J. Clark 	&dev_attr_cal_joy_right_index.attr,
2059995887a1SDerek J. Clark 	&dev_attr_cal_joy_right_status.attr,
2060995887a1SDerek J. Clark 	&dev_attr_cal_trigg_right.attr,
2061995887a1SDerek J. Clark 	&dev_attr_cal_trigg_right_index.attr,
2062995887a1SDerek J. Clark 	&dev_attr_cal_trigg_right_status.attr,
206382cd9bc8SDerek J. Clark 	&dev_attr_imu_bypass_right.attr,
206482cd9bc8SDerek J. Clark 	&dev_attr_imu_bypass_right_index.attr,
206582cd9bc8SDerek J. Clark 	&dev_attr_imu_enabled_right.attr,
206682cd9bc8SDerek J. Clark 	&dev_attr_imu_enabled_right_index.attr,
206782cd9bc8SDerek J. Clark 	&dev_attr_reset_right.attr,
206896b20c1fSDerek J. Clark 	&dev_attr_rumble_mode_right.attr,
206996b20c1fSDerek J. Clark 	&dev_attr_rumble_mode_right_index.attr,
207096b20c1fSDerek J. Clark 	&dev_attr_rumble_notification_right.attr,
207196b20c1fSDerek J. Clark 	&dev_attr_rumble_notification_right_index.attr,
2072d69ccfcbSDerek J. Clark 	&dev_attr_version_hardware_right.attr,
2073d69ccfcbSDerek J. Clark 	&dev_attr_version_firmware_right.attr,
2074d69ccfcbSDerek J. Clark 	&dev_attr_version_gen_right.attr,
2075d69ccfcbSDerek J. Clark 	&dev_attr_version_product_right.attr,
2076d69ccfcbSDerek J. Clark 	&dev_attr_version_protocol_right.attr,
2077d69ccfcbSDerek J. Clark 	NULL,
2078d69ccfcbSDerek J. Clark };
2079d69ccfcbSDerek J. Clark 
2080d69ccfcbSDerek J. Clark static const struct attribute_group right_gamepad_attr_group = {
2081d69ccfcbSDerek J. Clark 	.name = "right_handle",
2082d69ccfcbSDerek J. Clark 	.attrs = right_gamepad_attrs,
2083d69ccfcbSDerek J. Clark };
2084d69ccfcbSDerek J. Clark 
2085d69ccfcbSDerek J. Clark /* Touchpad */
208682cd9bc8SDerek J. Clark static struct go_cfg_attr touchpad_enabled = { FEATURE_TOUCHPAD_ENABLE };
208782cd9bc8SDerek J. Clark LEGO_DEVICE_ATTR_RW(touchpad_enabled, "enabled", UNSPECIFIED, index,
208882cd9bc8SDerek J. Clark 		    feature_status);
208982cd9bc8SDerek J. Clark static DEVICE_ATTR_RO_NAMED(touchpad_enabled_index, "enabled_index");
209082cd9bc8SDerek J. Clark 
209196b20c1fSDerek J. Clark static struct go_cfg_attr touchpad_vibration_enabled = { TP_VIBRATION_ENABLE };
209296b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(touchpad_vibration_enabled, "vibration_enabled", UNSPECIFIED,
209396b20c1fSDerek J. Clark 		    index, motor_config);
209496b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(touchpad_vibration_enabled_index,
209596b20c1fSDerek J. Clark 			    "vibration_enabled_index");
209696b20c1fSDerek J. Clark 
209796b20c1fSDerek J. Clark static struct go_cfg_attr touchpad_vibration_intensity = { TP_VIBRATION_INTENSITY };
209896b20c1fSDerek J. Clark LEGO_DEVICE_ATTR_RW(touchpad_vibration_intensity, "vibration_intensity",
209996b20c1fSDerek J. Clark 		    UNSPECIFIED, index, motor_config);
210096b20c1fSDerek J. Clark static DEVICE_ATTR_RO_NAMED(touchpad_vibration_intensity_index,
210196b20c1fSDerek J. Clark 			    "vibration_intensity_index");
210296b20c1fSDerek J. Clark 
2103d69ccfcbSDerek J. Clark static struct attribute *touchpad_attrs[] = {
210482cd9bc8SDerek J. Clark 	&dev_attr_touchpad_enabled.attr,
210582cd9bc8SDerek J. Clark 	&dev_attr_touchpad_enabled_index.attr,
210696b20c1fSDerek J. Clark 	&dev_attr_touchpad_vibration_enabled.attr,
210796b20c1fSDerek J. Clark 	&dev_attr_touchpad_vibration_enabled_index.attr,
210896b20c1fSDerek J. Clark 	&dev_attr_touchpad_vibration_intensity.attr,
210996b20c1fSDerek J. Clark 	&dev_attr_touchpad_vibration_intensity_index.attr,
211096b20c1fSDerek J. Clark 	NULL,
2111d69ccfcbSDerek J. Clark };
2112d69ccfcbSDerek J. Clark 
2113d69ccfcbSDerek J. Clark static const struct attribute_group touchpad_attr_group = {
2114d69ccfcbSDerek J. Clark 	.name = "touchpad",
2115d69ccfcbSDerek J. Clark 	.attrs = touchpad_attrs,
2116d69ccfcbSDerek J. Clark };
2117d69ccfcbSDerek J. Clark 
2118d69ccfcbSDerek J. Clark static const struct attribute_group *top_level_attr_groups[] = {
2119d69ccfcbSDerek J. Clark 	&mcu_attr_group,	  &tx_dongle_attr_group,
2120d69ccfcbSDerek J. Clark 	&left_gamepad_attr_group, &right_gamepad_attr_group,
2121d69ccfcbSDerek J. Clark 	&touchpad_attr_group,	  NULL,
2122d69ccfcbSDerek J. Clark };
2123d69ccfcbSDerek J. Clark 
2124325262faSDerek J. Clark /* RGB */
2125325262faSDerek J. Clark static struct go_cfg_attr rgb_enabled = { FEATURE_LIGHT_ENABLE };
2126325262faSDerek J. Clark 
2127325262faSDerek J. Clark LEGO_DEVICE_ATTR_RW(rgb_enabled, "enabled", UNSPECIFIED, index, feature_status);
2128325262faSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rgb_effect_index, "effect_index");
2129325262faSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rgb_enabled_index, "enabled_index");
2130325262faSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rgb_mode_index, "mode_index");
2131325262faSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rgb_profile_range, "profile_range");
2132325262faSDerek J. Clark static DEVICE_ATTR_RO_NAMED(rgb_speed_range, "speed_range");
2133325262faSDerek J. Clark static DEVICE_ATTR_RW_NAMED(rgb_effect, "effect");
2134325262faSDerek J. Clark static DEVICE_ATTR_RW_NAMED(rgb_mode, "mode");
2135325262faSDerek J. Clark static DEVICE_ATTR_RW_NAMED(rgb_profile, "profile");
2136325262faSDerek J. Clark static DEVICE_ATTR_RW_NAMED(rgb_speed, "speed");
2137325262faSDerek J. Clark 
2138325262faSDerek J. Clark static struct attribute *go_rgb_attrs[] = {
2139325262faSDerek J. Clark 	&dev_attr_rgb_effect.attr,
2140325262faSDerek J. Clark 	&dev_attr_rgb_effect_index.attr,
2141325262faSDerek J. Clark 	&dev_attr_rgb_enabled.attr,
2142325262faSDerek J. Clark 	&dev_attr_rgb_enabled_index.attr,
2143325262faSDerek J. Clark 	&dev_attr_rgb_mode.attr,
2144325262faSDerek J. Clark 	&dev_attr_rgb_mode_index.attr,
2145325262faSDerek J. Clark 	&dev_attr_rgb_profile.attr,
2146325262faSDerek J. Clark 	&dev_attr_rgb_profile_range.attr,
2147325262faSDerek J. Clark 	&dev_attr_rgb_speed.attr,
2148325262faSDerek J. Clark 	&dev_attr_rgb_speed_range.attr,
2149325262faSDerek J. Clark 	NULL,
2150325262faSDerek J. Clark };
2151325262faSDerek J. Clark 
2152325262faSDerek J. Clark static struct attribute_group rgb_attr_group = {
2153325262faSDerek J. Clark 	.attrs = go_rgb_attrs,
2154325262faSDerek J. Clark };
2155325262faSDerek J. Clark 
2156325262faSDerek J. Clark static struct mc_subled go_rgb_subled_info[] = {
2157325262faSDerek J. Clark 	{
2158325262faSDerek J. Clark 		.color_index = LED_COLOR_ID_RED,
2159325262faSDerek J. Clark 		.brightness = 0x50,
2160325262faSDerek J. Clark 		.intensity = 0x24,
2161325262faSDerek J. Clark 		.channel = 0x1,
2162325262faSDerek J. Clark 	},
2163325262faSDerek J. Clark 	{
2164325262faSDerek J. Clark 		.color_index = LED_COLOR_ID_GREEN,
2165325262faSDerek J. Clark 		.brightness = 0x50,
2166325262faSDerek J. Clark 		.intensity = 0x22,
2167325262faSDerek J. Clark 		.channel = 0x2,
2168325262faSDerek J. Clark 	},
2169325262faSDerek J. Clark 	{
2170325262faSDerek J. Clark 		.color_index = LED_COLOR_ID_BLUE,
2171325262faSDerek J. Clark 		.brightness = 0x50,
2172325262faSDerek J. Clark 		.intensity = 0x99,
2173325262faSDerek J. Clark 		.channel = 0x3,
2174325262faSDerek J. Clark 	},
2175325262faSDerek J. Clark };
2176325262faSDerek J. Clark 
2177325262faSDerek J. Clark static struct led_classdev_mc go_cdev_rgb = {
2178325262faSDerek J. Clark 	.led_cdev = {
2179325262faSDerek J. Clark 		.name = "go:rgb:joystick_rings",
2180325262faSDerek J. Clark 		.color = LED_COLOR_ID_RGB,
2181325262faSDerek J. Clark 		.brightness = 0x50,
2182325262faSDerek J. Clark 		.max_brightness = 0x64,
2183325262faSDerek J. Clark 		.brightness_set = hid_go_brightness_set,
2184325262faSDerek J. Clark 	},
2185325262faSDerek J. Clark 	.num_colors = ARRAY_SIZE(go_rgb_subled_info),
2186325262faSDerek J. Clark 	.subled_info = go_rgb_subled_info,
2187325262faSDerek J. Clark };
2188325262faSDerek J. Clark 
2189d69ccfcbSDerek J. Clark static void cfg_setup(struct work_struct *work)
2190d69ccfcbSDerek J. Clark {
2191d69ccfcbSDerek J. Clark 	int ret;
2192d69ccfcbSDerek J. Clark 
2193d69ccfcbSDerek J. Clark 	/* MCU Version Attrs */
2194d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2195d69ccfcbSDerek J. Clark 			       PRODUCT_VERSION, USB_MCU, NULL, 0);
2196d69ccfcbSDerek J. Clark 	if (ret < 0) {
2197d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2198d69ccfcbSDerek J. Clark 			"Failed to retrieve USB_MCU Product Version: %i\n", ret);
2199d69ccfcbSDerek J. Clark 		return;
2200d69ccfcbSDerek J. Clark 	}
2201d69ccfcbSDerek J. Clark 
2202d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2203d69ccfcbSDerek J. Clark 			       PROTOCOL_VERSION, USB_MCU, NULL, 0);
2204d69ccfcbSDerek J. Clark 	if (ret < 0) {
2205d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2206d69ccfcbSDerek J. Clark 			"Failed to retrieve USB_MCU Protocol Version: %i\n", ret);
2207d69ccfcbSDerek J. Clark 		return;
2208d69ccfcbSDerek J. Clark 	}
2209d69ccfcbSDerek J. Clark 
2210d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2211d69ccfcbSDerek J. Clark 			       FIRMWARE_VERSION, USB_MCU, NULL, 0);
2212d69ccfcbSDerek J. Clark 	if (ret < 0) {
2213d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2214d69ccfcbSDerek J. Clark 			"Failed to retrieve USB_MCU Firmware Version: %i\n", ret);
2215d69ccfcbSDerek J. Clark 		return;
2216d69ccfcbSDerek J. Clark 	}
2217d69ccfcbSDerek J. Clark 
2218d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2219d69ccfcbSDerek J. Clark 			       HARDWARE_VERSION, USB_MCU, NULL, 0);
2220d69ccfcbSDerek J. Clark 	if (ret < 0) {
2221d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2222d69ccfcbSDerek J. Clark 			"Failed to retrieve USB_MCU Hardware Version: %i\n", ret);
2223d69ccfcbSDerek J. Clark 		return;
2224d69ccfcbSDerek J. Clark 	}
2225d69ccfcbSDerek J. Clark 
2226d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2227d69ccfcbSDerek J. Clark 			       HARDWARE_GENERATION, USB_MCU, NULL, 0);
2228d69ccfcbSDerek J. Clark 	if (ret < 0) {
2229d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2230d69ccfcbSDerek J. Clark 			"Failed to retrieve USB_MCU Hardware Generation: %i\n", ret);
2231d69ccfcbSDerek J. Clark 		return;
2232d69ccfcbSDerek J. Clark 	}
2233d69ccfcbSDerek J. Clark 
2234d69ccfcbSDerek J. Clark 	/* TX Dongle Version Attrs */
2235d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2236d69ccfcbSDerek J. Clark 			       PRODUCT_VERSION, TX_DONGLE, NULL, 0);
2237d69ccfcbSDerek J. Clark 	if (ret < 0) {
2238d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2239d69ccfcbSDerek J. Clark 			"Failed to retrieve TX_DONGLE Product Version: %i\n", ret);
2240d69ccfcbSDerek J. Clark 		return;
2241d69ccfcbSDerek J. Clark 	}
2242d69ccfcbSDerek J. Clark 
2243d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2244d69ccfcbSDerek J. Clark 			       PROTOCOL_VERSION, TX_DONGLE, NULL, 0);
2245d69ccfcbSDerek J. Clark 	if (ret < 0) {
2246d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2247d69ccfcbSDerek J. Clark 			"Failed to retrieve TX_DONGLE Protocol Version: %i\n", ret);
2248d69ccfcbSDerek J. Clark 		return;
2249d69ccfcbSDerek J. Clark 	}
2250d69ccfcbSDerek J. Clark 
2251d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2252d69ccfcbSDerek J. Clark 			       FIRMWARE_VERSION, TX_DONGLE, NULL, 0);
2253d69ccfcbSDerek J. Clark 	if (ret < 0) {
2254d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2255d69ccfcbSDerek J. Clark 			"Failed to retrieve TX_DONGLE Firmware Version: %i\n", ret);
2256d69ccfcbSDerek J. Clark 		return;
2257d69ccfcbSDerek J. Clark 	}
2258d69ccfcbSDerek J. Clark 
2259d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2260d69ccfcbSDerek J. Clark 			       HARDWARE_VERSION, TX_DONGLE, NULL, 0);
2261d69ccfcbSDerek J. Clark 	if (ret < 0) {
2262d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2263d69ccfcbSDerek J. Clark 			"Failed to retrieve TX_DONGLE Hardware Version: %i\n", ret);
2264d69ccfcbSDerek J. Clark 		return;
2265d69ccfcbSDerek J. Clark 	}
2266d69ccfcbSDerek J. Clark 
2267d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2268d69ccfcbSDerek J. Clark 			       HARDWARE_GENERATION, TX_DONGLE, NULL, 0);
2269d69ccfcbSDerek J. Clark 	if (ret < 0) {
2270d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2271d69ccfcbSDerek J. Clark 			"Failed to retrieve TX_DONGLE Hardware Generation: %i\n", ret);
2272d69ccfcbSDerek J. Clark 		return;
2273d69ccfcbSDerek J. Clark 	}
2274d69ccfcbSDerek J. Clark 
2275d69ccfcbSDerek J. Clark 	/* Left Handle Version Attrs */
2276d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2277d69ccfcbSDerek J. Clark 			       PRODUCT_VERSION, LEFT_CONTROLLER, NULL, 0);
2278d69ccfcbSDerek J. Clark 	if (ret < 0) {
2279d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2280d69ccfcbSDerek J. Clark 			"Failed to retrieve LEFT_CONTROLLER Product Version: %i\n", ret);
2281d69ccfcbSDerek J. Clark 		return;
2282d69ccfcbSDerek J. Clark 	}
2283d69ccfcbSDerek J. Clark 
2284d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2285d69ccfcbSDerek J. Clark 			       PROTOCOL_VERSION, LEFT_CONTROLLER, NULL, 0);
2286d69ccfcbSDerek J. Clark 	if (ret < 0) {
2287d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2288d69ccfcbSDerek J. Clark 			"Failed to retrieve LEFT_CONTROLLER Protocol Version: %i\n", ret);
2289d69ccfcbSDerek J. Clark 		return;
2290d69ccfcbSDerek J. Clark 	}
2291d69ccfcbSDerek J. Clark 
2292d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2293d69ccfcbSDerek J. Clark 			       FIRMWARE_VERSION, LEFT_CONTROLLER, NULL, 0);
2294d69ccfcbSDerek J. Clark 	if (ret < 0) {
2295d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2296d69ccfcbSDerek J. Clark 			"Failed to retrieve LEFT_CONTROLLER Firmware Version: %i\n", ret);
2297d69ccfcbSDerek J. Clark 		return;
2298d69ccfcbSDerek J. Clark 	}
2299d69ccfcbSDerek J. Clark 
2300d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2301d69ccfcbSDerek J. Clark 			       HARDWARE_VERSION, LEFT_CONTROLLER, NULL, 0);
2302d69ccfcbSDerek J. Clark 	if (ret < 0) {
2303d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2304d69ccfcbSDerek J. Clark 			"Failed to retrieve LEFT_CONTROLLER Hardware Version: %i\n", ret);
2305d69ccfcbSDerek J. Clark 		return;
2306d69ccfcbSDerek J. Clark 	}
2307d69ccfcbSDerek J. Clark 
2308d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2309d69ccfcbSDerek J. Clark 			       HARDWARE_GENERATION, LEFT_CONTROLLER, NULL, 0);
2310d69ccfcbSDerek J. Clark 	if (ret < 0) {
2311d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2312d69ccfcbSDerek J. Clark 			"Failed to retrieve LEFT_CONTROLLER Hardware Generation: %i\n", ret);
2313d69ccfcbSDerek J. Clark 		return;
2314d69ccfcbSDerek J. Clark 	}
2315d69ccfcbSDerek J. Clark 
2316d69ccfcbSDerek J. Clark 	/* Right Handle Version Attrs */
2317d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2318d69ccfcbSDerek J. Clark 			       PRODUCT_VERSION, RIGHT_CONTROLLER, NULL, 0);
2319d69ccfcbSDerek J. Clark 	if (ret < 0) {
2320d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2321d69ccfcbSDerek J. Clark 			"Failed to retrieve RIGHT_CONTROLLER Product Version: %i\n", ret);
2322d69ccfcbSDerek J. Clark 		return;
2323d69ccfcbSDerek J. Clark 	}
2324d69ccfcbSDerek J. Clark 
2325d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2326d69ccfcbSDerek J. Clark 			       PROTOCOL_VERSION, RIGHT_CONTROLLER, NULL, 0);
2327d69ccfcbSDerek J. Clark 	if (ret < 0) {
2328d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2329d69ccfcbSDerek J. Clark 			"Failed to retrieve RIGHT_CONTROLLER Protocol Version: %i\n", ret);
2330d69ccfcbSDerek J. Clark 		return;
2331d69ccfcbSDerek J. Clark 	}
2332d69ccfcbSDerek J. Clark 
2333d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2334d69ccfcbSDerek J. Clark 			       FIRMWARE_VERSION, RIGHT_CONTROLLER, NULL, 0);
2335d69ccfcbSDerek J. Clark 	if (ret < 0) {
2336d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2337d69ccfcbSDerek J. Clark 			"Failed to retrieve RIGHT_CONTROLLER Firmware Version: %i\n", ret);
2338d69ccfcbSDerek J. Clark 		return;
2339d69ccfcbSDerek J. Clark 	}
2340d69ccfcbSDerek J. Clark 
2341d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2342d69ccfcbSDerek J. Clark 			       HARDWARE_VERSION, RIGHT_CONTROLLER, NULL, 0);
2343d69ccfcbSDerek J. Clark 	if (ret < 0) {
2344d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2345d69ccfcbSDerek J. Clark 			"Failed to retrieve RIGHT_CONTROLLER Hardware Version: %i\n", ret);
2346d69ccfcbSDerek J. Clark 		return;
2347d69ccfcbSDerek J. Clark 	}
2348d69ccfcbSDerek J. Clark 
2349d69ccfcbSDerek J. Clark 	ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,
2350d69ccfcbSDerek J. Clark 			       HARDWARE_GENERATION, RIGHT_CONTROLLER, NULL, 0);
2351d69ccfcbSDerek J. Clark 	if (ret < 0) {
2352d69ccfcbSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
2353d69ccfcbSDerek J. Clark 			"Failed to retrieve RIGHT_CONTROLLER Hardware Generation: %i\n", ret);
2354d69ccfcbSDerek J. Clark 		return;
2355d69ccfcbSDerek J. Clark 	}
2356d69ccfcbSDerek J. Clark }
2357d69ccfcbSDerek J. Clark 
2358d69ccfcbSDerek J. Clark static int hid_go_cfg_probe(struct hid_device *hdev,
2359d69ccfcbSDerek J. Clark 			    const struct hid_device_id *_id)
2360d69ccfcbSDerek J. Clark {
2361d69ccfcbSDerek J. Clark 	unsigned char *buf;
2362d69ccfcbSDerek J. Clark 	int ret;
2363d69ccfcbSDerek J. Clark 
2364d69ccfcbSDerek J. Clark 	buf = devm_kzalloc(&hdev->dev, GO_PACKET_SIZE, GFP_KERNEL);
2365d69ccfcbSDerek J. Clark 	if (!buf)
2366d69ccfcbSDerek J. Clark 		return -ENOMEM;
2367d69ccfcbSDerek J. Clark 
2368d69ccfcbSDerek J. Clark 	hid_set_drvdata(hdev, &drvdata);
2369d69ccfcbSDerek J. Clark 	drvdata.hdev = hdev;
2370d69ccfcbSDerek J. Clark 	mutex_init(&drvdata.cfg_mutex);
2371d69ccfcbSDerek J. Clark 
2372d69ccfcbSDerek J. Clark 	ret = sysfs_create_groups(&hdev->dev.kobj, top_level_attr_groups);
2373d69ccfcbSDerek J. Clark 	if (ret) {
2374d69ccfcbSDerek J. Clark 		dev_err_probe(&hdev->dev, ret,
2375d69ccfcbSDerek J. Clark 			      "Failed to create gamepad configuration attributes\n");
2376d69ccfcbSDerek J. Clark 		return ret;
2377d69ccfcbSDerek J. Clark 	}
2378d69ccfcbSDerek J. Clark 
2379325262faSDerek J. Clark 	ret = devm_led_classdev_multicolor_register(&hdev->dev, &go_cdev_rgb);
2380325262faSDerek J. Clark 	if (ret) {
2381325262faSDerek J. Clark 		dev_err_probe(&hdev->dev, ret, "Failed to create RGB device\n");
2382325262faSDerek J. Clark 		return ret;
2383325262faSDerek J. Clark 	}
2384325262faSDerek J. Clark 
2385325262faSDerek J. Clark 	ret = devm_device_add_group(go_cdev_rgb.led_cdev.dev, &rgb_attr_group);
2386325262faSDerek J. Clark 	if (ret) {
2387325262faSDerek J. Clark 		dev_err_probe(&hdev->dev, ret,
2388325262faSDerek J. Clark 			      "Failed to create RGB configuration attributes\n");
2389325262faSDerek J. Clark 		return ret;
2390325262faSDerek J. Clark 	}
2391325262faSDerek J. Clark 
2392325262faSDerek J. Clark 	drvdata.led_cdev = &go_cdev_rgb.led_cdev;
2393325262faSDerek J. Clark 
2394d69ccfcbSDerek J. Clark 	init_completion(&drvdata.send_cmd_complete);
2395d69ccfcbSDerek J. Clark 
2396d69ccfcbSDerek J. Clark 	/* Executing calls prior to returning from probe will lock the MCU. Schedule
2397d69ccfcbSDerek J. Clark 	 * initial data call after probe has completed and MCU can accept calls.
2398d69ccfcbSDerek J. Clark 	 */
2399d69ccfcbSDerek J. Clark 	INIT_DELAYED_WORK(&drvdata.go_cfg_setup, &cfg_setup);
2400d69ccfcbSDerek J. Clark 	ret = schedule_delayed_work(&drvdata.go_cfg_setup, msecs_to_jiffies(2));
2401d69ccfcbSDerek J. Clark 	if (!ret) {
2402d69ccfcbSDerek J. Clark 		dev_err(&hdev->dev,
2403d69ccfcbSDerek J. Clark 			"Failed to schedule startup delayed work\n");
2404d69ccfcbSDerek J. Clark 		return -ENODEV;
2405d69ccfcbSDerek J. Clark 	}
2406d69ccfcbSDerek J. Clark 	return 0;
2407d69ccfcbSDerek J. Clark }
2408d69ccfcbSDerek J. Clark 
2409d69ccfcbSDerek J. Clark static void hid_go_cfg_remove(struct hid_device *hdev)
2410d69ccfcbSDerek J. Clark {
2411d69ccfcbSDerek J. Clark 	guard(mutex)(&drvdata.cfg_mutex);
2412d69ccfcbSDerek J. Clark 	sysfs_remove_groups(&hdev->dev.kobj, top_level_attr_groups);
2413d69ccfcbSDerek J. Clark 	hid_hw_close(hdev);
2414d69ccfcbSDerek J. Clark 	hid_hw_stop(hdev);
2415d69ccfcbSDerek J. Clark 	hid_set_drvdata(hdev, NULL);
2416d69ccfcbSDerek J. Clark }
2417d69ccfcbSDerek J. Clark 
2418d69ccfcbSDerek J. Clark static int hid_go_probe(struct hid_device *hdev, const struct hid_device_id *id)
2419d69ccfcbSDerek J. Clark {
2420d69ccfcbSDerek J. Clark 	int ret, ep;
2421d69ccfcbSDerek J. Clark 
2422d69ccfcbSDerek J. Clark 	hdev->quirks |= HID_QUIRK_INPUT_PER_APP | HID_QUIRK_MULTI_INPUT;
2423d69ccfcbSDerek J. Clark 
2424d69ccfcbSDerek J. Clark 	ret = hid_parse(hdev);
2425d69ccfcbSDerek J. Clark 	if (ret) {
2426d69ccfcbSDerek J. Clark 		hid_err(hdev, "Parse failed\n");
2427d69ccfcbSDerek J. Clark 		return ret;
2428d69ccfcbSDerek J. Clark 	}
2429d69ccfcbSDerek J. Clark 
2430d69ccfcbSDerek J. Clark 	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
2431d69ccfcbSDerek J. Clark 	if (ret) {
2432d69ccfcbSDerek J. Clark 		hid_err(hdev, "Failed to start HID device\n");
2433d69ccfcbSDerek J. Clark 		return ret;
2434d69ccfcbSDerek J. Clark 	}
2435d69ccfcbSDerek J. Clark 
2436d69ccfcbSDerek J. Clark 	ret = hid_hw_open(hdev);
2437d69ccfcbSDerek J. Clark 	if (ret) {
2438d69ccfcbSDerek J. Clark 		hid_err(hdev, "Failed to open HID device\n");
2439d69ccfcbSDerek J. Clark 		hid_hw_stop(hdev);
2440d69ccfcbSDerek J. Clark 		return ret;
2441d69ccfcbSDerek J. Clark 	}
2442d69ccfcbSDerek J. Clark 
2443d69ccfcbSDerek J. Clark 	ep = get_endpoint_address(hdev);
2444d69ccfcbSDerek J. Clark 	if (ep != GO_GP_INTF_IN) {
2445d69ccfcbSDerek J. Clark 		dev_dbg(&hdev->dev, "Started interface %x as generic HID device\n", ep);
2446d69ccfcbSDerek J. Clark 		return 0;
2447d69ccfcbSDerek J. Clark 	}
2448d69ccfcbSDerek J. Clark 
2449d69ccfcbSDerek J. Clark 	ret = hid_go_cfg_probe(hdev, id);
2450d69ccfcbSDerek J. Clark 	if (ret)
2451d69ccfcbSDerek J. Clark 		dev_err_probe(&hdev->dev, ret, "Failed to start configuration interface\n");
2452d69ccfcbSDerek J. Clark 
2453d69ccfcbSDerek J. Clark 	dev_dbg(&hdev->dev, "Started Legion Go HID Device: %x\n", ep);
2454d69ccfcbSDerek J. Clark 
2455d69ccfcbSDerek J. Clark 	return ret;
2456d69ccfcbSDerek J. Clark }
2457d69ccfcbSDerek J. Clark 
2458d69ccfcbSDerek J. Clark static void hid_go_remove(struct hid_device *hdev)
2459d69ccfcbSDerek J. Clark {
2460d69ccfcbSDerek J. Clark 	int ep = get_endpoint_address(hdev);
2461d69ccfcbSDerek J. Clark 
2462d69ccfcbSDerek J. Clark 	if (ep <= 0)
2463d69ccfcbSDerek J. Clark 		return;
2464d69ccfcbSDerek J. Clark 
2465d69ccfcbSDerek J. Clark 	switch (ep) {
2466d69ccfcbSDerek J. Clark 	case GO_GP_INTF_IN:
2467d69ccfcbSDerek J. Clark 		hid_go_cfg_remove(hdev);
2468d69ccfcbSDerek J. Clark 		break;
2469d69ccfcbSDerek J. Clark 	default:
2470d69ccfcbSDerek J. Clark 		hid_hw_close(hdev);
2471d69ccfcbSDerek J. Clark 		hid_hw_stop(hdev);
2472d69ccfcbSDerek J. Clark 		break;
2473d69ccfcbSDerek J. Clark 	}
2474d69ccfcbSDerek J. Clark }
2475d69ccfcbSDerek J. Clark 
2476d69ccfcbSDerek J. Clark static const struct hid_device_id hid_go_devices[] = {
2477d69ccfcbSDerek J. Clark 	{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO,
2478d69ccfcbSDerek J. Clark 			 USB_DEVICE_ID_LENOVO_LEGION_GO2_XINPUT) },
2479d69ccfcbSDerek J. Clark 	{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO,
2480d69ccfcbSDerek J. Clark 			 USB_DEVICE_ID_LENOVO_LEGION_GO2_DINPUT) },
2481d69ccfcbSDerek J. Clark 	{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO,
2482d69ccfcbSDerek J. Clark 			 USB_DEVICE_ID_LENOVO_LEGION_GO2_DUAL_DINPUT) },
2483d69ccfcbSDerek J. Clark 	{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO,
2484d69ccfcbSDerek J. Clark 			 USB_DEVICE_ID_LENOVO_LEGION_GO2_FPS) },
2485d69ccfcbSDerek J. Clark 	{}
2486d69ccfcbSDerek J. Clark };
2487d69ccfcbSDerek J. Clark MODULE_DEVICE_TABLE(hid, hid_go_devices);
2488d69ccfcbSDerek J. Clark 
2489d69ccfcbSDerek J. Clark static struct hid_driver hid_lenovo_go = {
2490d69ccfcbSDerek J. Clark 	.name = "hid-lenovo-go",
2491d69ccfcbSDerek J. Clark 	.id_table = hid_go_devices,
2492d69ccfcbSDerek J. Clark 	.probe = hid_go_probe,
2493d69ccfcbSDerek J. Clark 	.remove = hid_go_remove,
2494d69ccfcbSDerek J. Clark 	.raw_event = hid_go_raw_event,
2495d69ccfcbSDerek J. Clark };
2496d69ccfcbSDerek J. Clark module_hid_driver(hid_lenovo_go);
2497d69ccfcbSDerek J. Clark 
2498d69ccfcbSDerek J. Clark MODULE_AUTHOR("Derek J. Clark");
2499d69ccfcbSDerek J. Clark MODULE_DESCRIPTION("HID Driver for Lenovo Legion Go Series Gamepads.");
2500d69ccfcbSDerek J. Clark MODULE_LICENSE("GPL");
2501