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