xref: /linux/drivers/hid/hid-msi.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1a9517958SDerek J. Clark // SPDX-License-Identifier: GPL-2.0-or-later
2a9517958SDerek J. Clark /*
3a9517958SDerek J. Clark  *  HID driver for MSI Claw Handheld PC gamepads.
4a9517958SDerek J. Clark  *
5a9517958SDerek J. Clark  *  Provides configuration support for the MSI Claw series of handheld PC
6a9517958SDerek J. Clark  *  gamepads. Multiple iterations of the device firmware has led to some
7a9517958SDerek J. Clark  *  quirks for how certain attributes are handled. The original firmware
8a9517958SDerek J. Clark  *  did not support remapping of the M1 (right) and M2 (left) rear paddles.
9a9517958SDerek J. Clark  *  Additionally, the MCU RAM address for writing configuration data has
10a9517958SDerek J. Clark  *  changed twice. Checks are done during probe to enumerate these variances.
11a9517958SDerek J. Clark  *
12a9517958SDerek J. Clark  *  Copyright (c) 2026 Zhouwang Huang <honjow311@gmail.com>
13a9517958SDerek J. Clark  *  Copyright (c) 2026 Denis Benato <denis.benato@linux.dev>
14a9517958SDerek J. Clark  *  Copyright (c) 2026 Valve Corporation
15a9517958SDerek J. Clark  */
16a9517958SDerek J. Clark 
17a9517958SDerek J. Clark #include <linux/array_size.h>
18a9517958SDerek J. Clark #include <linux/cleanup.h>
19a9517958SDerek J. Clark #include <linux/completion.h>
20a9517958SDerek J. Clark #include <linux/container_of.h>
21a9517958SDerek J. Clark #include <linux/delay.h>
22a9517958SDerek J. Clark #include <linux/device.h>
23a9517958SDerek J. Clark #include <linux/hid.h>
24a9517958SDerek J. Clark #include <linux/kobject.h>
25f27e2e3cSDerek J. Clark #include <linux/led-class-multicolor.h>
26a9517958SDerek J. Clark #include <linux/leds.h>
27a9517958SDerek J. Clark #include <linux/module.h>
28a9517958SDerek J. Clark #include <linux/mutex.h>
29a9517958SDerek J. Clark #include <linux/pm.h>
30a9517958SDerek J. Clark #include <linux/spinlock.h>
31a9517958SDerek J. Clark #include <linux/sysfs.h>
32a9517958SDerek J. Clark #include <linux/types.h>
33a9517958SDerek J. Clark #include <linux/unaligned.h>
34a9517958SDerek J. Clark #include <linux/usb.h>
35a9517958SDerek J. Clark #include <linux/workqueue.h>
36a9517958SDerek J. Clark 
37a9517958SDerek J. Clark #include "hid-ids.h"
38a9517958SDerek J. Clark 
39a9517958SDerek J. Clark #define CLAW_OUTPUT_REPORT_ID	0x0f
40a9517958SDerek J. Clark #define CLAW_INPUT_REPORT_ID	0x10
41a9517958SDerek J. Clark 
42a9517958SDerek J. Clark #define CLAW_PACKET_SIZE	64
43a9517958SDerek J. Clark 
44a9517958SDerek J. Clark #define CLAW_DINPUT_CFG_INTF_IN	0x82
45a9517958SDerek J. Clark #define CLAW_XINPUT_CFG_INTF_IN	0x83
46a9517958SDerek J. Clark 
4793ff13edSDerek J. Clark #define CLAW_KEYS_MAX		5
4893ff13edSDerek J. Clark 
49f27e2e3cSDerek J. Clark #define CLAW_RGB_ZONES		9
50f27e2e3cSDerek J. Clark #define CLAW_RGB_MAX_FRAMES	8
51f27e2e3cSDerek J. Clark #define CLAW_RGB_FRAME_OFFSET	0x24
52f27e2e3cSDerek J. Clark 
53a9517958SDerek J. Clark enum claw_command_index {
54a9517958SDerek J. Clark 	CLAW_COMMAND_TYPE_NONE =			0x00,
55a9517958SDerek J. Clark 	CLAW_COMMAND_TYPE_READ_PROFILE =		0x04,
56a9517958SDerek J. Clark 	CLAW_COMMAND_TYPE_READ_PROFILE_ACK =		0x05,
57a9517958SDerek J. Clark 	CLAW_COMMAND_TYPE_ACK =				0x06,
58a9517958SDerek J. Clark 	CLAW_COMMAND_TYPE_WRITE_PROFILE_DATA =		0x21,
59a9517958SDerek J. Clark 	CLAW_COMMAND_TYPE_SYNC_TO_ROM =			0x22,
60a9517958SDerek J. Clark 	CLAW_COMMAND_TYPE_SWITCH_MODE =			0x24,
61a9517958SDerek J. Clark 	CLAW_COMMAND_TYPE_READ_GAMEPAD_MODE =		0x26,
62a9517958SDerek J. Clark 	CLAW_COMMAND_TYPE_GAMEPAD_MODE_ACK =		0x27,
63a9517958SDerek J. Clark 	CLAW_COMMAND_TYPE_RESET_DEVICE =		0x28,
64a9517958SDerek J. Clark };
65a9517958SDerek J. Clark 
66a9517958SDerek J. Clark enum claw_gamepad_mode_index {
67a9517958SDerek J. Clark 	CLAW_GAMEPAD_MODE_XINPUT =	0x01,
68a9517958SDerek J. Clark 	CLAW_GAMEPAD_MODE_DINPUT =	0x02,
69a9517958SDerek J. Clark 	CLAW_GAMEPAD_MODE_DESKTOP =	0x04,
70a9517958SDerek J. Clark };
71a9517958SDerek J. Clark 
72a9517958SDerek J. Clark static const char * const claw_gamepad_mode_text[] = {
73a9517958SDerek J. Clark 	[CLAW_GAMEPAD_MODE_XINPUT] =	"xinput",
74a9517958SDerek J. Clark 	[CLAW_GAMEPAD_MODE_DINPUT] =	"dinput",
75a9517958SDerek J. Clark 	[CLAW_GAMEPAD_MODE_DESKTOP] =	"desktop",
76a9517958SDerek J. Clark };
77a9517958SDerek J. Clark 
7893ff13edSDerek J. Clark enum claw_profile_ack_pending {
7993ff13edSDerek J. Clark 	CLAW_NO_PENDING,
8093ff13edSDerek J. Clark 	CLAW_M1_PENDING,
8193ff13edSDerek J. Clark 	CLAW_M2_PENDING,
82f27e2e3cSDerek J. Clark 	CLAW_RGB_PENDING,
83*95547c53SDerek J. Clark 	CLAW_RUMBLE_LEFT_PENDING,
84*95547c53SDerek J. Clark 	CLAW_RUMBLE_RIGHT_PENDING,
8593ff13edSDerek J. Clark };
8693ff13edSDerek J. Clark 
8793ff13edSDerek J. Clark enum claw_key_index {
8893ff13edSDerek J. Clark 	CLAW_KEY_M1,
8993ff13edSDerek J. Clark 	CLAW_KEY_M2,
9093ff13edSDerek J. Clark };
9193ff13edSDerek J. Clark 
92a9517958SDerek J. Clark enum claw_mkeys_function_index {
93a9517958SDerek J. Clark 	CLAW_MKEY_FUNCTION_MACRO,
94a9517958SDerek J. Clark 	CLAW_MKEY_FUNCTION_DISABLED,
95a9517958SDerek J. Clark 	CLAW_MKEY_FUNCTION_COMBO,
96a9517958SDerek J. Clark };
97a9517958SDerek J. Clark 
98a9517958SDerek J. Clark enum claw_mode_field {
99a9517958SDerek J. Clark 	CLAW_FIELD_GAMEPAD_MODE,
100a9517958SDerek J. Clark 	CLAW_FIELD_MKEYS_FUNCTION,
101a9517958SDerek J. Clark };
102a9517958SDerek J. Clark 
103a9517958SDerek J. Clark static const char * const claw_mkeys_function_text[] = {
104a9517958SDerek J. Clark 	[CLAW_MKEY_FUNCTION_MACRO] =	"macro",
105a9517958SDerek J. Clark 	[CLAW_MKEY_FUNCTION_DISABLED] =	"disabled",
106a9517958SDerek J. Clark 	[CLAW_MKEY_FUNCTION_COMBO] =	"combination",
107a9517958SDerek J. Clark };
108a9517958SDerek J. Clark 
10993ff13edSDerek J. Clark static const struct {
11093ff13edSDerek J. Clark 	u8 code;
11193ff13edSDerek J. Clark 	const char *name;
11293ff13edSDerek J. Clark } claw_button_mapping_key_map[] = {
11393ff13edSDerek J. Clark 	/* Gamepad buttons */
11493ff13edSDerek J. Clark 	{ 0x01, "ABS_HAT0Y_UP" },
11593ff13edSDerek J. Clark 	{ 0x02, "ABS_HAT0Y_DOWN" },
11693ff13edSDerek J. Clark 	{ 0x03, "ABS_HAT0X_LEFT" },
11793ff13edSDerek J. Clark 	{ 0x04, "ABS_HAT0X_RIGHT" },
11893ff13edSDerek J. Clark 	{ 0x05, "BTN_TL" },
11993ff13edSDerek J. Clark 	{ 0x06, "BTN_TR" },
12093ff13edSDerek J. Clark 	{ 0x07, "BTN_THUMBL" },
12193ff13edSDerek J. Clark 	{ 0x08, "BTN_THUMBR" },
12293ff13edSDerek J. Clark 	{ 0x09, "BTN_SOUTH" },
12393ff13edSDerek J. Clark 	{ 0x0a, "BTN_EAST" },
12493ff13edSDerek J. Clark 	{ 0x0b, "BTN_NORTH" },
12593ff13edSDerek J. Clark 	{ 0x0c, "BTN_WEST" },
12693ff13edSDerek J. Clark 	{ 0x0d, "BTN_MODE" },
12793ff13edSDerek J. Clark 	{ 0x0e, "BTN_SELECT" },
12893ff13edSDerek J. Clark 	{ 0x0f, "BTN_START" },
12993ff13edSDerek J. Clark 	{ 0x13, "BTN_TL2"},
13093ff13edSDerek J. Clark 	{ 0x14, "BTN_TR2"},
13193ff13edSDerek J. Clark 	{ 0x15, "ABS_Y_UP"},
13293ff13edSDerek J. Clark 	{ 0x16, "ABS_Y_DOWN"},
13393ff13edSDerek J. Clark 	{ 0x17, "ABS_X_LEFT"},
13493ff13edSDerek J. Clark 	{ 0x18, "ABS_X_RIGHT"},
13593ff13edSDerek J. Clark 	{ 0x19, "ABS_RY_UP"},
13693ff13edSDerek J. Clark 	{ 0x1a, "ABS_RY_DOWN"},
13793ff13edSDerek J. Clark 	{ 0x1b, "ABS_RX_LEFT"},
13893ff13edSDerek J. Clark 	{ 0x1c, "ABS_RX_RIGHT"},
13993ff13edSDerek J. Clark 	/* Keyboard keys */
14093ff13edSDerek J. Clark 	{ 0x32, "KEY_ESC" },
14193ff13edSDerek J. Clark 	{ 0x33, "KEY_F1" },
14293ff13edSDerek J. Clark 	{ 0x34, "KEY_F2" },
14393ff13edSDerek J. Clark 	{ 0x35, "KEY_F3" },
14493ff13edSDerek J. Clark 	{ 0x36, "KEY_F4" },
14593ff13edSDerek J. Clark 	{ 0x37, "KEY_F5" },
14693ff13edSDerek J. Clark 	{ 0x38, "KEY_F6" },
14793ff13edSDerek J. Clark 	{ 0x39, "KEY_F7" },
14893ff13edSDerek J. Clark 	{ 0x3a, "KEY_F8" },
14993ff13edSDerek J. Clark 	{ 0x3b, "KEY_F9" },
15093ff13edSDerek J. Clark 	{ 0x3c, "KEY_F10" },
15193ff13edSDerek J. Clark 	{ 0x3d, "KEY_F11" },
15293ff13edSDerek J. Clark 	{ 0x3e, "KEY_F12" },
15393ff13edSDerek J. Clark 	{ 0x3f, "KEY_GRAVE" },
15493ff13edSDerek J. Clark 	{ 0x40, "KEY_1" },
15593ff13edSDerek J. Clark 	{ 0x41, "KEY_2" },
15693ff13edSDerek J. Clark 	{ 0x42, "KEY_3" },
15793ff13edSDerek J. Clark 	{ 0x43, "KEY_4" },
15893ff13edSDerek J. Clark 	{ 0x44, "KEY_5" },
15993ff13edSDerek J. Clark 	{ 0x45, "KEY_6" },
16093ff13edSDerek J. Clark 	{ 0x46, "KEY_7" },
16193ff13edSDerek J. Clark 	{ 0x47, "KEY_8" },
16293ff13edSDerek J. Clark 	{ 0x48, "KEY_9" },
16393ff13edSDerek J. Clark 	{ 0x49, "KEY_0" },
16493ff13edSDerek J. Clark 	{ 0x4a, "KEY_MINUS" },
16593ff13edSDerek J. Clark 	{ 0x4b, "KEY_EQUAL" },
16693ff13edSDerek J. Clark 	{ 0x4c, "KEY_BACKSPACE" },
16793ff13edSDerek J. Clark 	{ 0x4d, "KEY_TAB" },
16893ff13edSDerek J. Clark 	{ 0x4e, "KEY_Q" },
16993ff13edSDerek J. Clark 	{ 0x4f, "KEY_W" },
17093ff13edSDerek J. Clark 	{ 0x50, "KEY_E" },
17193ff13edSDerek J. Clark 	{ 0x51, "KEY_R" },
17293ff13edSDerek J. Clark 	{ 0x52, "KEY_T" },
17393ff13edSDerek J. Clark 	{ 0x53, "KEY_Y" },
17493ff13edSDerek J. Clark 	{ 0x54, "KEY_U" },
17593ff13edSDerek J. Clark 	{ 0x55, "KEY_I" },
17693ff13edSDerek J. Clark 	{ 0x56, "KEY_O" },
17793ff13edSDerek J. Clark 	{ 0x57, "KEY_P" },
17893ff13edSDerek J. Clark 	{ 0x58, "KEY_LEFTBRACE" },
17993ff13edSDerek J. Clark 	{ 0x59, "KEY_RIGHTBRACE" },
18093ff13edSDerek J. Clark 	{ 0x5a, "KEY_BACKSLASH" },
18193ff13edSDerek J. Clark 	{ 0x5b, "KEY_CAPSLOCK" },
18293ff13edSDerek J. Clark 	{ 0x5c, "KEY_A" },
18393ff13edSDerek J. Clark 	{ 0x5d, "KEY_S" },
18493ff13edSDerek J. Clark 	{ 0x5e, "KEY_D" },
18593ff13edSDerek J. Clark 	{ 0x5f, "KEY_F" },
18693ff13edSDerek J. Clark 	{ 0x60, "KEY_G" },
18793ff13edSDerek J. Clark 	{ 0x61, "KEY_H" },
18893ff13edSDerek J. Clark 	{ 0x62, "KEY_J" },
18993ff13edSDerek J. Clark 	{ 0x63, "KEY_K" },
19093ff13edSDerek J. Clark 	{ 0x64, "KEY_L" },
19193ff13edSDerek J. Clark 	{ 0x65, "KEY_SEMICOLON" },
19293ff13edSDerek J. Clark 	{ 0x66, "KEY_APOSTROPHE" },
19393ff13edSDerek J. Clark 	{ 0x67, "KEY_ENTER" },
19493ff13edSDerek J. Clark 	{ 0x68, "KEY_LEFTSHIFT" },
19593ff13edSDerek J. Clark 	{ 0x69, "KEY_Z" },
19693ff13edSDerek J. Clark 	{ 0x6a, "KEY_X" },
19793ff13edSDerek J. Clark 	{ 0x6b, "KEY_C" },
19893ff13edSDerek J. Clark 	{ 0x6c, "KEY_V" },
19993ff13edSDerek J. Clark 	{ 0x6d, "KEY_B" },
20093ff13edSDerek J. Clark 	{ 0x6e, "KEY_N" },
20193ff13edSDerek J. Clark 	{ 0x6f, "KEY_M" },
20293ff13edSDerek J. Clark 	{ 0x70, "KEY_COMMA" },
20393ff13edSDerek J. Clark 	{ 0x71, "KEY_DOT" },
20493ff13edSDerek J. Clark 	{ 0x72, "KEY_SLASH" },
20593ff13edSDerek J. Clark 	{ 0x73, "KEY_RIGHTSHIFT" },
20693ff13edSDerek J. Clark 	{ 0x74, "KEY_LEFTCTRL" },
20793ff13edSDerek J. Clark 	{ 0x75, "KEY_LEFTMETA" },
20893ff13edSDerek J. Clark 	{ 0x76, "KEY_LEFTALT" },
20993ff13edSDerek J. Clark 	{ 0x77, "KEY_SPACE" },
21093ff13edSDerek J. Clark 	{ 0x78, "KEY_RIGHTALT" },
21193ff13edSDerek J. Clark 	{ 0x79, "KEY_RIGHTCTRL" },
21293ff13edSDerek J. Clark 	{ 0x7a, "KEY_INSERT" },
21393ff13edSDerek J. Clark 	{ 0x7b, "KEY_HOME" },
21493ff13edSDerek J. Clark 	{ 0x7c, "KEY_PAGEUP" },
21593ff13edSDerek J. Clark 	{ 0x7d, "KEY_DELETE" },
21693ff13edSDerek J. Clark 	{ 0x7e, "KEY_END" },
21793ff13edSDerek J. Clark 	{ 0x7f, "KEY_PAGEDOWN" },
21893ff13edSDerek J. Clark 	{ 0x8a, "KEY_KPENTER" },
21993ff13edSDerek J. Clark 	{ 0x8b, "KEY_KP0" },
22093ff13edSDerek J. Clark 	{ 0x8c, "KEY_KP1" },
22193ff13edSDerek J. Clark 	{ 0x8d, "KEY_KP2" },
22293ff13edSDerek J. Clark 	{ 0x8e, "KEY_KP3" },
22393ff13edSDerek J. Clark 	{ 0x8f, "KEY_KP4" },
22493ff13edSDerek J. Clark 	{ 0x90, "KEY_KP5" },
22593ff13edSDerek J. Clark 	{ 0x91, "KEY_KP6" },
22693ff13edSDerek J. Clark 	{ 0x92, "KEY_KP7" },
22793ff13edSDerek J. Clark 	{ 0x93, "KEY_KP8" },
22893ff13edSDerek J. Clark 	{ 0x94, "KEY_KP9" },
22993ff13edSDerek J. Clark 	{ 0x95, "MD_PLAY" },
23093ff13edSDerek J. Clark 	{ 0x96, "MD_STOP" },
23193ff13edSDerek J. Clark 	{ 0x97, "MD_NEXT" },
23293ff13edSDerek J. Clark 	{ 0x98, "MD_PREV" },
23393ff13edSDerek J. Clark 	{ 0x99, "MD_VOL_UP" },
23493ff13edSDerek J. Clark 	{ 0x9a, "MD_VOL_DOWN" },
23593ff13edSDerek J. Clark 	{ 0x9b, "MD_VOL_MUTE" },
23693ff13edSDerek J. Clark 	{ 0x9c, "KEY_F23" },
23793ff13edSDerek J. Clark 	/* Mouse events */
23893ff13edSDerek J. Clark 	{ 0xc8, "BTN_LEFT" },
23993ff13edSDerek J. Clark 	{ 0xc9, "BTN_MIDDLE" },
24093ff13edSDerek J. Clark 	{ 0xca, "BTN_RIGHT" },
24193ff13edSDerek J. Clark 	{ 0xcb, "BTN_SIDE" },
24293ff13edSDerek J. Clark 	{ 0xcc, "BTN_EXTRA" },
24393ff13edSDerek J. Clark 	{ 0xcd, "REL_WHEEL_UP" },
24493ff13edSDerek J. Clark 	{ 0xce, "REL_WHEEL_DOWN" },
24593ff13edSDerek J. Clark 	{ 0xff, "DISABLED" },
24693ff13edSDerek J. Clark };
24793ff13edSDerek J. Clark 
248f27e2e3cSDerek J. Clark enum claw_rgb_effect_index {
249f27e2e3cSDerek J. Clark 	CLAW_RGB_EFFECT_MONOCOLOR,
250f27e2e3cSDerek J. Clark 	CLAW_RGB_EFFECT_BREATHE,
251f27e2e3cSDerek J. Clark 	CLAW_RGB_EFFECT_CHROMA,
252f27e2e3cSDerek J. Clark 	CLAW_RGB_EFFECT_RAINBOW,
253f27e2e3cSDerek J. Clark 	CLAW_RGB_EFFECT_FROSTFIRE,
254f27e2e3cSDerek J. Clark };
255f27e2e3cSDerek J. Clark 
256f27e2e3cSDerek J. Clark static const char * const claw_rgb_effect_text[] = {
257f27e2e3cSDerek J. Clark 	[CLAW_RGB_EFFECT_MONOCOLOR] =	"monocolor",
258f27e2e3cSDerek J. Clark 	[CLAW_RGB_EFFECT_BREATHE] =	"breathe",
259f27e2e3cSDerek J. Clark 	[CLAW_RGB_EFFECT_CHROMA] =	"chroma",
260f27e2e3cSDerek J. Clark 	[CLAW_RGB_EFFECT_RAINBOW] =	"rainbow",
261f27e2e3cSDerek J. Clark 	[CLAW_RGB_EFFECT_FROSTFIRE] =	"frostfire",
262f27e2e3cSDerek J. Clark };
263f27e2e3cSDerek J. Clark 
26493ff13edSDerek J. Clark static const u16 button_mapping_addr_old[] = {
26593ff13edSDerek J. Clark 	0x007a,  /* M1 */
26693ff13edSDerek J. Clark 	0x011f,  /* M2 */
26793ff13edSDerek J. Clark };
26893ff13edSDerek J. Clark 
26993ff13edSDerek J. Clark static const u16 button_mapping_addr_new[] = {
27093ff13edSDerek J. Clark 	0x00bb,  /* M1 */
27193ff13edSDerek J. Clark 	0x0164,  /* M2 */
27293ff13edSDerek J. Clark };
27393ff13edSDerek J. Clark 
274f27e2e3cSDerek J. Clark static const u16 rgb_addr_old = 0x01fa;
275f27e2e3cSDerek J. Clark static const u16 rgb_addr_new = 0x024a;
276f27e2e3cSDerek J. Clark 
277*95547c53SDerek J. Clark static const u16 rumble_addr[] = {
278*95547c53SDerek J. Clark 	0x0022,  /* left  */
279*95547c53SDerek J. Clark 	0x0023,  /* right */
280*95547c53SDerek J. Clark };
281*95547c53SDerek J. Clark 
282a9517958SDerek J. Clark struct claw_command_report {
283a9517958SDerek J. Clark 	u8 report_id;
284a9517958SDerek J. Clark 	u8 padding[2];
285a9517958SDerek J. Clark 	u8 header_tail;
286a9517958SDerek J. Clark 	u8 cmd;
287a9517958SDerek J. Clark 	u8 data[59];
288a9517958SDerek J. Clark } __packed;
289a9517958SDerek J. Clark 
29093ff13edSDerek J. Clark struct claw_profile_report {
29193ff13edSDerek J. Clark 	u8 profile;
29293ff13edSDerek J. Clark 	__be16 read_addr;
29393ff13edSDerek J. Clark } __packed;
29493ff13edSDerek J. Clark 
29593ff13edSDerek J. Clark struct claw_mkey_report {
29693ff13edSDerek J. Clark 	struct claw_profile_report;
29793ff13edSDerek J. Clark 	u8 padding_0;
29893ff13edSDerek J. Clark 	u8 padding_1;
29993ff13edSDerek J. Clark 	u8 padding_2;
30093ff13edSDerek J. Clark 	u8 codes[5];
30193ff13edSDerek J. Clark } __packed;
30293ff13edSDerek J. Clark 
303f27e2e3cSDerek J. Clark struct rgb_zone {
304f27e2e3cSDerek J. Clark 	u8 red;
305f27e2e3cSDerek J. Clark 	u8 green;
306f27e2e3cSDerek J. Clark 	u8 blue;
307f27e2e3cSDerek J. Clark };
308f27e2e3cSDerek J. Clark 
309f27e2e3cSDerek J. Clark struct rgb_frame {
310f27e2e3cSDerek J. Clark 	struct rgb_zone zone[CLAW_RGB_ZONES];
311f27e2e3cSDerek J. Clark };
312f27e2e3cSDerek J. Clark 
313f27e2e3cSDerek J. Clark struct claw_rgb_report {
314f27e2e3cSDerek J. Clark 	struct claw_profile_report;
315f27e2e3cSDerek J. Clark 	u8 frame_bytes;
316f27e2e3cSDerek J. Clark 	u8 padding;
317f27e2e3cSDerek J. Clark 	u8 frame_count;
318f27e2e3cSDerek J. Clark 	u8 state; /* Always 0x09 */
319f27e2e3cSDerek J. Clark 	u8 speed;
320f27e2e3cSDerek J. Clark 	u8 brightness;
321f27e2e3cSDerek J. Clark 	struct rgb_frame zone_data;
322f27e2e3cSDerek J. Clark } __packed;
323f27e2e3cSDerek J. Clark 
324*95547c53SDerek J. Clark struct claw_rumble_report {
325*95547c53SDerek J. Clark 	struct claw_profile_report;
326*95547c53SDerek J. Clark 	u8 padding;
327*95547c53SDerek J. Clark 	u8 intensity;
328*95547c53SDerek J. Clark } __packed;
329*95547c53SDerek J. Clark 
330a9517958SDerek J. Clark struct claw_drvdata {
331a9517958SDerek J. Clark 	/* MCU General Variables */
33293ff13edSDerek J. Clark 	enum claw_profile_ack_pending profile_pending;
333a9517958SDerek J. Clark 	struct completion orphan_ack_complete;
334a9517958SDerek J. Clark 	struct completion send_cmd_complete;
335a9517958SDerek J. Clark 	struct delayed_work cfg_resume;
336a9517958SDerek J. Clark 	struct delayed_work cfg_setup;
337a9517958SDerek J. Clark 	spinlock_t registration_lock; /* Lock for registration read/write */
33893ff13edSDerek J. Clark 	struct mutex profile_mutex; /* mutex for profile_pending calls */
33993ff13edSDerek J. Clark 	spinlock_t profile_lock; /* Lock for profile_pending read/write */
340a9517958SDerek J. Clark 	struct hid_device *hdev;
341a9517958SDerek J. Clark 	bool orphan_ack_pending;
342a9517958SDerek J. Clark 	struct mutex cfg_mutex; /* mutex for synchronous data */
34393ff13edSDerek J. Clark 	struct mutex rom_mutex; /* mutex for SYNC_TO_ROM calls */
344a9517958SDerek J. Clark 	spinlock_t cmd_lock; /* Lock for cmd data read/write */
345a9517958SDerek J. Clark 	u8 waiting_cmd;
346a9517958SDerek J. Clark 	int cmd_status;
34793ff13edSDerek J. Clark 	u16 bcd_device;
348a9517958SDerek J. Clark 	u8 ep;
349a9517958SDerek J. Clark 
350a9517958SDerek J. Clark 	/* Gamepad Variables */
351a9517958SDerek J. Clark 	enum claw_mkeys_function_index mkeys_function;
352a9517958SDerek J. Clark 	enum claw_gamepad_mode_index gamepad_mode;
35393ff13edSDerek J. Clark 	u8 m1_codes[CLAW_KEYS_MAX];
35493ff13edSDerek J. Clark 	u8 m2_codes[CLAW_KEYS_MAX];
355*95547c53SDerek J. Clark 	u8 rumble_intensity_right;
356*95547c53SDerek J. Clark 	u8 rumble_intensity_left;
357f27e2e3cSDerek J. Clark 	const u16 *bmap_addr;
358*95547c53SDerek J. Clark 	spinlock_t rumble_lock; /* lock for rumble_intensity read/write */
359*95547c53SDerek J. Clark 	spinlock_t mode_lock; /* Lock for mode data read/write */
360*95547c53SDerek J. Clark 	bool rumble_support;
361a9517958SDerek J. Clark 	bool gp_registered;
36293ff13edSDerek J. Clark 	bool bmap_support;
363f27e2e3cSDerek J. Clark 
364f27e2e3cSDerek J. Clark 	/* RGB Variables */
365f27e2e3cSDerek J. Clark 	struct rgb_frame rgb_frames[CLAW_RGB_MAX_FRAMES];
366f27e2e3cSDerek J. Clark 	enum claw_rgb_effect_index rgb_effect;
367f27e2e3cSDerek J. Clark 	struct led_classdev_mc led_mc;
368f27e2e3cSDerek J. Clark 	struct delayed_work rgb_queue;
369f27e2e3cSDerek J. Clark 	spinlock_t frame_lock; /* lock for rgb_frames read/write */
370f27e2e3cSDerek J. Clark 	bool rgb_registered;
371f27e2e3cSDerek J. Clark 	u8 rgb_frame_count;
372f27e2e3cSDerek J. Clark 	bool rgb_enabled;
373f27e2e3cSDerek J. Clark 	u8 rgb_speed;
374f27e2e3cSDerek J. Clark 	u16 rgb_addr;
375a9517958SDerek J. Clark };
376a9517958SDerek J. Clark 
377a9517958SDerek J. Clark static int get_endpoint_address(struct hid_device *hdev)
378a9517958SDerek J. Clark {
379a9517958SDerek J. Clark 	struct usb_host_endpoint *ep;
380a9517958SDerek J. Clark 	struct usb_interface *intf;
381a9517958SDerek J. Clark 
382a9517958SDerek J. Clark 	intf = to_usb_interface(hdev->dev.parent);
383a9517958SDerek J. Clark 	ep = intf->cur_altsetting->endpoint;
384a9517958SDerek J. Clark 	if (ep)
385a9517958SDerek J. Clark 		return ep->desc.bEndpointAddress;
386a9517958SDerek J. Clark 
387a9517958SDerek J. Clark 	return -ENODEV;
388a9517958SDerek J. Clark }
389a9517958SDerek J. Clark 
390a9517958SDerek J. Clark static int claw_gamepad_mode_event(struct claw_drvdata *drvdata,
391a9517958SDerek J. Clark 				   struct claw_command_report *cmd_rep)
392a9517958SDerek J. Clark {
393a9517958SDerek J. Clark 	if (cmd_rep->data[0] >= ARRAY_SIZE(claw_gamepad_mode_text) ||
394a9517958SDerek J. Clark 	    !claw_gamepad_mode_text[cmd_rep->data[0]] ||
395a9517958SDerek J. Clark 	    cmd_rep->data[1] >= ARRAY_SIZE(claw_mkeys_function_text))
396a9517958SDerek J. Clark 		return -EINVAL;
397a9517958SDerek J. Clark 
398a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->mode_lock) {
399a9517958SDerek J. Clark 		drvdata->gamepad_mode = cmd_rep->data[0];
400a9517958SDerek J. Clark 		drvdata->mkeys_function = cmd_rep->data[1];
401a9517958SDerek J. Clark 	}
402a9517958SDerek J. Clark 
403a9517958SDerek J. Clark 	return 0;
404a9517958SDerek J. Clark }
405a9517958SDerek J. Clark 
40693ff13edSDerek J. Clark static int claw_profile_event(struct claw_drvdata *drvdata, struct claw_command_report *cmd_rep)
40793ff13edSDerek J. Clark {
40893ff13edSDerek J. Clark 	enum claw_profile_ack_pending profile;
409*95547c53SDerek J. Clark 	struct claw_rumble_report *rumble;
41093ff13edSDerek J. Clark 	struct claw_mkey_report *mkeys;
411f27e2e3cSDerek J. Clark 	struct claw_rgb_report *frame;
412f27e2e3cSDerek J. Clark 	u16 rgb_addr, read_addr;
413f27e2e3cSDerek J. Clark 	u8 *codes, key, f_idx;
414f27e2e3cSDerek J. Clark 	u16 frame_calc;
41593ff13edSDerek J. Clark 	int i;
41693ff13edSDerek J. Clark 
41793ff13edSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->profile_lock)
41893ff13edSDerek J. Clark 		profile = drvdata->profile_pending;
41993ff13edSDerek J. Clark 
42093ff13edSDerek J. Clark 	switch (profile) {
42193ff13edSDerek J. Clark 	case CLAW_M1_PENDING:
42293ff13edSDerek J. Clark 	case CLAW_M2_PENDING:
42393ff13edSDerek J. Clark 		key = (profile == CLAW_M1_PENDING) ? CLAW_KEY_M1 : CLAW_KEY_M2;
42493ff13edSDerek J. Clark 		mkeys = (struct claw_mkey_report *)cmd_rep->data;
42593ff13edSDerek J. Clark 		if (be16_to_cpu(mkeys->read_addr) != drvdata->bmap_addr[key])
42693ff13edSDerek J. Clark 			return -EAGAIN;
42793ff13edSDerek J. Clark 		codes = (profile == CLAW_M1_PENDING) ? drvdata->m1_codes : drvdata->m2_codes;
42893ff13edSDerek J. Clark 		for (i = 0; i < CLAW_KEYS_MAX; i++)
42993ff13edSDerek J. Clark 			codes[i] = (mkeys->codes[i]);
43093ff13edSDerek J. Clark 		break;
431f27e2e3cSDerek J. Clark 	case CLAW_RGB_PENDING:
432f27e2e3cSDerek J. Clark 		frame = (struct claw_rgb_report *)cmd_rep->data;
433f27e2e3cSDerek J. Clark 		rgb_addr = drvdata->rgb_addr;
434f27e2e3cSDerek J. Clark 		read_addr = be16_to_cpu(frame->read_addr);
435f27e2e3cSDerek J. Clark 
436f27e2e3cSDerek J. Clark 		if (read_addr < drvdata->rgb_addr)
437f27e2e3cSDerek J. Clark 			return -EAGAIN;
438f27e2e3cSDerek J. Clark 
439f27e2e3cSDerek J. Clark 		frame_calc = (read_addr - rgb_addr) / CLAW_RGB_FRAME_OFFSET;
440f27e2e3cSDerek J. Clark 		if (frame_calc >= CLAW_RGB_MAX_FRAMES) {
441f27e2e3cSDerek J. Clark 			dev_err(&drvdata->hdev->dev, "Got unsupported frame index: %x\n",
442f27e2e3cSDerek J. Clark 				frame_calc);
443f27e2e3cSDerek J. Clark 			return -EAGAIN;
444f27e2e3cSDerek J. Clark 		}
445f27e2e3cSDerek J. Clark 		f_idx = frame_calc;
446f27e2e3cSDerek J. Clark 
447f27e2e3cSDerek J. Clark 		scoped_guard(spinlock_irqsave, &drvdata->frame_lock) {
448f27e2e3cSDerek J. Clark 			memcpy(&drvdata->rgb_frames[f_idx], &frame->zone_data,
449f27e2e3cSDerek J. Clark 			       sizeof(struct rgb_frame));
450f27e2e3cSDerek J. Clark 
451f27e2e3cSDerek J. Clark 			/* Only use frame 0 for remaining variable assignment */
452f27e2e3cSDerek J. Clark 			if (f_idx != 0)
453f27e2e3cSDerek J. Clark 				break;
454f27e2e3cSDerek J. Clark 
455f27e2e3cSDerek J. Clark 			drvdata->rgb_speed = frame->speed;
456f27e2e3cSDerek J. Clark 			drvdata->led_mc.led_cdev.brightness = frame->brightness;
457f27e2e3cSDerek J. Clark 			drvdata->led_mc.subled_info[0].intensity = frame->zone_data.zone[0].red;
458f27e2e3cSDerek J. Clark 			drvdata->led_mc.subled_info[1].intensity = frame->zone_data.zone[0].green;
459f27e2e3cSDerek J. Clark 			drvdata->led_mc.subled_info[2].intensity = frame->zone_data.zone[0].blue;
460f27e2e3cSDerek J. Clark 		}
461f27e2e3cSDerek J. Clark 
462f27e2e3cSDerek J. Clark 		break;
463*95547c53SDerek J. Clark 	case CLAW_RUMBLE_LEFT_PENDING:
464*95547c53SDerek J. Clark 		rumble = (struct claw_rumble_report *)cmd_rep->data;
465*95547c53SDerek J. Clark 		if (be16_to_cpu(rumble->read_addr) != rumble_addr[0])
466*95547c53SDerek J. Clark 			return -EAGAIN;
467*95547c53SDerek J. Clark 		scoped_guard(spinlock_irqsave, &drvdata->rumble_lock)
468*95547c53SDerek J. Clark 			drvdata->rumble_intensity_left = rumble->intensity;
469*95547c53SDerek J. Clark 		break;
470*95547c53SDerek J. Clark 	case CLAW_RUMBLE_RIGHT_PENDING:
471*95547c53SDerek J. Clark 		rumble = (struct claw_rumble_report *)cmd_rep->data;
472*95547c53SDerek J. Clark 		if (be16_to_cpu(rumble->read_addr) != rumble_addr[1])
473*95547c53SDerek J. Clark 			return -EAGAIN;
474*95547c53SDerek J. Clark 		scoped_guard(spinlock_irqsave, &drvdata->rumble_lock)
475*95547c53SDerek J. Clark 			drvdata->rumble_intensity_right = rumble->intensity;
476*95547c53SDerek J. Clark 		break;
47793ff13edSDerek J. Clark 	default:
47893ff13edSDerek J. Clark 		dev_dbg(&drvdata->hdev->dev,
47993ff13edSDerek J. Clark 			"Got profile event without changes pending from command: %x\n",
48093ff13edSDerek J. Clark 			cmd_rep->cmd);
48193ff13edSDerek J. Clark 		return -EINVAL;
48293ff13edSDerek J. Clark 	}
48393ff13edSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->profile_lock)
48493ff13edSDerek J. Clark 		drvdata->profile_pending = CLAW_NO_PENDING;
48593ff13edSDerek J. Clark 
48693ff13edSDerek J. Clark 	return 0;
48793ff13edSDerek J. Clark }
48893ff13edSDerek J. Clark 
489a9517958SDerek J. Clark static int claw_raw_event(struct claw_drvdata *drvdata, struct hid_report *report,
490a9517958SDerek J. Clark 			  u8 *data, int size)
491a9517958SDerek J. Clark {
492a9517958SDerek J. Clark 	struct claw_command_report *cmd_rep;
493a9517958SDerek J. Clark 	int ret = 0;
494a9517958SDerek J. Clark 
495a9517958SDerek J. Clark 	if (size != CLAW_PACKET_SIZE)
496a9517958SDerek J. Clark 		return 0;
497a9517958SDerek J. Clark 
498a9517958SDerek J. Clark 	cmd_rep = (struct claw_command_report *)data;
499a9517958SDerek J. Clark 
500a9517958SDerek J. Clark 	if (cmd_rep->report_id != CLAW_INPUT_REPORT_ID || cmd_rep->header_tail != 0x3c)
501a9517958SDerek J. Clark 		return 0;
502a9517958SDerek J. Clark 
503a9517958SDerek J. Clark 	dev_dbg(&drvdata->hdev->dev, "Rx data as raw input report: [%*ph]\n",
504a9517958SDerek J. Clark 		CLAW_PACKET_SIZE, data);
505a9517958SDerek J. Clark 
506a9517958SDerek J. Clark 	guard(spinlock_irqsave)(&drvdata->cmd_lock);
507a9517958SDerek J. Clark 	switch (cmd_rep->cmd) {
508a9517958SDerek J. Clark 	case CLAW_COMMAND_TYPE_GAMEPAD_MODE_ACK:
509a9517958SDerek J. Clark 		ret = claw_gamepad_mode_event(drvdata, cmd_rep);
510a9517958SDerek J. Clark 		if (drvdata->waiting_cmd == CLAW_COMMAND_TYPE_READ_GAMEPAD_MODE) {
511a9517958SDerek J. Clark 			drvdata->cmd_status = ret;
512a9517958SDerek J. Clark 			complete(&drvdata->send_cmd_complete);
513a9517958SDerek J. Clark 		}
514a9517958SDerek J. Clark 
515a9517958SDerek J. Clark 		break;
51693ff13edSDerek J. Clark 	case CLAW_COMMAND_TYPE_READ_PROFILE_ACK:
51793ff13edSDerek J. Clark 		ret = claw_profile_event(drvdata, cmd_rep);
51893ff13edSDerek J. Clark 		/* Stale address received, ignore and keep waiting */
51993ff13edSDerek J. Clark 		if (ret == -EAGAIN)
52093ff13edSDerek J. Clark 			return 0;
52193ff13edSDerek J. Clark 		if (drvdata->waiting_cmd == CLAW_COMMAND_TYPE_READ_PROFILE) {
52293ff13edSDerek J. Clark 			drvdata->cmd_status = ret;
52393ff13edSDerek J. Clark 			complete(&drvdata->send_cmd_complete);
52493ff13edSDerek J. Clark 		}
52593ff13edSDerek J. Clark 
52693ff13edSDerek J. Clark 		break;
527a9517958SDerek J. Clark 	case CLAW_COMMAND_TYPE_ACK:
528a9517958SDerek J. Clark 		if (drvdata->orphan_ack_pending) {
529a9517958SDerek J. Clark 			drvdata->orphan_ack_pending = false;
530a9517958SDerek J. Clark 			complete(&drvdata->orphan_ack_complete);
531a9517958SDerek J. Clark 			break;
532a9517958SDerek J. Clark 		}
533a9517958SDerek J. Clark 
534a9517958SDerek J. Clark 		if (drvdata->waiting_cmd == CLAW_COMMAND_TYPE_NONE) {
535a9517958SDerek J. Clark 			dev_warn(&drvdata->hdev->dev, "Got unexpected ACK from MCU, ignoring\n");
536a9517958SDerek J. Clark 			break;
537a9517958SDerek J. Clark 		}
538a9517958SDerek J. Clark 
539a9517958SDerek J. Clark 		drvdata->cmd_status = 0;
540a9517958SDerek J. Clark 		complete(&drvdata->send_cmd_complete);
541a9517958SDerek J. Clark 
542a9517958SDerek J. Clark 		dev_dbg(&drvdata->hdev->dev, "Waiting CMD: %x\n", drvdata->waiting_cmd);
543a9517958SDerek J. Clark 
544a9517958SDerek J. Clark 		break;
545a9517958SDerek J. Clark 	default:
546a9517958SDerek J. Clark 		dev_dbg(&drvdata->hdev->dev, "Unknown command: %x\n", cmd_rep->cmd);
547a9517958SDerek J. Clark 		return 0;
548a9517958SDerek J. Clark 	}
549a9517958SDerek J. Clark 
550a9517958SDerek J. Clark 	return ret;
551a9517958SDerek J. Clark }
552a9517958SDerek J. Clark 
553a9517958SDerek J. Clark static int msi_raw_event(struct hid_device *hdev, struct hid_report *report,
554a9517958SDerek J. Clark 			 u8 *data, int size)
555a9517958SDerek J. Clark {
556a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
557a9517958SDerek J. Clark 
558a9517958SDerek J. Clark 	if (!drvdata || (drvdata->ep != CLAW_XINPUT_CFG_INTF_IN &&
559a9517958SDerek J. Clark 			 drvdata->ep != CLAW_DINPUT_CFG_INTF_IN))
560a9517958SDerek J. Clark 		return 0;
561a9517958SDerek J. Clark 
562a9517958SDerek J. Clark 	return claw_raw_event(drvdata, report, data, size);
563a9517958SDerek J. Clark }
564a9517958SDerek J. Clark 
565a9517958SDerek J. Clark /* Caller must hold drvdata->cfg_mutex. */
566a9517958SDerek J. Clark static int __claw_hw_output_report(struct hid_device *hdev, u8 index, u8 *data,
567a9517958SDerek J. Clark 				   size_t len, unsigned int timeout)
568a9517958SDerek J. Clark {
569a9517958SDerek J. Clark 	unsigned char *dmabuf __free(kfree) = NULL;
570a9517958SDerek J. Clark 	u8 header[] = { CLAW_OUTPUT_REPORT_ID, 0, 0, 0x3c, index };
571a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
572a9517958SDerek J. Clark 	size_t header_size = ARRAY_SIZE(header);
573a9517958SDerek J. Clark 	bool orphaned;
574a9517958SDerek J. Clark 	int ret;
575a9517958SDerek J. Clark 
576a9517958SDerek J. Clark 	lockdep_assert_held(&drvdata->cfg_mutex);
577a9517958SDerek J. Clark 
578a9517958SDerek J. Clark 	/* If expecting an orphan ack, hold next event until MCU has time to clear it */
579a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->cmd_lock)
580a9517958SDerek J. Clark 		orphaned = drvdata->orphan_ack_pending;
581a9517958SDerek J. Clark 
582a9517958SDerek J. Clark 	if (orphaned) {
583a9517958SDerek J. Clark 		wait_for_completion_timeout(&drvdata->orphan_ack_complete, msecs_to_jiffies(25));
584a9517958SDerek J. Clark 		scoped_guard(spinlock_irqsave, &drvdata->cmd_lock)
585a9517958SDerek J. Clark 			drvdata->orphan_ack_pending = false;
586a9517958SDerek J. Clark 	}
587a9517958SDerek J. Clark 
588a9517958SDerek J. Clark 	if (header_size + len > CLAW_PACKET_SIZE)
589a9517958SDerek J. Clark 		return -EINVAL;
590a9517958SDerek J. Clark 
591a9517958SDerek J. Clark 	/* We can't use a devm_alloc reusable buffer without side effects during suspend */
592a9517958SDerek J. Clark 	dmabuf = kzalloc(CLAW_PACKET_SIZE, GFP_KERNEL);
593a9517958SDerek J. Clark 	if (!dmabuf)
594a9517958SDerek J. Clark 		return -ENOMEM;
595a9517958SDerek J. Clark 
596a9517958SDerek J. Clark 	memcpy(dmabuf, header, header_size);
597a9517958SDerek J. Clark 	if (data && len)
598a9517958SDerek J. Clark 		memcpy(dmabuf + header_size, data, len);
599a9517958SDerek J. Clark 
600a9517958SDerek J. Clark 	reinit_completion(&drvdata->send_cmd_complete);
601a9517958SDerek J. Clark 
602a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->cmd_lock) {
603a9517958SDerek J. Clark 		if (timeout) {
604a9517958SDerek J. Clark 			drvdata->waiting_cmd = index;
605a9517958SDerek J. Clark 			drvdata->cmd_status = -ETIMEDOUT;
606a9517958SDerek J. Clark 		} else {
607a9517958SDerek J. Clark 			reinit_completion(&drvdata->orphan_ack_complete);
608a9517958SDerek J. Clark 			drvdata->waiting_cmd = CLAW_COMMAND_TYPE_NONE;
609a9517958SDerek J. Clark 			drvdata->orphan_ack_pending = true;
610a9517958SDerek J. Clark 		}
611a9517958SDerek J. Clark 	}
612a9517958SDerek J. Clark 
613a9517958SDerek J. Clark 	dev_dbg(&hdev->dev, "Send data as raw output report: [%*ph]\n",
614a9517958SDerek J. Clark 		CLAW_PACKET_SIZE, dmabuf);
615a9517958SDerek J. Clark 
616a9517958SDerek J. Clark 	ret = hid_hw_output_report(hdev, dmabuf, CLAW_PACKET_SIZE);
617a9517958SDerek J. Clark 	if (ret < 0)
618a9517958SDerek J. Clark 		goto err;
619a9517958SDerek J. Clark 
620a9517958SDerek J. Clark 	ret = ret == CLAW_PACKET_SIZE ? 0 : -EIO;
621a9517958SDerek J. Clark 	if (ret)
622a9517958SDerek J. Clark 		goto err;
623a9517958SDerek J. Clark 
624a9517958SDerek J. Clark 	if (timeout) {
625a9517958SDerek J. Clark 		ret = wait_for_completion_interruptible_timeout(&drvdata->send_cmd_complete,
626a9517958SDerek J. Clark 								msecs_to_jiffies(timeout));
627a9517958SDerek J. Clark 
628a9517958SDerek J. Clark 		dev_dbg(&hdev->dev, "Remaining timeout: %u\n", ret);
629a9517958SDerek J. Clark 		ret = ret > 0 ? drvdata->cmd_status : ret ?: -EBUSY;
630a9517958SDerek J. Clark 		if (ret)
631a9517958SDerek J. Clark 			goto err;
632a9517958SDerek J. Clark 	}
633a9517958SDerek J. Clark 
634a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->cmd_lock)
635a9517958SDerek J. Clark 		drvdata->waiting_cmd = CLAW_COMMAND_TYPE_NONE;
636a9517958SDerek J. Clark 
637a9517958SDerek J. Clark 	return ret;
638a9517958SDerek J. Clark 
639a9517958SDerek J. Clark err:
640a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->cmd_lock) {
641a9517958SDerek J. Clark 		drvdata->waiting_cmd = CLAW_COMMAND_TYPE_NONE;
642a9517958SDerek J. Clark 		drvdata->orphan_ack_pending = false;
643a9517958SDerek J. Clark 	}
644a9517958SDerek J. Clark 	return ret;
645a9517958SDerek J. Clark }
646a9517958SDerek J. Clark 
647a9517958SDerek J. Clark static int claw_hw_output_report(struct hid_device *hdev, u8 index, u8 *data,
648a9517958SDerek J. Clark 				 size_t len, unsigned int timeout)
649a9517958SDerek J. Clark {
650a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
651a9517958SDerek J. Clark 
652a9517958SDerek J. Clark 	guard(mutex)(&drvdata->cfg_mutex);
653a9517958SDerek J. Clark 	return __claw_hw_output_report(hdev, index, data, len, timeout);
654a9517958SDerek J. Clark }
655a9517958SDerek J. Clark 
656a9517958SDerek J. Clark static int claw_switch_mode(struct hid_device *hdev, enum claw_mode_field field, u8 val)
657a9517958SDerek J. Clark {
658a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
659a9517958SDerek J. Clark 	u8 data[2];
660a9517958SDerek J. Clark 
661a9517958SDerek J. Clark 	guard(mutex)(&drvdata->cfg_mutex);
662a9517958SDerek J. Clark 
663a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->mode_lock) {
664a9517958SDerek J. Clark 		switch (field) {
665a9517958SDerek J. Clark 		case CLAW_FIELD_GAMEPAD_MODE:
666a9517958SDerek J. Clark 			data[0] = val;
667a9517958SDerek J. Clark 			data[1] = drvdata->mkeys_function;
668a9517958SDerek J. Clark 			break;
669a9517958SDerek J. Clark 		case CLAW_FIELD_MKEYS_FUNCTION:
670a9517958SDerek J. Clark 			data[0] = drvdata->gamepad_mode;
671a9517958SDerek J. Clark 			data[1] = val;
672a9517958SDerek J. Clark 			break;
673a9517958SDerek J. Clark 		}
674a9517958SDerek J. Clark 	}
675a9517958SDerek J. Clark 
676a9517958SDerek J. Clark 	return __claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_SWITCH_MODE, data,
677a9517958SDerek J. Clark 				       ARRAY_SIZE(data), 0);
678a9517958SDerek J. Clark }
679a9517958SDerek J. Clark 
680a9517958SDerek J. Clark static ssize_t gamepad_mode_store(struct device *dev, struct device_attribute *attr,
681a9517958SDerek J. Clark 				  const char *buf, size_t count)
682a9517958SDerek J. Clark {
683a9517958SDerek J. Clark 	struct hid_device *hdev = to_hid_device(dev);
684a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
685a9517958SDerek J. Clark 	int i, ret = -EINVAL;
686a9517958SDerek J. Clark 
687a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
688a9517958SDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
689a9517958SDerek J. Clark 		if (!smp_load_acquire(&drvdata->gp_registered))
690a9517958SDerek J. Clark 			return -ENODEV;
691a9517958SDerek J. Clark 	}
692a9517958SDerek J. Clark 
693a9517958SDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(claw_gamepad_mode_text); i++) {
694a9517958SDerek J. Clark 		if (claw_gamepad_mode_text[i] && sysfs_streq(buf, claw_gamepad_mode_text[i])) {
695a9517958SDerek J. Clark 			ret = i;
696a9517958SDerek J. Clark 			break;
697a9517958SDerek J. Clark 		}
698a9517958SDerek J. Clark 	}
699a9517958SDerek J. Clark 	if (ret < 0)
700a9517958SDerek J. Clark 		return ret;
701a9517958SDerek J. Clark 
702a9517958SDerek J. Clark 	ret = claw_switch_mode(hdev, CLAW_FIELD_GAMEPAD_MODE, ret);
703a9517958SDerek J. Clark 	if (ret)
704a9517958SDerek J. Clark 		return ret;
705a9517958SDerek J. Clark 
706a9517958SDerek J. Clark 	return count;
707a9517958SDerek J. Clark }
708a9517958SDerek J. Clark 
709a9517958SDerek J. Clark static ssize_t gamepad_mode_show(struct device *dev,
710a9517958SDerek J. Clark 				 struct device_attribute *attr, char *buf)
711a9517958SDerek J. Clark {
712a9517958SDerek J. Clark 	struct hid_device *hdev = to_hid_device(dev);
713a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
714a9517958SDerek J. Clark 	int ret, i;
715a9517958SDerek J. Clark 
716a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
717a9517958SDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
718a9517958SDerek J. Clark 		if (!smp_load_acquire(&drvdata->gp_registered))
719a9517958SDerek J. Clark 			return -ENODEV;
720a9517958SDerek J. Clark 	}
721a9517958SDerek J. Clark 
722a9517958SDerek J. Clark 	ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_READ_GAMEPAD_MODE, NULL, 0, 25);
723a9517958SDerek J. Clark 	if (ret)
724a9517958SDerek J. Clark 		return ret;
725a9517958SDerek J. Clark 
726a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->mode_lock)
727a9517958SDerek J. Clark 		i = drvdata->gamepad_mode;
728a9517958SDerek J. Clark 
729a9517958SDerek J. Clark 	if (!claw_gamepad_mode_text[i] || claw_gamepad_mode_text[i][0] == '\0')
730a9517958SDerek J. Clark 		return sysfs_emit(buf, "unsupported\n");
731a9517958SDerek J. Clark 
732a9517958SDerek J. Clark 	return sysfs_emit(buf, "%s\n", claw_gamepad_mode_text[i]);
733a9517958SDerek J. Clark }
734a9517958SDerek J. Clark static DEVICE_ATTR_RW(gamepad_mode);
735a9517958SDerek J. Clark 
736a9517958SDerek J. Clark static ssize_t gamepad_mode_index_show(struct device *dev,
737a9517958SDerek J. Clark 				       struct device_attribute *attr, char *buf)
738a9517958SDerek J. Clark {
739a9517958SDerek J. Clark 	ssize_t count = 0;
740a9517958SDerek J. Clark 	int i;
741a9517958SDerek J. Clark 
742a9517958SDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(claw_gamepad_mode_text); i++) {
743a9517958SDerek J. Clark 		if (!claw_gamepad_mode_text[i] || claw_gamepad_mode_text[i][0] == '\0')
744a9517958SDerek J. Clark 			continue;
745a9517958SDerek J. Clark 		count += sysfs_emit_at(buf, count, "%s ", claw_gamepad_mode_text[i]);
746a9517958SDerek J. Clark 	}
747a9517958SDerek J. Clark 
748a9517958SDerek J. Clark 	if (count)
749a9517958SDerek J. Clark 		buf[count - 1] = '\n';
750a9517958SDerek J. Clark 
751a9517958SDerek J. Clark 	return count;
752a9517958SDerek J. Clark }
753a9517958SDerek J. Clark static DEVICE_ATTR_RO(gamepad_mode_index);
754a9517958SDerek J. Clark 
755a9517958SDerek J. Clark static ssize_t mkeys_function_store(struct device *dev, struct device_attribute *attr,
756a9517958SDerek J. Clark 				    const char *buf, size_t count)
757a9517958SDerek J. Clark {
758a9517958SDerek J. Clark 	struct hid_device *hdev = to_hid_device(dev);
759a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
760a9517958SDerek J. Clark 	int i, ret = -EINVAL;
761a9517958SDerek J. Clark 
762a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
763a9517958SDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
764a9517958SDerek J. Clark 		if (!smp_load_acquire(&drvdata->gp_registered))
765a9517958SDerek J. Clark 			return -ENODEV;
766a9517958SDerek J. Clark 	}
767a9517958SDerek J. Clark 
768a9517958SDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(claw_mkeys_function_text); i++) {
769a9517958SDerek J. Clark 		if (claw_mkeys_function_text[i] && sysfs_streq(buf, claw_mkeys_function_text[i])) {
770a9517958SDerek J. Clark 			ret = i;
771a9517958SDerek J. Clark 			break;
772a9517958SDerek J. Clark 		}
773a9517958SDerek J. Clark 	}
774a9517958SDerek J. Clark 	if (ret < 0)
775a9517958SDerek J. Clark 		return ret;
776a9517958SDerek J. Clark 
777a9517958SDerek J. Clark 	ret = claw_switch_mode(hdev, CLAW_FIELD_MKEYS_FUNCTION, ret);
778a9517958SDerek J. Clark 	if (ret)
779a9517958SDerek J. Clark 		return ret;
780a9517958SDerek J. Clark 
781a9517958SDerek J. Clark 	return count;
782a9517958SDerek J. Clark }
783a9517958SDerek J. Clark 
784a9517958SDerek J. Clark static ssize_t mkeys_function_show(struct device *dev, struct device_attribute *attr,
785a9517958SDerek J. Clark 				   char *buf)
786a9517958SDerek J. Clark {
787a9517958SDerek J. Clark 	struct hid_device *hdev = to_hid_device(dev);
788a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
789a9517958SDerek J. Clark 	int ret, i;
790a9517958SDerek J. Clark 
791a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
792a9517958SDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
793a9517958SDerek J. Clark 		if (!smp_load_acquire(&drvdata->gp_registered))
794a9517958SDerek J. Clark 			return -ENODEV;
795a9517958SDerek J. Clark 	}
796a9517958SDerek J. Clark 
797a9517958SDerek J. Clark 	ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_READ_GAMEPAD_MODE, NULL, 0, 25);
798a9517958SDerek J. Clark 	if (ret)
799a9517958SDerek J. Clark 		return ret;
800a9517958SDerek J. Clark 
801a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->mode_lock)
802a9517958SDerek J. Clark 		i = drvdata->mkeys_function;
803a9517958SDerek J. Clark 
804a9517958SDerek J. Clark 	if (i >= ARRAY_SIZE(claw_mkeys_function_text))
805a9517958SDerek J. Clark 		return sysfs_emit(buf, "unsupported\n");
806a9517958SDerek J. Clark 
807a9517958SDerek J. Clark 	return sysfs_emit(buf, "%s\n", claw_mkeys_function_text[i]);
808a9517958SDerek J. Clark }
809a9517958SDerek J. Clark static DEVICE_ATTR_RW(mkeys_function);
810a9517958SDerek J. Clark 
811a9517958SDerek J. Clark static ssize_t mkeys_function_index_show(struct device *dev,
812a9517958SDerek J. Clark 					 struct device_attribute *attr, char *buf)
813a9517958SDerek J. Clark {
814a9517958SDerek J. Clark 	int i, count = 0;
815a9517958SDerek J. Clark 
816a9517958SDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(claw_mkeys_function_text); i++)
817a9517958SDerek J. Clark 		count += sysfs_emit_at(buf, count, "%s ", claw_mkeys_function_text[i]);
818a9517958SDerek J. Clark 
819a9517958SDerek J. Clark 	if (count)
820a9517958SDerek J. Clark 		buf[count - 1] = '\n';
821a9517958SDerek J. Clark 
822a9517958SDerek J. Clark 	return count;
823a9517958SDerek J. Clark }
824a9517958SDerek J. Clark static DEVICE_ATTR_RO(mkeys_function_index);
825a9517958SDerek J. Clark 
826a9517958SDerek J. Clark static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
827a9517958SDerek J. Clark 			   const char *buf, size_t count)
828a9517958SDerek J. Clark {
829a9517958SDerek J. Clark 	struct hid_device *hdev = to_hid_device(dev);
830a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
831a9517958SDerek J. Clark 	bool val;
832a9517958SDerek J. Clark 	int ret;
833a9517958SDerek J. Clark 
834a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
835a9517958SDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
836a9517958SDerek J. Clark 		if (!smp_load_acquire(&drvdata->gp_registered))
837a9517958SDerek J. Clark 			return -ENODEV;
838a9517958SDerek J. Clark 	}
839a9517958SDerek J. Clark 
840a9517958SDerek J. Clark 	ret = kstrtobool(buf, &val);
841a9517958SDerek J. Clark 	if (ret)
842a9517958SDerek J. Clark 		return ret;
843a9517958SDerek J. Clark 
844a9517958SDerek J. Clark 	if (!val)
845a9517958SDerek J. Clark 		return -EINVAL;
846a9517958SDerek J. Clark 
847a9517958SDerek J. Clark 	ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_RESET_DEVICE, NULL, 0, 0);
848a9517958SDerek J. Clark 	if (ret)
849a9517958SDerek J. Clark 		return ret;
850a9517958SDerek J. Clark 
851a9517958SDerek J. Clark 	return count;
852a9517958SDerek J. Clark }
853a9517958SDerek J. Clark static DEVICE_ATTR_WO(reset);
854a9517958SDerek J. Clark 
85593ff13edSDerek J. Clark static int mkey_mapping_name_to_code(const char *name)
85693ff13edSDerek J. Clark {
85793ff13edSDerek J. Clark 	int i;
85893ff13edSDerek J. Clark 
85993ff13edSDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(claw_button_mapping_key_map); i++) {
86093ff13edSDerek J. Clark 		if (!strcmp(name, claw_button_mapping_key_map[i].name))
86193ff13edSDerek J. Clark 			return claw_button_mapping_key_map[i].code;
86293ff13edSDerek J. Clark 	}
86393ff13edSDerek J. Clark 
86493ff13edSDerek J. Clark 	return -EINVAL;
86593ff13edSDerek J. Clark }
86693ff13edSDerek J. Clark 
86793ff13edSDerek J. Clark static const char *mkey_mapping_code_to_name(u8 code)
86893ff13edSDerek J. Clark {
86993ff13edSDerek J. Clark 	int i;
87093ff13edSDerek J. Clark 
87193ff13edSDerek J. Clark 	if (code == 0xff)
87293ff13edSDerek J. Clark 		return NULL;
87393ff13edSDerek J. Clark 
87493ff13edSDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(claw_button_mapping_key_map); i++) {
87593ff13edSDerek J. Clark 		if (claw_button_mapping_key_map[i].code == code)
87693ff13edSDerek J. Clark 			return claw_button_mapping_key_map[i].name;
87793ff13edSDerek J. Clark 	}
87893ff13edSDerek J. Clark 
87993ff13edSDerek J. Clark 	return NULL;
88093ff13edSDerek J. Clark }
88193ff13edSDerek J. Clark 
88293ff13edSDerek J. Clark static int claw_mkey_store(struct device *dev, const char *buf, u8 mkey)
88393ff13edSDerek J. Clark {
88493ff13edSDerek J. Clark 	struct hid_device *hdev = to_hid_device(dev);
88593ff13edSDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
88693ff13edSDerek J. Clark 	struct claw_mkey_report report = { {0x01, cpu_to_be16(drvdata->bmap_addr[mkey])},
88793ff13edSDerek J. Clark 				   0x07, 0x04, 0x00, {0xff, 0xff, 0xff, 0xff, 0xff} };
88893ff13edSDerek J. Clark 	char **raw_keys __free(argv_free) = NULL;
88993ff13edSDerek J. Clark 	int ret, key_count, i;
89093ff13edSDerek J. Clark 
89193ff13edSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
89293ff13edSDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
89393ff13edSDerek J. Clark 		if (!smp_load_acquire(&drvdata->gp_registered))
89493ff13edSDerek J. Clark 			return -ENODEV;
89593ff13edSDerek J. Clark 	}
89693ff13edSDerek J. Clark 
89793ff13edSDerek J. Clark 	raw_keys = argv_split(GFP_KERNEL, buf, &key_count);
89893ff13edSDerek J. Clark 	if (!raw_keys)
89993ff13edSDerek J. Clark 		return -ENOMEM;
90093ff13edSDerek J. Clark 
90193ff13edSDerek J. Clark 	if (key_count > CLAW_KEYS_MAX)
90293ff13edSDerek J. Clark 		return -EINVAL;
90393ff13edSDerek J. Clark 
90493ff13edSDerek J. Clark 	if (key_count == 0)
90593ff13edSDerek J. Clark 		goto set_buttons;
90693ff13edSDerek J. Clark 
90793ff13edSDerek J. Clark 	for (i = 0; i < key_count; i++) {
90893ff13edSDerek J. Clark 		ret = mkey_mapping_name_to_code(raw_keys[i]);
90993ff13edSDerek J. Clark 		if (ret < 0)
91093ff13edSDerek J. Clark 			return ret;
91193ff13edSDerek J. Clark 
91293ff13edSDerek J. Clark 		report.codes[i] = ret;
91393ff13edSDerek J. Clark 	}
91493ff13edSDerek J. Clark 
91593ff13edSDerek J. Clark set_buttons:
91693ff13edSDerek J. Clark 	scoped_guard(mutex, &drvdata->rom_mutex) {
91793ff13edSDerek J. Clark 		ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_WRITE_PROFILE_DATA,
91893ff13edSDerek J. Clark 					    (u8 *)&report, sizeof(report), 25);
91993ff13edSDerek J. Clark 		if (ret)
92093ff13edSDerek J. Clark 			return ret;
92193ff13edSDerek J. Clark 		/* MCU will not send ACK until the USB transaction completes. ACK is sent
92293ff13edSDerek J. Clark 		 * immediately after and will hit the stale state machine, before the next
92393ff13edSDerek J. Clark 		 * command re-arms the state machine. Timeout 0 ensures no deadlock waiting
92493ff13edSDerek J. Clark 		 * for ACK that ill never come.
92593ff13edSDerek J. Clark 		 */
92693ff13edSDerek J. Clark 		ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_SYNC_TO_ROM, NULL, 0, 0);
92793ff13edSDerek J. Clark 	}
92893ff13edSDerek J. Clark 
92993ff13edSDerek J. Clark 	return ret;
93093ff13edSDerek J. Clark }
93193ff13edSDerek J. Clark 
93293ff13edSDerek J. Clark static int claw_mkey_show(struct device *dev, char *buf, enum claw_key_index m_key)
93393ff13edSDerek J. Clark {
93493ff13edSDerek J. Clark 	struct hid_device *hdev = to_hid_device(dev);
93593ff13edSDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
93693ff13edSDerek J. Clark 	struct claw_mkey_report report = { {0x01, cpu_to_be16(drvdata->bmap_addr[m_key])}, 0x07 };
93793ff13edSDerek J. Clark 	int i, ret, count = 0;
93893ff13edSDerek J. Clark 	const char *name;
93993ff13edSDerek J. Clark 	u8 *codes;
94093ff13edSDerek J. Clark 
94193ff13edSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
94293ff13edSDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
94393ff13edSDerek J. Clark 		if (!smp_load_acquire(&drvdata->gp_registered))
94493ff13edSDerek J. Clark 			return -ENODEV;
94593ff13edSDerek J. Clark 	}
94693ff13edSDerek J. Clark 
94793ff13edSDerek J. Clark 	codes = (m_key == CLAW_KEY_M1) ? drvdata->m1_codes : drvdata->m2_codes;
94893ff13edSDerek J. Clark 
94993ff13edSDerek J. Clark 	guard(mutex)(&drvdata->profile_mutex);
95093ff13edSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->profile_lock)
95193ff13edSDerek J. Clark 		drvdata->profile_pending = (m_key == CLAW_KEY_M1) ? CLAW_M1_PENDING
95293ff13edSDerek J. Clark 								  : CLAW_M2_PENDING;
95393ff13edSDerek J. Clark 
95493ff13edSDerek J. Clark 	ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_READ_PROFILE,
95593ff13edSDerek J. Clark 				    (u8 *)&report, sizeof(report), 25);
95693ff13edSDerek J. Clark 	if (ret)
95793ff13edSDerek J. Clark 		return ret;
95893ff13edSDerek J. Clark 
95993ff13edSDerek J. Clark 	for (i = 0; i < CLAW_KEYS_MAX; i++) {
96093ff13edSDerek J. Clark 		name = mkey_mapping_code_to_name(codes[i]);
96193ff13edSDerek J. Clark 		if (name)
96293ff13edSDerek J. Clark 			count += sysfs_emit_at(buf, count, "%s ", name);
96393ff13edSDerek J. Clark 	}
96493ff13edSDerek J. Clark 
96593ff13edSDerek J. Clark 	if (!count)
96693ff13edSDerek J. Clark 		return sysfs_emit(buf, "(not set)\n");
96793ff13edSDerek J. Clark 
96893ff13edSDerek J. Clark 	buf[count - 1] = '\n';
96993ff13edSDerek J. Clark 
97093ff13edSDerek J. Clark 	return count;
97193ff13edSDerek J. Clark }
97293ff13edSDerek J. Clark 
97393ff13edSDerek J. Clark static ssize_t button_m1_store(struct device *dev, struct device_attribute *attr,
97493ff13edSDerek J. Clark 			       const char *buf, size_t count)
97593ff13edSDerek J. Clark {
97693ff13edSDerek J. Clark 	int ret;
97793ff13edSDerek J. Clark 
97893ff13edSDerek J. Clark 	ret = claw_mkey_store(dev, buf, CLAW_KEY_M1);
97993ff13edSDerek J. Clark 	if (ret)
98093ff13edSDerek J. Clark 		return ret;
98193ff13edSDerek J. Clark 
98293ff13edSDerek J. Clark 	return count;
98393ff13edSDerek J. Clark }
98493ff13edSDerek J. Clark 
98593ff13edSDerek J. Clark static ssize_t button_m1_show(struct device *dev, struct device_attribute *attr,
98693ff13edSDerek J. Clark 			      char *buf)
98793ff13edSDerek J. Clark {
98893ff13edSDerek J. Clark 	return claw_mkey_show(dev, buf, CLAW_KEY_M1);
98993ff13edSDerek J. Clark }
99093ff13edSDerek J. Clark static DEVICE_ATTR_RW(button_m1);
99193ff13edSDerek J. Clark 
99293ff13edSDerek J. Clark static ssize_t button_m2_store(struct device *dev, struct device_attribute *attr,
99393ff13edSDerek J. Clark 			       const char *buf, size_t count)
99493ff13edSDerek J. Clark {
99593ff13edSDerek J. Clark 	int ret;
99693ff13edSDerek J. Clark 
99793ff13edSDerek J. Clark 	ret = claw_mkey_store(dev, buf, CLAW_KEY_M2);
99893ff13edSDerek J. Clark 	if (ret)
99993ff13edSDerek J. Clark 		return ret;
100093ff13edSDerek J. Clark 
100193ff13edSDerek J. Clark 	return count;
100293ff13edSDerek J. Clark }
100393ff13edSDerek J. Clark 
100493ff13edSDerek J. Clark static ssize_t button_m2_show(struct device *dev, struct device_attribute *attr,
100593ff13edSDerek J. Clark 			      char *buf)
100693ff13edSDerek J. Clark {
100793ff13edSDerek J. Clark 	return claw_mkey_show(dev, buf, CLAW_KEY_M2);
100893ff13edSDerek J. Clark }
100993ff13edSDerek J. Clark static DEVICE_ATTR_RW(button_m2);
101093ff13edSDerek J. Clark 
101193ff13edSDerek J. Clark static ssize_t button_mapping_options_show(struct device *dev,
101293ff13edSDerek J. Clark 					   struct device_attribute *attr, char *buf)
101393ff13edSDerek J. Clark {
101493ff13edSDerek J. Clark 	int i, count = 0;
101593ff13edSDerek J. Clark 
101693ff13edSDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(claw_button_mapping_key_map); i++)
101793ff13edSDerek J. Clark 		count += sysfs_emit_at(buf, count, "%s ", claw_button_mapping_key_map[i].name);
101893ff13edSDerek J. Clark 
101993ff13edSDerek J. Clark 	if (count)
102093ff13edSDerek J. Clark 		buf[count - 1] = '\n';
102193ff13edSDerek J. Clark 
102293ff13edSDerek J. Clark 	return count;
102393ff13edSDerek J. Clark }
102493ff13edSDerek J. Clark static DEVICE_ATTR_RO(button_mapping_options);
102593ff13edSDerek J. Clark 
1026*95547c53SDerek J. Clark static ssize_t rumble_intensity_left_store(struct device *dev,
1027*95547c53SDerek J. Clark 					   struct device_attribute *attr,
1028*95547c53SDerek J. Clark 					   const char *buf, size_t count)
1029*95547c53SDerek J. Clark {
1030*95547c53SDerek J. Clark 	struct claw_rumble_report report = { {0x01, cpu_to_be16(rumble_addr[0])}, 0x01 };
1031*95547c53SDerek J. Clark 	struct hid_device *hdev = to_hid_device(dev);
1032*95547c53SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
1033*95547c53SDerek J. Clark 	u8 val;
1034*95547c53SDerek J. Clark 	int ret;
1035*95547c53SDerek J. Clark 
1036*95547c53SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1037*95547c53SDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1038*95547c53SDerek J. Clark 		if (!smp_load_acquire(&drvdata->gp_registered))
1039*95547c53SDerek J. Clark 			return -ENODEV;
1040*95547c53SDerek J. Clark 	}
1041*95547c53SDerek J. Clark 
1042*95547c53SDerek J. Clark 	ret = kstrtou8(buf, 10, &val);
1043*95547c53SDerek J. Clark 	if (ret)
1044*95547c53SDerek J. Clark 		return ret;
1045*95547c53SDerek J. Clark 
1046*95547c53SDerek J. Clark 	if (val > 100)
1047*95547c53SDerek J. Clark 		return -EINVAL;
1048*95547c53SDerek J. Clark 
1049*95547c53SDerek J. Clark 	report.intensity = val;
1050*95547c53SDerek J. Clark 
1051*95547c53SDerek J. Clark 	guard(mutex)(&drvdata->rom_mutex);
1052*95547c53SDerek J. Clark 	ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_WRITE_PROFILE_DATA,
1053*95547c53SDerek J. Clark 				    (u8 *)&report, sizeof(report), 25);
1054*95547c53SDerek J. Clark 	if (ret)
1055*95547c53SDerek J. Clark 		return ret;
1056*95547c53SDerek J. Clark 
1057*95547c53SDerek J. Clark 	/* MCU will not send ACK until the USB transaction completes. ACK is sent
1058*95547c53SDerek J. Clark 	 * immediately after and will hit the stale state machine, before the next
1059*95547c53SDerek J. Clark 	 * command re-arms the state machine. Timeout 0 ensures no deadlock waiting
1060*95547c53SDerek J. Clark 	 * for ACK that ill never come.
1061*95547c53SDerek J. Clark 	 */
1062*95547c53SDerek J. Clark 	ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_SYNC_TO_ROM, NULL, 0, 0);
1063*95547c53SDerek J. Clark 	if (ret)
1064*95547c53SDerek J. Clark 		return ret;
1065*95547c53SDerek J. Clark 
1066*95547c53SDerek J. Clark 	return count;
1067*95547c53SDerek J. Clark }
1068*95547c53SDerek J. Clark 
1069*95547c53SDerek J. Clark static ssize_t rumble_intensity_left_show(struct device *dev,
1070*95547c53SDerek J. Clark 					  struct device_attribute *attr,
1071*95547c53SDerek J. Clark 					  char *buf)
1072*95547c53SDerek J. Clark {
1073*95547c53SDerek J. Clark 	struct claw_rumble_report report = { {0x01, cpu_to_be16(rumble_addr[0])}, 0x01 };
1074*95547c53SDerek J. Clark 	struct hid_device *hdev = to_hid_device(dev);
1075*95547c53SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
1076*95547c53SDerek J. Clark 	int ret;
1077*95547c53SDerek J. Clark 	u8 val;
1078*95547c53SDerek J. Clark 
1079*95547c53SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1080*95547c53SDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1081*95547c53SDerek J. Clark 		if (!smp_load_acquire(&drvdata->gp_registered))
1082*95547c53SDerek J. Clark 			return -ENODEV;
1083*95547c53SDerek J. Clark 	}
1084*95547c53SDerek J. Clark 
1085*95547c53SDerek J. Clark 	guard(mutex)(&drvdata->profile_mutex);
1086*95547c53SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->profile_lock)
1087*95547c53SDerek J. Clark 		drvdata->profile_pending = CLAW_RUMBLE_LEFT_PENDING;
1088*95547c53SDerek J. Clark 	ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_READ_PROFILE,
1089*95547c53SDerek J. Clark 				    (u8 *)&report, sizeof(report), 25);
1090*95547c53SDerek J. Clark 	if (ret)
1091*95547c53SDerek J. Clark 		return ret;
1092*95547c53SDerek J. Clark 
1093*95547c53SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->rumble_lock)
1094*95547c53SDerek J. Clark 		val = drvdata->rumble_intensity_left;
1095*95547c53SDerek J. Clark 
1096*95547c53SDerek J. Clark 	return sysfs_emit(buf, "%u\n", val);
1097*95547c53SDerek J. Clark }
1098*95547c53SDerek J. Clark static DEVICE_ATTR_RW(rumble_intensity_left);
1099*95547c53SDerek J. Clark 
1100*95547c53SDerek J. Clark static ssize_t rumble_intensity_right_store(struct device *dev,
1101*95547c53SDerek J. Clark 					    struct device_attribute *attr,
1102*95547c53SDerek J. Clark 					    const char *buf, size_t count)
1103*95547c53SDerek J. Clark {
1104*95547c53SDerek J. Clark 	struct claw_rumble_report report = { {0x01, cpu_to_be16(rumble_addr[1])}, 0x01 };
1105*95547c53SDerek J. Clark 	struct hid_device *hdev = to_hid_device(dev);
1106*95547c53SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
1107*95547c53SDerek J. Clark 	u8 val;
1108*95547c53SDerek J. Clark 	int ret;
1109*95547c53SDerek J. Clark 
1110*95547c53SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1111*95547c53SDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1112*95547c53SDerek J. Clark 		if (!smp_load_acquire(&drvdata->gp_registered))
1113*95547c53SDerek J. Clark 			return -ENODEV;
1114*95547c53SDerek J. Clark 	}
1115*95547c53SDerek J. Clark 
1116*95547c53SDerek J. Clark 	ret = kstrtou8(buf, 10, &val);
1117*95547c53SDerek J. Clark 	if (ret)
1118*95547c53SDerek J. Clark 		return ret;
1119*95547c53SDerek J. Clark 
1120*95547c53SDerek J. Clark 	if (val > 100)
1121*95547c53SDerek J. Clark 		return -EINVAL;
1122*95547c53SDerek J. Clark 
1123*95547c53SDerek J. Clark 	report.intensity = val;
1124*95547c53SDerek J. Clark 
1125*95547c53SDerek J. Clark 	guard(mutex)(&drvdata->rom_mutex);
1126*95547c53SDerek J. Clark 	ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_WRITE_PROFILE_DATA,
1127*95547c53SDerek J. Clark 				    (u8 *)&report, sizeof(report), 25);
1128*95547c53SDerek J. Clark 	if (ret)
1129*95547c53SDerek J. Clark 		return ret;
1130*95547c53SDerek J. Clark 
1131*95547c53SDerek J. Clark 	/* MCU will not send ACK until the USB transaction completes. ACK is sent
1132*95547c53SDerek J. Clark 	 * immediately after and will hit the stale state machine, before the next
1133*95547c53SDerek J. Clark 	 * command re-arms the state machine. Timeout 0 ensures no deadlock waiting
1134*95547c53SDerek J. Clark 	 * for ACK that ill never come.
1135*95547c53SDerek J. Clark 	 */
1136*95547c53SDerek J. Clark 	ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_SYNC_TO_ROM, NULL, 0, 0);
1137*95547c53SDerek J. Clark 	if (ret)
1138*95547c53SDerek J. Clark 		return ret;
1139*95547c53SDerek J. Clark 
1140*95547c53SDerek J. Clark 	return count;
1141*95547c53SDerek J. Clark }
1142*95547c53SDerek J. Clark 
1143*95547c53SDerek J. Clark static ssize_t rumble_intensity_right_show(struct device *dev,
1144*95547c53SDerek J. Clark 					   struct device_attribute *attr,
1145*95547c53SDerek J. Clark 					   char *buf)
1146*95547c53SDerek J. Clark {
1147*95547c53SDerek J. Clark 	struct claw_rumble_report report = { {0x01, cpu_to_be16(rumble_addr[1])}, 0x01 };
1148*95547c53SDerek J. Clark 	struct hid_device *hdev = to_hid_device(dev);
1149*95547c53SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
1150*95547c53SDerek J. Clark 	int ret;
1151*95547c53SDerek J. Clark 	u8 val;
1152*95547c53SDerek J. Clark 
1153*95547c53SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1154*95547c53SDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1155*95547c53SDerek J. Clark 		if (!smp_load_acquire(&drvdata->gp_registered))
1156*95547c53SDerek J. Clark 			return -ENODEV;
1157*95547c53SDerek J. Clark 	}
1158*95547c53SDerek J. Clark 
1159*95547c53SDerek J. Clark 	guard(mutex)(&drvdata->profile_mutex);
1160*95547c53SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->profile_lock)
1161*95547c53SDerek J. Clark 		drvdata->profile_pending = CLAW_RUMBLE_RIGHT_PENDING;
1162*95547c53SDerek J. Clark 	ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_READ_PROFILE,
1163*95547c53SDerek J. Clark 				    (u8 *)&report, sizeof(report), 25);
1164*95547c53SDerek J. Clark 	if (ret)
1165*95547c53SDerek J. Clark 		return ret;
1166*95547c53SDerek J. Clark 
1167*95547c53SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->rumble_lock)
1168*95547c53SDerek J. Clark 		val = drvdata->rumble_intensity_right;
1169*95547c53SDerek J. Clark 
1170*95547c53SDerek J. Clark 	return sysfs_emit(buf, "%u\n", val);
1171*95547c53SDerek J. Clark }
1172*95547c53SDerek J. Clark static DEVICE_ATTR_RW(rumble_intensity_right);
1173*95547c53SDerek J. Clark 
1174*95547c53SDerek J. Clark static ssize_t rumble_intensity_range_show(struct device *dev,
1175*95547c53SDerek J. Clark 					   struct device_attribute *attr,
1176*95547c53SDerek J. Clark 					   char *buf)
1177*95547c53SDerek J. Clark {
1178*95547c53SDerek J. Clark 	return sysfs_emit(buf, "0-100\n");
1179*95547c53SDerek J. Clark }
1180*95547c53SDerek J. Clark static DEVICE_ATTR_RO(rumble_intensity_range);
1181*95547c53SDerek J. Clark 
1182a9517958SDerek J. Clark static umode_t claw_gamepad_attr_is_visible(struct kobject *kobj, struct attribute *attr,
1183a9517958SDerek J. Clark 					    int n)
1184a9517958SDerek J. Clark {
1185a9517958SDerek J. Clark 	struct hid_device *hdev = to_hid_device(kobj_to_dev(kobj));
1186a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
1187a9517958SDerek J. Clark 
1188a9517958SDerek J. Clark 	if (!drvdata) {
1189a9517958SDerek J. Clark 		dev_warn(&hdev->dev,
1190a9517958SDerek J. Clark 			 "Failed to get drvdata from kobj. Gamepad attributes are not available.\n");
1191a9517958SDerek J. Clark 		return 0;
1192a9517958SDerek J. Clark 	}
1193a9517958SDerek J. Clark 
119493ff13edSDerek J. Clark 	/* Always show attrs available on all firmware */
119593ff13edSDerek J. Clark 	if (attr == &dev_attr_gamepad_mode.attr ||
119693ff13edSDerek J. Clark 	    attr == &dev_attr_gamepad_mode_index.attr ||
119793ff13edSDerek J. Clark 	    attr == &dev_attr_mkeys_function.attr ||
119893ff13edSDerek J. Clark 	    attr == &dev_attr_mkeys_function_index.attr ||
119993ff13edSDerek J. Clark 	    attr == &dev_attr_reset.attr)
1200a9517958SDerek J. Clark 		return attr->mode;
120193ff13edSDerek J. Clark 
1202*95547c53SDerek J. Clark 	/* Hide rumble attrs if not supported */
1203*95547c53SDerek J. Clark 	if (attr == &dev_attr_rumble_intensity_left.attr ||
1204*95547c53SDerek J. Clark 	    attr == &dev_attr_rumble_intensity_right.attr ||
1205*95547c53SDerek J. Clark 	    attr == &dev_attr_rumble_intensity_range.attr)
1206*95547c53SDerek J. Clark 		return drvdata->rumble_support ? attr->mode : 0;
1207*95547c53SDerek J. Clark 
120893ff13edSDerek J. Clark 	/* Hide button mapping attrs if it isn't supported */
120993ff13edSDerek J. Clark 	return drvdata->bmap_support ? attr->mode : 0;
1210a9517958SDerek J. Clark }
1211a9517958SDerek J. Clark 
1212a9517958SDerek J. Clark static struct attribute *claw_gamepad_attrs[] = {
121393ff13edSDerek J. Clark 	&dev_attr_button_m1.attr,
121493ff13edSDerek J. Clark 	&dev_attr_button_m2.attr,
121593ff13edSDerek J. Clark 	&dev_attr_button_mapping_options.attr,
1216a9517958SDerek J. Clark 	&dev_attr_gamepad_mode.attr,
1217a9517958SDerek J. Clark 	&dev_attr_gamepad_mode_index.attr,
1218a9517958SDerek J. Clark 	&dev_attr_mkeys_function.attr,
1219a9517958SDerek J. Clark 	&dev_attr_mkeys_function_index.attr,
1220a9517958SDerek J. Clark 	&dev_attr_reset.attr,
1221*95547c53SDerek J. Clark 	&dev_attr_rumble_intensity_left.attr,
1222*95547c53SDerek J. Clark 	&dev_attr_rumble_intensity_right.attr,
1223*95547c53SDerek J. Clark 	&dev_attr_rumble_intensity_range.attr,
1224a9517958SDerek J. Clark 	NULL,
1225a9517958SDerek J. Clark };
1226a9517958SDerek J. Clark 
1227a9517958SDerek J. Clark static const struct attribute_group claw_gamepad_attr_group = {
1228a9517958SDerek J. Clark 	.attrs = claw_gamepad_attrs,
1229a9517958SDerek J. Clark 	.is_visible = claw_gamepad_attr_is_visible,
1230a9517958SDerek J. Clark };
1231a9517958SDerek J. Clark 
1232f27e2e3cSDerek J. Clark /* Read RGB config from device */
1233f27e2e3cSDerek J. Clark static int claw_read_rgb_config(struct hid_device *hdev)
1234f27e2e3cSDerek J. Clark {
1235f27e2e3cSDerek J. Clark 	u8 data[4] = { 0x01, 0x00, 0x00, CLAW_RGB_FRAME_OFFSET };
1236f27e2e3cSDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
1237f27e2e3cSDerek J. Clark 	u16 read_addr = drvdata->rgb_addr;
1238f27e2e3cSDerek J. Clark 	size_t len = ARRAY_SIZE(data);
1239f27e2e3cSDerek J. Clark 	int ret, i;
1240f27e2e3cSDerek J. Clark 
1241f27e2e3cSDerek J. Clark 	if (!drvdata->rgb_addr)
1242f27e2e3cSDerek J. Clark 		return -ENODEV;
1243f27e2e3cSDerek J. Clark 
1244f27e2e3cSDerek J. Clark 	/* Loop through all 8 pages of RGB data */
1245f27e2e3cSDerek J. Clark 	guard(mutex)(&drvdata->profile_mutex);
1246f27e2e3cSDerek J. Clark 	for (i = 0; i < CLAW_RGB_MAX_FRAMES; i++) {
1247f27e2e3cSDerek J. Clark 		scoped_guard(spinlock_irqsave, &drvdata->profile_lock)
1248f27e2e3cSDerek J. Clark 			drvdata->profile_pending = CLAW_RGB_PENDING;
1249f27e2e3cSDerek J. Clark 		data[1] = (read_addr >> 8) & 0xff;
1250f27e2e3cSDerek J. Clark 		data[2] = read_addr & 0x00ff;
1251f27e2e3cSDerek J. Clark 		ret = claw_hw_output_report(hdev, CLAW_COMMAND_TYPE_READ_PROFILE, data, len, 25);
1252f27e2e3cSDerek J. Clark 		if (ret)
1253f27e2e3cSDerek J. Clark 			return ret;
1254f27e2e3cSDerek J. Clark 
1255f27e2e3cSDerek J. Clark 		read_addr += CLAW_RGB_FRAME_OFFSET;
1256f27e2e3cSDerek J. Clark 	}
1257f27e2e3cSDerek J. Clark 
1258f27e2e3cSDerek J. Clark 	return 0;
1259f27e2e3cSDerek J. Clark }
1260f27e2e3cSDerek J. Clark 
1261f27e2e3cSDerek J. Clark /* Send RGB configuration to device */
1262f27e2e3cSDerek J. Clark static int claw_write_rgb_state(struct claw_drvdata *drvdata)
1263f27e2e3cSDerek J. Clark {
1264f27e2e3cSDerek J. Clark 	struct claw_rgb_report report = { {0x01, 0}, CLAW_RGB_FRAME_OFFSET, 0x00,
1265f27e2e3cSDerek J. Clark 			drvdata->rgb_frame_count, 0x09, drvdata->rgb_speed,
1266f27e2e3cSDerek J. Clark 			drvdata->led_mc.led_cdev.brightness };
1267f27e2e3cSDerek J. Clark 	u16 write_addr = drvdata->rgb_addr;
1268f27e2e3cSDerek J. Clark 	int f, ret;
1269f27e2e3cSDerek J. Clark 
1270f27e2e3cSDerek J. Clark 	if (!drvdata->rgb_addr)
1271f27e2e3cSDerek J. Clark 		return -ENODEV;
1272f27e2e3cSDerek J. Clark 
1273f27e2e3cSDerek J. Clark 	if (!drvdata->rgb_frame_count)
1274f27e2e3cSDerek J. Clark 		return -EINVAL;
1275f27e2e3cSDerek J. Clark 
1276f27e2e3cSDerek J. Clark 	guard(mutex)(&drvdata->rom_mutex);
1277f27e2e3cSDerek J. Clark 	/* Loop through (up to) 8 pages of RGB data */
1278f27e2e3cSDerek J. Clark 	for (f = 0; f < drvdata->rgb_frame_count; f++) {
1279f27e2e3cSDerek J. Clark 		scoped_guard(spinlock_irqsave, &drvdata->frame_lock)
1280f27e2e3cSDerek J. Clark 			report.zone_data = drvdata->rgb_frames[f];
1281f27e2e3cSDerek J. Clark 
1282f27e2e3cSDerek J. Clark 		/* Set the MCU address to write the frame data to */
1283f27e2e3cSDerek J. Clark 		report.read_addr = cpu_to_be16(write_addr);
1284f27e2e3cSDerek J. Clark 
1285f27e2e3cSDerek J. Clark 		/* Serialize the rgb_report and write it to MCU */
1286f27e2e3cSDerek J. Clark 		ret = claw_hw_output_report(drvdata->hdev, CLAW_COMMAND_TYPE_WRITE_PROFILE_DATA,
1287f27e2e3cSDerek J. Clark 					    (u8 *)&report, sizeof(report), 25);
1288f27e2e3cSDerek J. Clark 		if (ret)
1289f27e2e3cSDerek J. Clark 			return ret;
1290f27e2e3cSDerek J. Clark 
1291f27e2e3cSDerek J. Clark 		/* Increment the write addr by the offset for the next frame */
1292f27e2e3cSDerek J. Clark 		write_addr += CLAW_RGB_FRAME_OFFSET;
1293f27e2e3cSDerek J. Clark 	}
1294f27e2e3cSDerek J. Clark 
1295f27e2e3cSDerek J. Clark 	/* MCU will not send ACK until the USB transaction completes. ACK is sent
1296f27e2e3cSDerek J. Clark 	 * immediately after and will hit the stale state machine, before the next
1297f27e2e3cSDerek J. Clark 	 * command re-arms the state machine. Timeout 0 ensures no deadlock waiting
1298f27e2e3cSDerek J. Clark 	 * for ACK that ill never come.
1299f27e2e3cSDerek J. Clark 	 */
1300f27e2e3cSDerek J. Clark 	ret = claw_hw_output_report(drvdata->hdev, CLAW_COMMAND_TYPE_SYNC_TO_ROM, NULL, 0, 0);
1301f27e2e3cSDerek J. Clark 
1302f27e2e3cSDerek J. Clark 	return ret;
1303f27e2e3cSDerek J. Clark }
1304f27e2e3cSDerek J. Clark 
1305f27e2e3cSDerek J. Clark /* Fill all zones with the same color */
1306f27e2e3cSDerek J. Clark static void claw_frame_fill_solid(struct rgb_frame *frame, struct rgb_zone zone)
1307f27e2e3cSDerek J. Clark {
1308f27e2e3cSDerek J. Clark 	int z;
1309f27e2e3cSDerek J. Clark 
1310f27e2e3cSDerek J. Clark 	for (z = 0; z < CLAW_RGB_ZONES; z++)
1311f27e2e3cSDerek J. Clark 		frame->zone[z] = zone;
1312f27e2e3cSDerek J. Clark }
1313f27e2e3cSDerek J. Clark 
1314f27e2e3cSDerek J. Clark /* Apply solid effect (1 frame, no color) */
1315f27e2e3cSDerek J. Clark static int claw_apply_disabled(struct claw_drvdata *drvdata)
1316f27e2e3cSDerek J. Clark {
1317f27e2e3cSDerek J. Clark 	struct rgb_zone off = { 0x00, 0x00, 0x00};
1318f27e2e3cSDerek J. Clark 
1319f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->frame_lock) {
1320f27e2e3cSDerek J. Clark 		drvdata->rgb_frame_count = 1;
1321f27e2e3cSDerek J. Clark 		claw_frame_fill_solid(&drvdata->rgb_frames[0], off);
1322f27e2e3cSDerek J. Clark 	}
1323f27e2e3cSDerek J. Clark 
1324f27e2e3cSDerek J. Clark 	return claw_write_rgb_state(drvdata);
1325f27e2e3cSDerek J. Clark }
1326f27e2e3cSDerek J. Clark 
1327f27e2e3cSDerek J. Clark /* Apply solid effect (1 frame, all zones same color) */
1328f27e2e3cSDerek J. Clark static int claw_apply_monocolor(struct claw_drvdata *drvdata)
1329f27e2e3cSDerek J. Clark {
1330f27e2e3cSDerek J. Clark 	struct mc_subled *subleds = drvdata->led_mc.subled_info;
1331f27e2e3cSDerek J. Clark 	struct rgb_zone zone = { subleds[0].intensity, subleds[1].intensity,
1332f27e2e3cSDerek J. Clark 				 subleds[2].intensity };
1333f27e2e3cSDerek J. Clark 
1334f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->frame_lock) {
1335f27e2e3cSDerek J. Clark 		drvdata->rgb_frame_count = 1;
1336f27e2e3cSDerek J. Clark 		claw_frame_fill_solid(&drvdata->rgb_frames[0], zone);
1337f27e2e3cSDerek J. Clark 	}
1338f27e2e3cSDerek J. Clark 
1339f27e2e3cSDerek J. Clark 	return claw_write_rgb_state(drvdata);
1340f27e2e3cSDerek J. Clark }
1341f27e2e3cSDerek J. Clark 
1342f27e2e3cSDerek J. Clark /* Apply breathe effect (2 frames: color -> off) */
1343f27e2e3cSDerek J. Clark static int claw_apply_breathe(struct claw_drvdata *drvdata)
1344f27e2e3cSDerek J. Clark {
1345f27e2e3cSDerek J. Clark 	struct mc_subled *subleds = drvdata->led_mc.subled_info;
1346f27e2e3cSDerek J. Clark 	struct rgb_zone zone = { subleds[0].intensity, subleds[1].intensity,
1347f27e2e3cSDerek J. Clark 				 subleds[2].intensity };
1348f27e2e3cSDerek J. Clark 	static const struct rgb_zone off = { 0, 0, 0 };
1349f27e2e3cSDerek J. Clark 
1350f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->frame_lock) {
1351f27e2e3cSDerek J. Clark 		drvdata->rgb_frame_count = 2;
1352f27e2e3cSDerek J. Clark 		claw_frame_fill_solid(&drvdata->rgb_frames[0], zone);
1353f27e2e3cSDerek J. Clark 		claw_frame_fill_solid(&drvdata->rgb_frames[1], off);
1354f27e2e3cSDerek J. Clark 	}
1355f27e2e3cSDerek J. Clark 
1356f27e2e3cSDerek J. Clark 	return claw_write_rgb_state(drvdata);
1357f27e2e3cSDerek J. Clark }
1358f27e2e3cSDerek J. Clark 
1359f27e2e3cSDerek J. Clark /* Apply chroma effect (6 frames: rainbow cycle, all zones sync) */
1360f27e2e3cSDerek J. Clark static int claw_apply_chroma(struct claw_drvdata *drvdata)
1361f27e2e3cSDerek J. Clark {
1362f27e2e3cSDerek J. Clark 	static const struct rgb_zone colors[] = {
1363f27e2e3cSDerek J. Clark 		{255,   0,   0},  /* red     */
1364f27e2e3cSDerek J. Clark 		{255, 255,   0},  /* yellow  */
1365f27e2e3cSDerek J. Clark 		{  0, 255,   0},  /* green   */
1366f27e2e3cSDerek J. Clark 		{  0, 255, 255},  /* cyan    */
1367f27e2e3cSDerek J. Clark 		{  0,   0, 255},  /* blue    */
1368f27e2e3cSDerek J. Clark 		{255,   0, 255},  /* magenta */
1369f27e2e3cSDerek J. Clark 	};
1370f27e2e3cSDerek J. Clark 	u8 frame_count = ARRAY_SIZE(colors);
1371f27e2e3cSDerek J. Clark 	int f;
1372f27e2e3cSDerek J. Clark 
1373f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->frame_lock) {
1374f27e2e3cSDerek J. Clark 		drvdata->rgb_frame_count = frame_count;
1375f27e2e3cSDerek J. Clark 
1376f27e2e3cSDerek J. Clark 		for (f = 0; f < frame_count; f++)
1377f27e2e3cSDerek J. Clark 			claw_frame_fill_solid(&drvdata->rgb_frames[f], colors[f]);
1378f27e2e3cSDerek J. Clark 	}
1379f27e2e3cSDerek J. Clark 
1380f27e2e3cSDerek J. Clark 	return claw_write_rgb_state(drvdata);
1381f27e2e3cSDerek J. Clark }
1382f27e2e3cSDerek J. Clark 
1383f27e2e3cSDerek J. Clark /* Apply rainbow effect (4 frames: rotating colors around joysticks) */
1384f27e2e3cSDerek J. Clark static int claw_apply_rainbow(struct claw_drvdata *drvdata)
1385f27e2e3cSDerek J. Clark {
1386f27e2e3cSDerek J. Clark 	static const struct rgb_zone colors[] = {
1387f27e2e3cSDerek J. Clark 		{255,   0,   0},  /* red   */
1388f27e2e3cSDerek J. Clark 		{  0, 255,   0},  /* green */
1389f27e2e3cSDerek J. Clark 		{  0, 255, 255},  /* cyan  */
1390f27e2e3cSDerek J. Clark 		{  0,   0, 255},  /* blue  */
1391f27e2e3cSDerek J. Clark 	};
1392f27e2e3cSDerek J. Clark 	u8 frame_count = ARRAY_SIZE(colors);
1393f27e2e3cSDerek J. Clark 	int f, z;
1394f27e2e3cSDerek J. Clark 
1395f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->frame_lock) {
1396f27e2e3cSDerek J. Clark 		drvdata->rgb_frame_count = frame_count;
1397f27e2e3cSDerek J. Clark 
1398f27e2e3cSDerek J. Clark 		for (f = 0; f < frame_count; f++) {
1399f27e2e3cSDerek J. Clark 			for (z = 0; z < 4; z++) {
1400f27e2e3cSDerek J. Clark 				drvdata->rgb_frames[f].zone[z]     = colors[(z + f) % 4];
1401f27e2e3cSDerek J. Clark 				drvdata->rgb_frames[f].zone[z + 4] = colors[(z + f) % 4];
1402f27e2e3cSDerek J. Clark 			}
1403f27e2e3cSDerek J. Clark 			drvdata->rgb_frames[f].zone[8] = colors[f];
1404f27e2e3cSDerek J. Clark 		}
1405f27e2e3cSDerek J. Clark 	}
1406f27e2e3cSDerek J. Clark 
1407f27e2e3cSDerek J. Clark 	return claw_write_rgb_state(drvdata);
1408f27e2e3cSDerek J. Clark }
1409f27e2e3cSDerek J. Clark 
1410f27e2e3cSDerek J. Clark /*
1411f27e2e3cSDerek J. Clark  * Apply frostfire effect (4 frames: fire vs ice rotating)
1412f27e2e3cSDerek J. Clark  * Right joystick: fire red -> dark -> ice blue -> dark (clockwise)
1413f27e2e3cSDerek J. Clark  * Left joystick: ice blue -> dark -> fire red -> dark (counter-clockwise)
1414f27e2e3cSDerek J. Clark  * ABXY: fire red -> dark -> ice blue -> dark
1415f27e2e3cSDerek J. Clark  */
1416f27e2e3cSDerek J. Clark static int claw_apply_frostfire(struct claw_drvdata *drvdata)
1417f27e2e3cSDerek J. Clark {
1418f27e2e3cSDerek J. Clark 	static const struct rgb_zone colors[] = {
1419f27e2e3cSDerek J. Clark 		{255,   0,   0},  /* fire red */
1420f27e2e3cSDerek J. Clark 		{  0,   0,   0},  /* dark     */
1421f27e2e3cSDerek J. Clark 		{  0,   0, 255},  /* ice blue */
1422f27e2e3cSDerek J. Clark 		{  0,   0,   0},  /* dark     */
1423f27e2e3cSDerek J. Clark 	};
1424f27e2e3cSDerek J. Clark 	u8 frame_count = ARRAY_SIZE(colors);
1425f27e2e3cSDerek J. Clark 	int f, z;
1426f27e2e3cSDerek J. Clark 
1427f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->frame_lock) {
1428f27e2e3cSDerek J. Clark 		drvdata->rgb_frame_count = frame_count;
1429f27e2e3cSDerek J. Clark 
1430f27e2e3cSDerek J. Clark 		for (f = 0; f < frame_count; f++) {
1431f27e2e3cSDerek J. Clark 			for (z = 0; z < 4; z++) {
1432f27e2e3cSDerek J. Clark 				drvdata->rgb_frames[f].zone[z]     = colors[(z + f) % 4];
1433f27e2e3cSDerek J. Clark 				drvdata->rgb_frames[f].zone[z + 4] = colors[(z - f + 6) % 4];
1434f27e2e3cSDerek J. Clark 			}
1435f27e2e3cSDerek J. Clark 			drvdata->rgb_frames[f].zone[8] = colors[f];
1436f27e2e3cSDerek J. Clark 		}
1437f27e2e3cSDerek J. Clark 	}
1438f27e2e3cSDerek J. Clark 
1439f27e2e3cSDerek J. Clark 	return claw_write_rgb_state(drvdata);
1440f27e2e3cSDerek J. Clark }
1441f27e2e3cSDerek J. Clark 
1442f27e2e3cSDerek J. Clark /* Apply current state to device */
1443f27e2e3cSDerek J. Clark static int claw_apply_rgb_state(struct claw_drvdata *drvdata)
1444f27e2e3cSDerek J. Clark {
1445f27e2e3cSDerek J. Clark 	if (!drvdata->rgb_enabled)
1446f27e2e3cSDerek J. Clark 		return claw_apply_disabled(drvdata);
1447f27e2e3cSDerek J. Clark 
1448f27e2e3cSDerek J. Clark 	switch (drvdata->rgb_effect) {
1449f27e2e3cSDerek J. Clark 	case CLAW_RGB_EFFECT_MONOCOLOR:
1450f27e2e3cSDerek J. Clark 		return claw_apply_monocolor(drvdata);
1451f27e2e3cSDerek J. Clark 	case CLAW_RGB_EFFECT_BREATHE:
1452f27e2e3cSDerek J. Clark 		return claw_apply_breathe(drvdata);
1453f27e2e3cSDerek J. Clark 	case CLAW_RGB_EFFECT_CHROMA:
1454f27e2e3cSDerek J. Clark 		return claw_apply_chroma(drvdata);
1455f27e2e3cSDerek J. Clark 	case CLAW_RGB_EFFECT_RAINBOW:
1456f27e2e3cSDerek J. Clark 		return claw_apply_rainbow(drvdata);
1457f27e2e3cSDerek J. Clark 	case CLAW_RGB_EFFECT_FROSTFIRE:
1458f27e2e3cSDerek J. Clark 		return claw_apply_frostfire(drvdata);
1459f27e2e3cSDerek J. Clark 	default:
1460f27e2e3cSDerek J. Clark 		dev_err(&drvdata->hdev->dev, "No supported rgb_effect selected\n");
1461f27e2e3cSDerek J. Clark 		return -EINVAL;
1462f27e2e3cSDerek J. Clark 	}
1463f27e2e3cSDerek J. Clark }
1464f27e2e3cSDerek J. Clark 
1465f27e2e3cSDerek J. Clark static void claw_rgb_queue_fn(struct work_struct *work)
1466f27e2e3cSDerek J. Clark {
1467f27e2e3cSDerek J. Clark 	struct delayed_work *dwork = container_of(work, struct delayed_work, work);
1468f27e2e3cSDerek J. Clark 	struct claw_drvdata *drvdata = container_of(dwork, struct claw_drvdata, rgb_queue);
1469f27e2e3cSDerek J. Clark 	int ret;
1470f27e2e3cSDerek J. Clark 
1471f27e2e3cSDerek J. Clark 	if (!drvdata)
1472f27e2e3cSDerek J. Clark 		return;
1473f27e2e3cSDerek J. Clark 
1474f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1475f27e2e3cSDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1476f27e2e3cSDerek J. Clark 		if (!smp_load_acquire(&drvdata->rgb_registered))
1477f27e2e3cSDerek J. Clark 			return;
1478f27e2e3cSDerek J. Clark 	}
1479f27e2e3cSDerek J. Clark 
1480f27e2e3cSDerek J. Clark 	ret = claw_apply_rgb_state(drvdata);
1481f27e2e3cSDerek J. Clark 	if (ret)
1482f27e2e3cSDerek J. Clark 		dev_err(&drvdata->hdev->dev, "Failed to apply RGB state: %d\n", ret);
1483f27e2e3cSDerek J. Clark }
1484f27e2e3cSDerek J. Clark 
1485f27e2e3cSDerek J. Clark static ssize_t effect_store(struct device *dev,
1486f27e2e3cSDerek J. Clark 			    struct device_attribute *attr,
1487f27e2e3cSDerek J. Clark 			    const char *buf, size_t count)
1488f27e2e3cSDerek J. Clark {
1489f27e2e3cSDerek J. Clark 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
1490f27e2e3cSDerek J. Clark 	struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev);
1491f27e2e3cSDerek J. Clark 	struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc);
1492f27e2e3cSDerek J. Clark 	int ret;
1493f27e2e3cSDerek J. Clark 
1494f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1495f27e2e3cSDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1496f27e2e3cSDerek J. Clark 		if (!smp_load_acquire(&drvdata->rgb_registered))
1497f27e2e3cSDerek J. Clark 			return -ENODEV;
1498f27e2e3cSDerek J. Clark 	}
1499f27e2e3cSDerek J. Clark 
1500f27e2e3cSDerek J. Clark 	ret = sysfs_match_string(claw_rgb_effect_text, buf);
1501f27e2e3cSDerek J. Clark 	if (ret < 0)
1502f27e2e3cSDerek J. Clark 		return ret;
1503f27e2e3cSDerek J. Clark 
1504f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->profile_lock)
1505f27e2e3cSDerek J. Clark 		drvdata->rgb_effect = ret;
1506f27e2e3cSDerek J. Clark 
1507f27e2e3cSDerek J. Clark 	mod_delayed_work(system_wq, &drvdata->rgb_queue, msecs_to_jiffies(50));
1508f27e2e3cSDerek J. Clark 
1509f27e2e3cSDerek J. Clark 	return count;
1510f27e2e3cSDerek J. Clark }
1511f27e2e3cSDerek J. Clark 
1512f27e2e3cSDerek J. Clark static ssize_t effect_show(struct device *dev,
1513f27e2e3cSDerek J. Clark 			   struct device_attribute *attr, char *buf)
1514f27e2e3cSDerek J. Clark {
1515f27e2e3cSDerek J. Clark 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
1516f27e2e3cSDerek J. Clark 	struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev);
1517f27e2e3cSDerek J. Clark 	struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc);
1518f27e2e3cSDerek J. Clark 
1519f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1520f27e2e3cSDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1521f27e2e3cSDerek J. Clark 		if (!smp_load_acquire(&drvdata->rgb_registered))
1522f27e2e3cSDerek J. Clark 			return -ENODEV;
1523f27e2e3cSDerek J. Clark 	}
1524f27e2e3cSDerek J. Clark 
1525f27e2e3cSDerek J. Clark 	if (drvdata->rgb_effect >= ARRAY_SIZE(claw_rgb_effect_text))
1526f27e2e3cSDerek J. Clark 		return -EINVAL;
1527f27e2e3cSDerek J. Clark 
1528f27e2e3cSDerek J. Clark 	return sysfs_emit(buf, "%s\n", claw_rgb_effect_text[drvdata->rgb_effect]);
1529f27e2e3cSDerek J. Clark }
1530f27e2e3cSDerek J. Clark 
1531f27e2e3cSDerek J. Clark static DEVICE_ATTR_RW(effect);
1532f27e2e3cSDerek J. Clark 
1533f27e2e3cSDerek J. Clark static ssize_t effect_index_show(struct device *dev,
1534f27e2e3cSDerek J. Clark 				 struct device_attribute *attr, char *buf)
1535f27e2e3cSDerek J. Clark {
1536f27e2e3cSDerek J. Clark 	int i, count = 0;
1537f27e2e3cSDerek J. Clark 
1538f27e2e3cSDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(claw_rgb_effect_text); i++)
1539f27e2e3cSDerek J. Clark 		count += sysfs_emit_at(buf, count, "%s ", claw_rgb_effect_text[i]);
1540f27e2e3cSDerek J. Clark 
1541f27e2e3cSDerek J. Clark 	if (count)
1542f27e2e3cSDerek J. Clark 		buf[count - 1] = '\n';
1543f27e2e3cSDerek J. Clark 
1544f27e2e3cSDerek J. Clark 	return count;
1545f27e2e3cSDerek J. Clark }
1546f27e2e3cSDerek J. Clark static DEVICE_ATTR_RO(effect_index);
1547f27e2e3cSDerek J. Clark 
1548f27e2e3cSDerek J. Clark static ssize_t enabled_store(struct device *dev,
1549f27e2e3cSDerek J. Clark 			     struct device_attribute *attr,
1550f27e2e3cSDerek J. Clark 			     const char *buf, size_t count)
1551f27e2e3cSDerek J. Clark {
1552f27e2e3cSDerek J. Clark 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
1553f27e2e3cSDerek J. Clark 	struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev);
1554f27e2e3cSDerek J. Clark 	struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc);
1555f27e2e3cSDerek J. Clark 	bool val;
1556f27e2e3cSDerek J. Clark 	int ret;
1557f27e2e3cSDerek J. Clark 
1558f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1559f27e2e3cSDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1560f27e2e3cSDerek J. Clark 		if (!smp_load_acquire(&drvdata->rgb_registered))
1561f27e2e3cSDerek J. Clark 			return -ENODEV;
1562f27e2e3cSDerek J. Clark 	}
1563f27e2e3cSDerek J. Clark 
1564f27e2e3cSDerek J. Clark 	ret = kstrtobool(buf, &val);
1565f27e2e3cSDerek J. Clark 	if (ret)
1566f27e2e3cSDerek J. Clark 		return ret;
1567f27e2e3cSDerek J. Clark 
1568f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->profile_lock)
1569f27e2e3cSDerek J. Clark 		drvdata->rgb_enabled = val;
1570f27e2e3cSDerek J. Clark 
1571f27e2e3cSDerek J. Clark 	mod_delayed_work(system_wq, &drvdata->rgb_queue, msecs_to_jiffies(50));
1572f27e2e3cSDerek J. Clark 
1573f27e2e3cSDerek J. Clark 	return count;
1574f27e2e3cSDerek J. Clark }
1575f27e2e3cSDerek J. Clark 
1576f27e2e3cSDerek J. Clark static ssize_t enabled_show(struct device *dev,
1577f27e2e3cSDerek J. Clark 			    struct device_attribute *attr, char *buf)
1578f27e2e3cSDerek J. Clark {
1579f27e2e3cSDerek J. Clark 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
1580f27e2e3cSDerek J. Clark 	struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev);
1581f27e2e3cSDerek J. Clark 	struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc);
1582f27e2e3cSDerek J. Clark 
1583f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1584f27e2e3cSDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1585f27e2e3cSDerek J. Clark 		if (!smp_load_acquire(&drvdata->rgb_registered))
1586f27e2e3cSDerek J. Clark 			return -ENODEV;
1587f27e2e3cSDerek J. Clark 	}
1588f27e2e3cSDerek J. Clark 
1589f27e2e3cSDerek J. Clark 	return sysfs_emit(buf, "%s\n", drvdata->rgb_enabled ? "true" : "false");
1590f27e2e3cSDerek J. Clark }
1591f27e2e3cSDerek J. Clark static DEVICE_ATTR_RW(enabled);
1592f27e2e3cSDerek J. Clark 
1593f27e2e3cSDerek J. Clark static ssize_t enabled_index_show(struct device *dev,
1594f27e2e3cSDerek J. Clark 				  struct device_attribute *attr, char *buf)
1595f27e2e3cSDerek J. Clark {
1596f27e2e3cSDerek J. Clark 	return sysfs_emit(buf, "true false\n");
1597f27e2e3cSDerek J. Clark }
1598f27e2e3cSDerek J. Clark static DEVICE_ATTR_RO(enabled_index);
1599f27e2e3cSDerek J. Clark 
1600f27e2e3cSDerek J. Clark static ssize_t speed_store(struct device *dev, struct device_attribute *attr,
1601f27e2e3cSDerek J. Clark 			   const char *buf, size_t count)
1602f27e2e3cSDerek J. Clark {
1603f27e2e3cSDerek J. Clark 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
1604f27e2e3cSDerek J. Clark 	struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev);
1605f27e2e3cSDerek J. Clark 	struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc);
1606f27e2e3cSDerek J. Clark 	unsigned int val, speed;
1607f27e2e3cSDerek J. Clark 	int ret;
1608f27e2e3cSDerek J. Clark 
1609f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1610f27e2e3cSDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1611f27e2e3cSDerek J. Clark 		if (!smp_load_acquire(&drvdata->rgb_registered))
1612f27e2e3cSDerek J. Clark 			return -ENODEV;
1613f27e2e3cSDerek J. Clark 	}
1614f27e2e3cSDerek J. Clark 
1615f27e2e3cSDerek J. Clark 	ret = kstrtouint(buf, 10, &val);
1616f27e2e3cSDerek J. Clark 	if (ret)
1617f27e2e3cSDerek J. Clark 		return ret;
1618f27e2e3cSDerek J. Clark 
1619f27e2e3cSDerek J. Clark 	if (val > 20)
1620f27e2e3cSDerek J. Clark 		return -EINVAL;
1621f27e2e3cSDerek J. Clark 
1622f27e2e3cSDerek J. Clark 	/* 0 is fastest, invert value for intuitive userspace speed */
1623f27e2e3cSDerek J. Clark 	speed = 20 - val;
1624f27e2e3cSDerek J. Clark 
1625f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->profile_lock)
1626f27e2e3cSDerek J. Clark 		drvdata->rgb_speed = speed;
1627f27e2e3cSDerek J. Clark 
1628f27e2e3cSDerek J. Clark 	mod_delayed_work(system_wq, &drvdata->rgb_queue, msecs_to_jiffies(50));
1629f27e2e3cSDerek J. Clark 
1630f27e2e3cSDerek J. Clark 	return count;
1631f27e2e3cSDerek J. Clark }
1632f27e2e3cSDerek J. Clark 
1633f27e2e3cSDerek J. Clark static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
1634f27e2e3cSDerek J. Clark 			  char *buf)
1635f27e2e3cSDerek J. Clark {
1636f27e2e3cSDerek J. Clark 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
1637f27e2e3cSDerek J. Clark 	struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev);
1638f27e2e3cSDerek J. Clark 	struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc);
1639f27e2e3cSDerek J. Clark 	u8 speed = 20 - drvdata->rgb_speed;
1640f27e2e3cSDerek J. Clark 
1641f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1642f27e2e3cSDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1643f27e2e3cSDerek J. Clark 		if (!smp_load_acquire(&drvdata->rgb_registered))
1644f27e2e3cSDerek J. Clark 			return -ENODEV;
1645f27e2e3cSDerek J. Clark 	}
1646f27e2e3cSDerek J. Clark 
1647f27e2e3cSDerek J. Clark 	return sysfs_emit(buf, "%u\n", speed);
1648f27e2e3cSDerek J. Clark }
1649f27e2e3cSDerek J. Clark static DEVICE_ATTR_RW(speed);
1650f27e2e3cSDerek J. Clark 
1651f27e2e3cSDerek J. Clark static ssize_t speed_range_show(struct device *dev,
1652f27e2e3cSDerek J. Clark 				struct device_attribute *attr, char *buf)
1653f27e2e3cSDerek J. Clark {
1654f27e2e3cSDerek J. Clark 	return sysfs_emit(buf, "0-20\n");
1655f27e2e3cSDerek J. Clark }
1656f27e2e3cSDerek J. Clark static DEVICE_ATTR_RO(speed_range);
1657f27e2e3cSDerek J. Clark 
1658f27e2e3cSDerek J. Clark static void claw_led_brightness_set(struct led_classdev *led_cdev,
1659f27e2e3cSDerek J. Clark 				    enum led_brightness _brightness)
1660f27e2e3cSDerek J. Clark {
1661f27e2e3cSDerek J. Clark 	struct led_classdev_mc *led_mc = container_of(led_cdev, struct led_classdev_mc, led_cdev);
1662f27e2e3cSDerek J. Clark 	struct claw_drvdata *drvdata = container_of(led_mc, struct claw_drvdata, led_mc);
1663f27e2e3cSDerek J. Clark 
1664f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1665f27e2e3cSDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1666f27e2e3cSDerek J. Clark 		if (!smp_load_acquire(&drvdata->rgb_registered))
1667f27e2e3cSDerek J. Clark 			return;
1668f27e2e3cSDerek J. Clark 	}
1669f27e2e3cSDerek J. Clark 
1670f27e2e3cSDerek J. Clark 	mod_delayed_work(system_wq, &drvdata->rgb_queue, msecs_to_jiffies(50));
1671f27e2e3cSDerek J. Clark }
1672f27e2e3cSDerek J. Clark 
1673f27e2e3cSDerek J. Clark static struct attribute *claw_rgb_attrs[] = {
1674f27e2e3cSDerek J. Clark 	&dev_attr_effect.attr,
1675f27e2e3cSDerek J. Clark 	&dev_attr_effect_index.attr,
1676f27e2e3cSDerek J. Clark 	&dev_attr_enabled.attr,
1677f27e2e3cSDerek J. Clark 	&dev_attr_enabled_index.attr,
1678f27e2e3cSDerek J. Clark 	&dev_attr_speed.attr,
1679f27e2e3cSDerek J. Clark 	&dev_attr_speed_range.attr,
1680f27e2e3cSDerek J. Clark 	NULL,
1681f27e2e3cSDerek J. Clark };
1682f27e2e3cSDerek J. Clark 
1683f27e2e3cSDerek J. Clark static const struct attribute_group claw_rgb_attr_group = {
1684f27e2e3cSDerek J. Clark 	.attrs = claw_rgb_attrs,
1685f27e2e3cSDerek J. Clark };
1686f27e2e3cSDerek J. Clark 
1687f27e2e3cSDerek J. Clark static struct mc_subled claw_rgb_subled_info[] = {
1688f27e2e3cSDerek J. Clark 	{
1689f27e2e3cSDerek J. Clark 		.color_index = LED_COLOR_ID_RED,
1690f27e2e3cSDerek J. Clark 		.channel = 0x1,
1691f27e2e3cSDerek J. Clark 	},
1692f27e2e3cSDerek J. Clark 	{
1693f27e2e3cSDerek J. Clark 		.color_index = LED_COLOR_ID_GREEN,
1694f27e2e3cSDerek J. Clark 		.channel = 0x2,
1695f27e2e3cSDerek J. Clark 	},
1696f27e2e3cSDerek J. Clark 	{
1697f27e2e3cSDerek J. Clark 		.color_index = LED_COLOR_ID_BLUE,
1698f27e2e3cSDerek J. Clark 		.channel = 0x3,
1699f27e2e3cSDerek J. Clark 	},
1700f27e2e3cSDerek J. Clark };
1701f27e2e3cSDerek J. Clark 
1702a9517958SDerek J. Clark static void cfg_setup_fn(struct work_struct *work)
1703a9517958SDerek J. Clark {
1704a9517958SDerek J. Clark 	struct delayed_work *dwork = container_of(work, struct delayed_work, work);
1705a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = container_of(dwork, struct claw_drvdata, cfg_setup);
1706f27e2e3cSDerek J. Clark 	bool gamepad_ready = false, rgb_ready = false, gp_registered, rgb_registered;
1707a9517958SDerek J. Clark 	int ret;
1708a9517958SDerek J. Clark 
1709a9517958SDerek J. Clark 	ret = claw_hw_output_report(drvdata->hdev, CLAW_COMMAND_TYPE_READ_GAMEPAD_MODE,
1710a9517958SDerek J. Clark 				    NULL, 0, 25);
1711a9517958SDerek J. Clark 	if (ret) {
1712a9517958SDerek J. Clark 		dev_err(&drvdata->hdev->dev,
1713f27e2e3cSDerek J. Clark 			"Failed to read gamepad mode: %d\n", ret);
1714f27e2e3cSDerek J. Clark 		goto prep_rgb;
1715a9517958SDerek J. Clark 	}
1716f27e2e3cSDerek J. Clark 	gamepad_ready = true;
1717f27e2e3cSDerek J. Clark 
1718f27e2e3cSDerek J. Clark prep_rgb:
1719f27e2e3cSDerek J. Clark 	ret = claw_read_rgb_config(drvdata->hdev);
1720f27e2e3cSDerek J. Clark 	if (ret) {
1721f27e2e3cSDerek J. Clark 		dev_err(&drvdata->hdev->dev,
1722f27e2e3cSDerek J. Clark 			"Failed to read RGB config: %d\n", ret);
1723f27e2e3cSDerek J. Clark 		goto try_gamepad;
1724f27e2e3cSDerek J. Clark 	}
1725f27e2e3cSDerek J. Clark 	rgb_ready = true;
1726a9517958SDerek J. Clark 
1727a9517958SDerek J. Clark 	/* Add sysfs attributes after we get the device state */
1728f27e2e3cSDerek J. Clark try_gamepad:
1729f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock)
1730f27e2e3cSDerek J. Clark 		/* Pairs with smp_store_release from below */
1731f27e2e3cSDerek J. Clark 		gp_registered = smp_load_acquire(&drvdata->gp_registered);
1732f27e2e3cSDerek J. Clark 
1733f27e2e3cSDerek J. Clark 	if (!gp_registered && gamepad_ready) {
1734a9517958SDerek J. Clark 		ret = device_add_group(&drvdata->hdev->dev, &claw_gamepad_attr_group);
1735a9517958SDerek J. Clark 		if (ret) {
1736a9517958SDerek J. Clark 			dev_err(&drvdata->hdev->dev,
1737f27e2e3cSDerek J. Clark 				"Failed to create gamepad attrs: %d\n", ret);
1738f27e2e3cSDerek J. Clark 			goto try_rgb;
1739a9517958SDerek J. Clark 		}
1740f27e2e3cSDerek J. Clark 
1741f27e2e3cSDerek J. Clark 		scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1742a9517958SDerek J. Clark 			/* Pairs with smp_load_acquire in attribute show/store functions */
1743a9517958SDerek J. Clark 			smp_store_release(&drvdata->gp_registered, true);
1744f27e2e3cSDerek J. Clark 			gp_registered = true;
1745f27e2e3cSDerek J. Clark 		}
1746f27e2e3cSDerek J. Clark 	}
1747a9517958SDerek J. Clark 
1748f27e2e3cSDerek J. Clark try_rgb:
1749f27e2e3cSDerek J. Clark 	/* Add and enable RGB interface once we have the device state */
1750f27e2e3cSDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock)
1751f27e2e3cSDerek J. Clark 		/* Pairs with smp_store_release from below */
1752f27e2e3cSDerek J. Clark 		rgb_registered = smp_load_acquire(&drvdata->rgb_registered);
1753f27e2e3cSDerek J. Clark 
1754f27e2e3cSDerek J. Clark 	if (!rgb_registered && rgb_ready) {
1755f27e2e3cSDerek J. Clark 		ret = led_classdev_multicolor_register(&drvdata->hdev->dev,
1756f27e2e3cSDerek J. Clark 						       &drvdata->led_mc);
1757f27e2e3cSDerek J. Clark 		if (ret) {
1758f27e2e3cSDerek J. Clark 			dev_err(&drvdata->hdev->dev, "Failed to create led device: %d\n", ret);
1759f27e2e3cSDerek J. Clark 			goto update_kobjects;
1760f27e2e3cSDerek J. Clark 		}
1761f27e2e3cSDerek J. Clark 
1762f27e2e3cSDerek J. Clark 		ret = device_add_group(drvdata->led_mc.led_cdev.dev, &claw_rgb_attr_group);
1763f27e2e3cSDerek J. Clark 		if (ret) {
1764f27e2e3cSDerek J. Clark 			dev_err(&drvdata->hdev->dev, "Failed to create RGB attrs: %d\n", ret);
1765f27e2e3cSDerek J. Clark 			led_classdev_multicolor_unregister(&drvdata->led_mc);
1766f27e2e3cSDerek J. Clark 			goto update_kobjects;
1767f27e2e3cSDerek J. Clark 		}
1768f27e2e3cSDerek J. Clark 
1769f27e2e3cSDerek J. Clark 		scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1770f27e2e3cSDerek J. Clark 			/* Pairs with smp_load_acquire in attribute show/store functions */
1771f27e2e3cSDerek J. Clark 			smp_store_release(&drvdata->rgb_registered, true);
1772f27e2e3cSDerek J. Clark 			rgb_registered = true;
1773f27e2e3cSDerek J. Clark 		}
1774f27e2e3cSDerek J. Clark 	}
1775f27e2e3cSDerek J. Clark 
1776f27e2e3cSDerek J. Clark update_kobjects:
1777f27e2e3cSDerek J. Clark 	if (gp_registered)
1778a9517958SDerek J. Clark 		kobject_uevent(&drvdata->hdev->dev.kobj, KOBJ_CHANGE);
1779f27e2e3cSDerek J. Clark 	if (rgb_registered)
1780f27e2e3cSDerek J. Clark 		kobject_uevent(&drvdata->led_mc.led_cdev.dev->kobj, KOBJ_CHANGE);
1781a9517958SDerek J. Clark }
1782a9517958SDerek J. Clark 
1783a9517958SDerek J. Clark static void cfg_resume_fn(struct work_struct *work)
1784a9517958SDerek J. Clark {
1785a9517958SDerek J. Clark 	struct delayed_work *dwork = container_of(work, struct delayed_work, work);
1786a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = container_of(dwork, struct claw_drvdata, cfg_resume);
1787a9517958SDerek J. Clark 
1788a9517958SDerek J. Clark 	guard(spinlock_irqsave)(&drvdata->registration_lock);
1789a9517958SDerek J. Clark 	    /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1790f27e2e3cSDerek J. Clark 	if (!smp_load_acquire(&drvdata->gp_registered) ||
1791f27e2e3cSDerek J. Clark 	    /* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1792f27e2e3cSDerek J. Clark 	    !smp_load_acquire(&drvdata->rgb_registered))
1793a9517958SDerek J. Clark 		schedule_delayed_work(&drvdata->cfg_setup, msecs_to_jiffies(500));
1794a9517958SDerek J. Clark }
1795a9517958SDerek J. Clark 
179693ff13edSDerek J. Clark static void claw_features_supported(struct claw_drvdata *drvdata)
179793ff13edSDerek J. Clark {
179893ff13edSDerek J. Clark 	u8 major = (drvdata->bcd_device >> 8) & 0xff;
179993ff13edSDerek J. Clark 	u8 minor = drvdata->bcd_device & 0xff;
180093ff13edSDerek J. Clark 
180193ff13edSDerek J. Clark 	if (major == 0x01) {
180293ff13edSDerek J. Clark 		drvdata->bmap_support = true;
1803f27e2e3cSDerek J. Clark 		if (minor >= 0x66) {
180493ff13edSDerek J. Clark 			drvdata->bmap_addr = button_mapping_addr_new;
1805*95547c53SDerek J. Clark 			drvdata->rumble_support = true;
1806f27e2e3cSDerek J. Clark 			drvdata->rgb_addr = rgb_addr_new;
1807f27e2e3cSDerek J. Clark 		} else {
180893ff13edSDerek J. Clark 			drvdata->bmap_addr = button_mapping_addr_old;
1809f27e2e3cSDerek J. Clark 			drvdata->rgb_addr = rgb_addr_old;
1810f27e2e3cSDerek J. Clark 		}
181193ff13edSDerek J. Clark 		return;
181293ff13edSDerek J. Clark 	}
181393ff13edSDerek J. Clark 
181493ff13edSDerek J. Clark 	if ((major == 0x02 && minor >= 0x17) || major >= 0x03) {
181593ff13edSDerek J. Clark 		drvdata->bmap_support = true;
181693ff13edSDerek J. Clark 		drvdata->bmap_addr = button_mapping_addr_new;
1817*95547c53SDerek J. Clark 		drvdata->rumble_support = true;
1818f27e2e3cSDerek J. Clark 		drvdata->rgb_addr = rgb_addr_new;
181993ff13edSDerek J. Clark 		return;
182093ff13edSDerek J. Clark 	}
1821f27e2e3cSDerek J. Clark 
1822f27e2e3cSDerek J. Clark 	drvdata->rgb_addr = rgb_addr_old;
182393ff13edSDerek J. Clark }
182493ff13edSDerek J. Clark 
1825a9517958SDerek J. Clark static int claw_probe(struct hid_device *hdev, u8 ep)
1826a9517958SDerek J. Clark {
182793ff13edSDerek J. Clark 	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
182893ff13edSDerek J. Clark 	struct usb_device *udev = interface_to_usbdev(intf);
1829a9517958SDerek J. Clark 	struct claw_drvdata *drvdata;
1830a9517958SDerek J. Clark 	int ret;
1831a9517958SDerek J. Clark 
1832a9517958SDerek J. Clark 	drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
1833a9517958SDerek J. Clark 	if (!drvdata)
1834a9517958SDerek J. Clark 		return -ENOMEM;
1835a9517958SDerek J. Clark 
1836a9517958SDerek J. Clark 	drvdata->gamepad_mode = CLAW_GAMEPAD_MODE_XINPUT;
1837f27e2e3cSDerek J. Clark 	drvdata->rgb_enabled = true;
1838a9517958SDerek J. Clark 	drvdata->hdev = hdev;
1839a9517958SDerek J. Clark 	drvdata->ep = ep;
1840a9517958SDerek J. Clark 
184193ff13edSDerek J. Clark 	/* Determine feature level from firmware version */
184293ff13edSDerek J. Clark 	drvdata->bcd_device = le16_to_cpu(udev->descriptor.bcdDevice);
184393ff13edSDerek J. Clark 	claw_features_supported(drvdata);
184493ff13edSDerek J. Clark 
184593ff13edSDerek J. Clark 	if (!drvdata->bmap_support)
184693ff13edSDerek J. Clark 		dev_dbg(&hdev->dev, "M-Key mapping is not supported. Update firmware to enable.\n");
184793ff13edSDerek J. Clark 
1848f27e2e3cSDerek J. Clark 	/* Device is hardwired and name is guaranteed to be unique */
1849f27e2e3cSDerek J. Clark 	drvdata->led_mc.led_cdev.name = "msi_claw:rgb:joystick_rings";
1850f27e2e3cSDerek J. Clark 	drvdata->led_mc.led_cdev.brightness = 0x50;
1851f27e2e3cSDerek J. Clark 	drvdata->led_mc.led_cdev.max_brightness = 0x64;
1852f27e2e3cSDerek J. Clark 	drvdata->led_mc.led_cdev.color = LED_COLOR_ID_RGB;
1853f27e2e3cSDerek J. Clark 	drvdata->led_mc.led_cdev.brightness_set = claw_led_brightness_set;
1854f27e2e3cSDerek J. Clark 	drvdata->led_mc.num_colors = 3;
1855f27e2e3cSDerek J. Clark 	drvdata->led_mc.subled_info = devm_kmemdup(&hdev->dev, claw_rgb_subled_info,
1856f27e2e3cSDerek J. Clark 						   sizeof(claw_rgb_subled_info), GFP_KERNEL);
1857f27e2e3cSDerek J. Clark 	if (!drvdata->led_mc.subled_info)
1858f27e2e3cSDerek J. Clark 		return -ENOMEM;
1859f27e2e3cSDerek J. Clark 
1860a9517958SDerek J. Clark 	mutex_init(&drvdata->cfg_mutex);
186193ff13edSDerek J. Clark 	mutex_init(&drvdata->profile_mutex);
186293ff13edSDerek J. Clark 	mutex_init(&drvdata->rom_mutex);
1863a9517958SDerek J. Clark 	spin_lock_init(&drvdata->registration_lock);
1864a9517958SDerek J. Clark 	spin_lock_init(&drvdata->cmd_lock);
1865a9517958SDerek J. Clark 	spin_lock_init(&drvdata->mode_lock);
186693ff13edSDerek J. Clark 	spin_lock_init(&drvdata->profile_lock);
1867f27e2e3cSDerek J. Clark 	spin_lock_init(&drvdata->frame_lock);
1868*95547c53SDerek J. Clark 	spin_lock_init(&drvdata->rumble_lock);
1869a9517958SDerek J. Clark 	init_completion(&drvdata->orphan_ack_complete);
1870a9517958SDerek J. Clark 	init_completion(&drvdata->send_cmd_complete);
1871a9517958SDerek J. Clark 	INIT_DELAYED_WORK(&drvdata->cfg_resume, &cfg_resume_fn);
1872a9517958SDerek J. Clark 	INIT_DELAYED_WORK(&drvdata->cfg_setup, &cfg_setup_fn);
1873f27e2e3cSDerek J. Clark 	INIT_DELAYED_WORK(&drvdata->rgb_queue, &claw_rgb_queue_fn);
1874a9517958SDerek J. Clark 
1875a9517958SDerek J. Clark 	/* For control interface: open the HID transport for sending commands. */
1876a9517958SDerek J. Clark 	ret = hid_hw_open(hdev);
1877a9517958SDerek J. Clark 	if (ret)
1878a9517958SDerek J. Clark 		return ret;
1879a9517958SDerek J. Clark 
1880a9517958SDerek J. Clark 	hid_set_drvdata(hdev, drvdata);
1881a9517958SDerek J. Clark 	schedule_delayed_work(&drvdata->cfg_setup, msecs_to_jiffies(500));
1882a9517958SDerek J. Clark 
1883a9517958SDerek J. Clark 	return 0;
1884a9517958SDerek J. Clark }
1885a9517958SDerek J. Clark 
1886a9517958SDerek J. Clark static int msi_probe(struct hid_device *hdev, const struct hid_device_id *id)
1887a9517958SDerek J. Clark {
1888a9517958SDerek J. Clark 	int ret;
1889a9517958SDerek J. Clark 	u8 ep;
1890a9517958SDerek J. Clark 
1891a9517958SDerek J. Clark 	if (!hid_is_usb(hdev)) {
1892a9517958SDerek J. Clark 		ret = -ENODEV;
1893a9517958SDerek J. Clark 		goto err_probe;
1894a9517958SDerek J. Clark 	}
1895a9517958SDerek J. Clark 
1896a9517958SDerek J. Clark 	ret = hid_parse(hdev);
1897a9517958SDerek J. Clark 	if (ret)
1898a9517958SDerek J. Clark 		goto err_probe;
1899a9517958SDerek J. Clark 
1900a9517958SDerek J. Clark 	/* Set quirk to create separate input devices per HID application */
1901a9517958SDerek J. Clark 	hdev->quirks |= HID_QUIRK_INPUT_PER_APP | HID_QUIRK_MULTI_INPUT;
1902a9517958SDerek J. Clark 	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
1903a9517958SDerek J. Clark 	if (ret)
1904a9517958SDerek J. Clark 		goto err_probe;
1905a9517958SDerek J. Clark 
1906a9517958SDerek J. Clark 	/* For non-control interfaces (keyboard/mouse), allow userspace to grab the devices. */
1907a9517958SDerek J. Clark 	ret = get_endpoint_address(hdev);
1908a9517958SDerek J. Clark 	if (ret < 0)
1909a9517958SDerek J. Clark 		goto err_stop_hw;
1910a9517958SDerek J. Clark 
1911a9517958SDerek J. Clark 	ep = ret;
1912a9517958SDerek J. Clark 	if (ep == CLAW_XINPUT_CFG_INTF_IN || ep == CLAW_DINPUT_CFG_INTF_IN) {
1913a9517958SDerek J. Clark 		ret = claw_probe(hdev, ep);
1914a9517958SDerek J. Clark 		if (ret)
1915a9517958SDerek J. Clark 			goto err_stop_hw;
1916a9517958SDerek J. Clark 	}
1917a9517958SDerek J. Clark 
1918a9517958SDerek J. Clark 	return 0;
1919a9517958SDerek J. Clark 
1920a9517958SDerek J. Clark err_stop_hw:
1921a9517958SDerek J. Clark 	hid_hw_stop(hdev);
1922a9517958SDerek J. Clark err_probe:
1923a9517958SDerek J. Clark 	return dev_err_probe(&hdev->dev, ret, "Failed to init device\n");
1924a9517958SDerek J. Clark }
1925a9517958SDerek J. Clark 
1926a9517958SDerek J. Clark static void claw_remove(struct hid_device *hdev)
1927a9517958SDerek J. Clark {
1928a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
1929a9517958SDerek J. Clark 	bool gp_registered;
1930f27e2e3cSDerek J. Clark 	bool rgb_registered;
1931a9517958SDerek J. Clark 
1932a9517958SDerek J. Clark 	if (!drvdata)
1933a9517958SDerek J. Clark 		return;
1934a9517958SDerek J. Clark 
1935a9517958SDerek J. Clark 	cancel_delayed_work_sync(&drvdata->cfg_resume);
1936a9517958SDerek J. Clark 	cancel_delayed_work_sync(&drvdata->cfg_setup);
1937a9517958SDerek J. Clark 
1938a9517958SDerek J. Clark 	scoped_guard(spinlock_irqsave, &drvdata->registration_lock) {
1939a9517958SDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1940a9517958SDerek J. Clark 		gp_registered = smp_load_acquire(&drvdata->gp_registered);
1941a9517958SDerek J. Clark 		/* Pairs with smp_load_acquire in attribute show/store functions */
1942a9517958SDerek J. Clark 		smp_store_release(&drvdata->gp_registered, false);
1943f27e2e3cSDerek J. Clark 		/* Pairs with smp_store_release from cfg_setup_fn in system_wq context */
1944f27e2e3cSDerek J. Clark 		rgb_registered = smp_load_acquire(&drvdata->rgb_registered);
1945f27e2e3cSDerek J. Clark 		/* Pairs with smp_load_acquire in attribute show/store functions */
1946f27e2e3cSDerek J. Clark 		smp_store_release(&drvdata->rgb_registered, false);
1947a9517958SDerek J. Clark 	}
1948a9517958SDerek J. Clark 
1949a9517958SDerek J. Clark 	if (gp_registered)
1950a9517958SDerek J. Clark 		device_remove_group(&hdev->dev, &claw_gamepad_attr_group);
1951a9517958SDerek J. Clark 
1952f27e2e3cSDerek J. Clark 	if (rgb_registered) {
1953f27e2e3cSDerek J. Clark 		device_remove_group(drvdata->led_mc.led_cdev.dev, &claw_rgb_attr_group);
1954f27e2e3cSDerek J. Clark 		led_classdev_multicolor_unregister(&drvdata->led_mc);
1955f27e2e3cSDerek J. Clark 	}
1956f27e2e3cSDerek J. Clark 	cancel_delayed_work_sync(&drvdata->rgb_queue);
1957f27e2e3cSDerek J. Clark 
1958a9517958SDerek J. Clark 	hid_hw_close(hdev);
1959a9517958SDerek J. Clark }
1960a9517958SDerek J. Clark 
1961a9517958SDerek J. Clark static void msi_remove(struct hid_device *hdev)
1962a9517958SDerek J. Clark {
1963a9517958SDerek J. Clark 	int ret;
1964a9517958SDerek J. Clark 	u8 ep;
1965a9517958SDerek J. Clark 
1966a9517958SDerek J. Clark 	/* Safe assumption. SET_INTERFACE ioctl can't be used while driver is bound */
1967a9517958SDerek J. Clark 	ret = get_endpoint_address(hdev);
1968a9517958SDerek J. Clark 	if (ret <= 0)
1969a9517958SDerek J. Clark 		goto hw_stop;
1970a9517958SDerek J. Clark 
1971a9517958SDerek J. Clark 	ep = ret;
1972a9517958SDerek J. Clark 	if (ep == CLAW_XINPUT_CFG_INTF_IN || ep == CLAW_DINPUT_CFG_INTF_IN)
1973a9517958SDerek J. Clark 		claw_remove(hdev);
1974a9517958SDerek J. Clark 
1975a9517958SDerek J. Clark hw_stop:
1976a9517958SDerek J. Clark 	hid_hw_stop(hdev);
1977a9517958SDerek J. Clark }
1978a9517958SDerek J. Clark 
1979a9517958SDerek J. Clark static int claw_resume(struct hid_device *hdev)
1980a9517958SDerek J. Clark {
1981a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
1982a9517958SDerek J. Clark 
1983a9517958SDerek J. Clark 	if (!drvdata)
1984a9517958SDerek J. Clark 		return -ENODEV;
1985a9517958SDerek J. Clark 
1986a9517958SDerek J. Clark 	/* MCU can take up to 500ms to be ready after resume */
1987a9517958SDerek J. Clark 	schedule_delayed_work(&drvdata->cfg_resume, msecs_to_jiffies(500));
1988a9517958SDerek J. Clark 	return 0;
1989a9517958SDerek J. Clark }
1990a9517958SDerek J. Clark 
1991a9517958SDerek J. Clark static int msi_resume(struct hid_device *hdev)
1992a9517958SDerek J. Clark {
1993a9517958SDerek J. Clark 	int ret;
1994a9517958SDerek J. Clark 	u8 ep;
1995a9517958SDerek J. Clark 
1996a9517958SDerek J. Clark 	/* Safe assumption. SET_INTERFACE ioctl can't be used while driver is bound */
1997a9517958SDerek J. Clark 	ret = get_endpoint_address(hdev);
1998a9517958SDerek J. Clark 	if (ret <= 0)
1999a9517958SDerek J. Clark 		return 0;
2000a9517958SDerek J. Clark 
2001a9517958SDerek J. Clark 	ep = ret;
2002a9517958SDerek J. Clark 	if (ep == CLAW_XINPUT_CFG_INTF_IN || ep == CLAW_DINPUT_CFG_INTF_IN)
2003a9517958SDerek J. Clark 		return claw_resume(hdev);
2004a9517958SDerek J. Clark 
2005a9517958SDerek J. Clark 	return 0;
2006a9517958SDerek J. Clark }
2007a9517958SDerek J. Clark 
2008a9517958SDerek J. Clark static int claw_suspend(struct hid_device *hdev)
2009a9517958SDerek J. Clark {
2010a9517958SDerek J. Clark 	struct claw_drvdata *drvdata = hid_get_drvdata(hdev);
2011a9517958SDerek J. Clark 
2012a9517958SDerek J. Clark 	if (!drvdata)
2013a9517958SDerek J. Clark 		return -ENODEV;
2014a9517958SDerek J. Clark 
2015a9517958SDerek J. Clark 	cancel_delayed_work_sync(&drvdata->cfg_resume);
2016a9517958SDerek J. Clark 	cancel_delayed_work_sync(&drvdata->cfg_setup);
2017f27e2e3cSDerek J. Clark 	cancel_delayed_work_sync(&drvdata->rgb_queue);
2018a9517958SDerek J. Clark 
2019a9517958SDerek J. Clark 	return 0;
2020a9517958SDerek J. Clark }
2021a9517958SDerek J. Clark 
2022a9517958SDerek J. Clark static int msi_suspend(struct hid_device *hdev, pm_message_t msg)
2023a9517958SDerek J. Clark {
2024a9517958SDerek J. Clark 	int ret;
2025a9517958SDerek J. Clark 	u8 ep;
2026a9517958SDerek J. Clark 
2027a9517958SDerek J. Clark 	/* Safe assumption. SET_INTERFACE ioctl can't be used while driver is bound */
2028a9517958SDerek J. Clark 	ret = get_endpoint_address(hdev);
2029a9517958SDerek J. Clark 	if (ret <= 0)
2030a9517958SDerek J. Clark 		return 0;
2031a9517958SDerek J. Clark 
2032a9517958SDerek J. Clark 	ep = ret;
2033a9517958SDerek J. Clark 	if (ep == CLAW_XINPUT_CFG_INTF_IN || ep == CLAW_DINPUT_CFG_INTF_IN)
2034a9517958SDerek J. Clark 		return claw_suspend(hdev);
2035a9517958SDerek J. Clark 
2036a9517958SDerek J. Clark 	return 0;
2037a9517958SDerek J. Clark }
2038a9517958SDerek J. Clark 
2039a9517958SDerek J. Clark static const struct hid_device_id msi_devices[] = {
2040a9517958SDerek J. Clark 	{ HID_USB_DEVICE(USB_VENDOR_ID_MSI_2, USB_DEVICE_ID_MSI_CLAW_XINPUT) },
2041a9517958SDerek J. Clark 	{ HID_USB_DEVICE(USB_VENDOR_ID_MSI_2, USB_DEVICE_ID_MSI_CLAW_DINPUT) },
2042a9517958SDerek J. Clark 	{ HID_USB_DEVICE(USB_VENDOR_ID_MSI_2, USB_DEVICE_ID_MSI_CLAW_DESKTOP) },
2043a9517958SDerek J. Clark 	{ HID_USB_DEVICE(USB_VENDOR_ID_MSI_2, USB_DEVICE_ID_MSI_CLAW_BIOS) },
2044a9517958SDerek J. Clark 	{ }
2045a9517958SDerek J. Clark };
2046a9517958SDerek J. Clark MODULE_DEVICE_TABLE(hid, msi_devices);
2047a9517958SDerek J. Clark 
2048a9517958SDerek J. Clark static struct hid_driver msi_driver = {
2049a9517958SDerek J. Clark 	.name		= "hid-msi",
2050a9517958SDerek J. Clark 	.id_table	= msi_devices,
2051a9517958SDerek J. Clark 	.raw_event	= msi_raw_event,
2052a9517958SDerek J. Clark 	.probe		= msi_probe,
2053a9517958SDerek J. Clark 	.remove		= msi_remove,
2054a9517958SDerek J. Clark 	.resume		= pm_ptr(msi_resume),
2055a9517958SDerek J. Clark 	.suspend	= pm_ptr(msi_suspend),
2056a9517958SDerek J. Clark };
2057a9517958SDerek J. Clark module_hid_driver(msi_driver);
2058a9517958SDerek J. Clark 
2059a9517958SDerek J. Clark MODULE_LICENSE("GPL");
2060a9517958SDerek J. Clark MODULE_AUTHOR("Denis Benato <denis.benato@linux.dev>");
2061a9517958SDerek J. Clark MODULE_AUTHOR("Zhouwang Huang <honjow311@gmail.com>");
2062a9517958SDerek J. Clark MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
2063a9517958SDerek J. Clark MODULE_DESCRIPTION("HID driver for MSI Claw Handheld PC gamepads");
2064