xref: /linux/drivers/hid/hid-oxp.c (revision a93f3bf4e1d60777b1659b812c9e818cfc53b449)
184910c45SDerek J. Clark // SPDX-License-Identifier: GPL-2.0-or-later
284910c45SDerek J. Clark /*
384910c45SDerek J. Clark  *  HID driver for OneXPlayer gamepad configuration devices.
484910c45SDerek J. Clark  *
584910c45SDerek J. Clark  *  Copyright (c) 2026 Valve Corporation
684910c45SDerek J. Clark  */
784910c45SDerek J. Clark 
884910c45SDerek J. Clark #include <linux/array_size.h>
984910c45SDerek J. Clark #include <linux/cleanup.h>
1084910c45SDerek J. Clark #include <linux/delay.h>
1184910c45SDerek J. Clark #include <linux/dev_printk.h>
1284910c45SDerek J. Clark #include <linux/device.h>
13252c4bf1SDerek J. Clark #include <linux/dmi.h>
1484910c45SDerek J. Clark #include <linux/hid.h>
1584910c45SDerek J. Clark #include <linux/jiffies.h>
1684910c45SDerek J. Clark #include <linux/kstrtox.h>
1784910c45SDerek J. Clark #include <linux/led-class-multicolor.h>
1884910c45SDerek J. Clark #include <linux/mutex.h>
1984910c45SDerek J. Clark #include <linux/sysfs.h>
2084910c45SDerek J. Clark #include <linux/types.h>
2184910c45SDerek J. Clark #include <linux/workqueue.h>
2284910c45SDerek J. Clark 
2384910c45SDerek J. Clark #include "hid-ids.h"
2484910c45SDerek J. Clark 
2584910c45SDerek J. Clark #define OXP_PACKET_SIZE 64
2684910c45SDerek J. Clark 
2784910c45SDerek J. Clark #define GEN1_MESSAGE_ID	0xff
28252c4bf1SDerek J. Clark #define GEN2_MESSAGE_ID	0x3f
2984910c45SDerek J. Clark 
3084910c45SDerek J. Clark #define GEN1_USAGE_PAGE	0xff01
31252c4bf1SDerek J. Clark #define GEN2_USAGE_PAGE	0xff00
3284910c45SDerek J. Clark 
3384910c45SDerek J. Clark enum oxp_function_index {
3484910c45SDerek J. Clark 	OXP_FID_GEN1_RGB_SET =		0x07,
3584910c45SDerek J. Clark 	OXP_FID_GEN1_RGB_REPLY =	0x0f,
362f424f28SDerek J. Clark 	OXP_FID_GEN2_TOGGLE_MODE =	0xb2,
3799bde1dfSDerek J. Clark 	OXP_FID_GEN2_RUMBLE_SET =	0xb3,
38e4c850a6SDerek J. Clark 	OXP_FID_GEN2_KEY_STATE =	0xb4,
39252c4bf1SDerek J. Clark 	OXP_FID_GEN2_STATUS_EVENT =	0xb8,
4084910c45SDerek J. Clark };
4184910c45SDerek J. Clark 
42e4c850a6SDerek J. Clark #define OXP_MAPPING_GAMEPAD	0x01
43e4c850a6SDerek J. Clark #define OXP_MAPPING_KEYBOARD	0x02
44e4c850a6SDerek J. Clark 
45e4c850a6SDerek J. Clark struct oxp_button_data {
46e4c850a6SDerek J. Clark 	u8 mode;
47e4c850a6SDerek J. Clark 	u8 index;
48e4c850a6SDerek J. Clark 	u8 key_id;
49e4c850a6SDerek J. Clark 	u8 padding[2];
50e4c850a6SDerek J. Clark } __packed;
51e4c850a6SDerek J. Clark 
52e4c850a6SDerek J. Clark struct oxp_button_entry {
53e4c850a6SDerek J. Clark 	struct oxp_button_data data;
54e4c850a6SDerek J. Clark 	const char *name;
55e4c850a6SDerek J. Clark };
56e4c850a6SDerek J. Clark 
57e4c850a6SDerek J. Clark static const struct oxp_button_entry oxp_button_table[] = {
58e4c850a6SDerek J. Clark 	/* Gamepad Buttons */
59e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x01 }, "BTN_A" },
60e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x02 }, "BTN_B" },
61e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x03 }, "BTN_X" },
62e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x04 }, "BTN_Y" },
63e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x05 }, "BTN_LB" },
64e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x06 }, "BTN_RB" },
65e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x07 }, "BTN_LT" },
66e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x08 }, "BTN_RT" },
67e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x09 }, "BTN_START" },
68e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x0a }, "BTN_SELECT" },
69e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x0b }, "BTN_L3" },
70e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x0c }, "BTN_R3" },
71e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x0d }, "DPAD_UP" },
72e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x0e }, "DPAD_DOWN" },
73e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x0f }, "DPAD_LEFT" },
74e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x10 }, "DPAD_RIGHT" },
75e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x11 }, "JOY_L_UP" },
76e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x12 }, "JOY_L_UP_RIGHT" },
77e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x13 }, "JOY_L_RIGHT" },
78e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x14 }, "JOY_L_DOWN_RIGHT" },
79e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x15 }, "JOY_L_DOWN" },
80e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x16 }, "JOY_L_DOWN_LEFT" },
81e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x17 }, "JOY_L_LEFT" },
82e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x18 }, "JOY_L_UP_LEFT" },
83e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x19 }, "JOY_R_UP" },
84e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x1a }, "JOY_R_UP_RIGHT" },
85e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x1b }, "JOY_R_RIGHT" },
86e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x1c }, "JOY_R_DOWN_RIGHT" },
87e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x1d }, "JOY_R_DOWN" },
88e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x1e }, "JOY_R_DOWN_LEFT" },
89e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x1f }, "JOY_R_LEFT" },
90e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x20 }, "JOY_R_UP_LEFT" },
91e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_GAMEPAD, 0x22 }, "BTN_GUIDE" },
92e4c850a6SDerek J. Clark 	/* Keyboard Keys */
93e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x5a }, "KEY_F1" },
94e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x5b }, "KEY_F2" },
95e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x5c }, "KEY_F3" },
96e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x5d }, "KEY_F4" },
97e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x5e }, "KEY_F5" },
98e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x5f }, "KEY_F6" },
99e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x60 }, "KEY_F7" },
100e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x61 }, "KEY_F8" },
101e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x62 }, "KEY_F9" },
102e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x63 }, "KEY_F10" },
103e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x64 }, "KEY_F11" },
104e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x65 }, "KEY_F12" },
105e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x66 }, "KEY_F13" },
106e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x67 }, "KEY_F14" },
107e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x68 }, "KEY_F15" },
108e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x69 }, "KEY_F16" },
109e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x6a }, "KEY_F17" },
110e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x6b }, "KEY_F18" },
111e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x6c }, "KEY_F19" },
112e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x6d }, "KEY_F20" },
113e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x6e }, "KEY_F21" },
114e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x6f }, "KEY_F22" },
115e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x70 }, "KEY_F23" },
116e4c850a6SDerek J. Clark 	{ { OXP_MAPPING_KEYBOARD, 0x01, 0x71 }, "KEY_F24" },
117e4c850a6SDerek J. Clark };
118e4c850a6SDerek J. Clark 
119e4c850a6SDerek J. Clark enum oxp_joybutton_index {
120e4c850a6SDerek J. Clark 	BUTTON_A =	0x01,
121e4c850a6SDerek J. Clark 	BUTTON_B,
122e4c850a6SDerek J. Clark 	BUTTON_X,
123e4c850a6SDerek J. Clark 	BUTTON_Y,
124e4c850a6SDerek J. Clark 	BUTTON_LB,
125e4c850a6SDerek J. Clark 	BUTTON_RB,
126e4c850a6SDerek J. Clark 	BUTTON_LT,
127e4c850a6SDerek J. Clark 	BUTTON_RT,
128e4c850a6SDerek J. Clark 	BUTTON_START,
129e4c850a6SDerek J. Clark 	BUTTON_SELECT,
130e4c850a6SDerek J. Clark 	BUTTON_L3,
131e4c850a6SDerek J. Clark 	BUTTON_R3,
132e4c850a6SDerek J. Clark 	BUTTON_DUP,
133e4c850a6SDerek J. Clark 	BUTTON_DDOWN,
134e4c850a6SDerek J. Clark 	BUTTON_DLEFT,
135e4c850a6SDerek J. Clark 	BUTTON_DRIGHT,
136e4c850a6SDerek J. Clark 	BUTTON_M1 =	0x22,
137e4c850a6SDerek J. Clark 	BUTTON_M2,
138e4c850a6SDerek J. Clark 	/* These are unused currently, reserved for future devices */
139e4c850a6SDerek J. Clark 	BUTTON_M3,
140e4c850a6SDerek J. Clark 	BUTTON_M4,
141e4c850a6SDerek J. Clark 	BUTTON_M5,
142e4c850a6SDerek J. Clark 	BUTTON_M6,
143e4c850a6SDerek J. Clark };
144e4c850a6SDerek J. Clark 
145e4c850a6SDerek J. Clark struct oxp_button_idx {
146e4c850a6SDerek J. Clark 	enum oxp_joybutton_index button_idx;
147e4c850a6SDerek J. Clark 	u8 mapping_idx;
148e4c850a6SDerek J. Clark } __packed;
149e4c850a6SDerek J. Clark 
150e4c850a6SDerek J. Clark struct oxp_bmap_page_1 {
151e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_a;
152e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_b;
153e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_x;
154e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_y;
155e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_lb;
156e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_rb;
157e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_lt;
158e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_rt;
159e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_start;
160e4c850a6SDerek J. Clark } __packed;
161e4c850a6SDerek J. Clark 
162e4c850a6SDerek J. Clark struct oxp_bmap_page_2 {
163e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_select;
164e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_l3;
165e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_r3;
166e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_dup;
167e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_ddown;
168e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_dleft;
169e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_dright;
170e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_m1;
171e4c850a6SDerek J. Clark 	struct oxp_button_idx btn_m2;
172e4c850a6SDerek J. Clark } __packed;
173e4c850a6SDerek J. Clark 
17484910c45SDerek J. Clark static struct oxp_hid_cfg {
17584910c45SDerek J. Clark 	struct delayed_work oxp_rgb_queue;
176e4c850a6SDerek J. Clark 	struct delayed_work oxp_btn_queue;
177e4c850a6SDerek J. Clark 	struct oxp_bmap_page_1 *bmap_1;
178e4c850a6SDerek J. Clark 	struct oxp_bmap_page_2 *bmap_2;
1792f424f28SDerek J. Clark 	struct delayed_work oxp_mcu_init;
18084910c45SDerek J. Clark 	struct led_classdev_mc *led_mc;
18184910c45SDerek J. Clark 	struct hid_device *hdev;
18284910c45SDerek J. Clark 	struct mutex cfg_mutex; /*ensure single synchronous output report*/
18384910c45SDerek J. Clark 	u8 rgb_brightness;
1842f424f28SDerek J. Clark 	u8 gamepad_mode;
18599bde1dfSDerek J. Clark 	u8 rumble_intensity;
18684910c45SDerek J. Clark 	u8 rgb_effect;
18784910c45SDerek J. Clark 	u8 rgb_speed;
18884910c45SDerek J. Clark 	u8 rgb_en;
18984910c45SDerek J. Clark } drvdata;
19084910c45SDerek J. Clark 
191e4c850a6SDerek J. Clark #define OXP_FILL_PAGE_SLOT(page, btn)            \
192e4c850a6SDerek J. Clark 	{ .button_idx = (page)->btn.button_idx,  \
193e4c850a6SDerek J. Clark 	  .mapping_idx = (page)->btn.mapping_idx }
194e4c850a6SDerek J. Clark 
1952f424f28SDerek J. Clark enum oxp_gamepad_mode_index {
1962f424f28SDerek J. Clark 	OXP_GP_MODE_XINPUT = 0x00,
1972f424f28SDerek J. Clark 	OXP_GP_MODE_DEBUG = 0x03,
1982f424f28SDerek J. Clark };
1992f424f28SDerek J. Clark 
2002f424f28SDerek J. Clark static const char *const oxp_gamepad_mode_text[] = {
2012f424f28SDerek J. Clark 	[OXP_GP_MODE_XINPUT] = "xinput",
2022f424f28SDerek J. Clark 	[OXP_GP_MODE_DEBUG] = "debug",
2032f424f28SDerek J. Clark };
2042f424f28SDerek J. Clark 
20584910c45SDerek J. Clark enum oxp_feature_en_index {
20684910c45SDerek J. Clark 	OXP_FEAT_DISABLED,
20784910c45SDerek J. Clark 	OXP_FEAT_ENABLED,
20884910c45SDerek J. Clark };
20984910c45SDerek J. Clark 
21084910c45SDerek J. Clark static const char *const oxp_feature_en_text[] = {
21184910c45SDerek J. Clark 	[OXP_FEAT_DISABLED] = "false",
21284910c45SDerek J. Clark 	[OXP_FEAT_ENABLED] = "true",
21384910c45SDerek J. Clark };
21484910c45SDerek J. Clark 
21584910c45SDerek J. Clark enum oxp_rgb_effect_index {
21684910c45SDerek J. Clark 	OXP_UNKNOWN,
21784910c45SDerek J. Clark 	OXP_EFFECT_AURORA,
21884910c45SDerek J. Clark 	OXP_EFFECT_BIRTHDAY,
21984910c45SDerek J. Clark 	OXP_EFFECT_FLOWING,
22084910c45SDerek J. Clark 	OXP_EFFECT_CHROMA_1,
22184910c45SDerek J. Clark 	OXP_EFFECT_NEON,
22284910c45SDerek J. Clark 	OXP_EFFECT_CHROMA_2,
22384910c45SDerek J. Clark 	OXP_EFFECT_DREAMY,
22484910c45SDerek J. Clark 	OXP_EFFECT_WARM,
22584910c45SDerek J. Clark 	OXP_EFFECT_CYBERPUNK,
22684910c45SDerek J. Clark 	OXP_EFFECT_SEA,
22784910c45SDerek J. Clark 	OXP_EFFECT_SUNSET,
22884910c45SDerek J. Clark 	OXP_EFFECT_COLORFUL,
22984910c45SDerek J. Clark 	OXP_EFFECT_MONSTER,
23084910c45SDerek J. Clark 	OXP_EFFECT_GREEN,
23184910c45SDerek J. Clark 	OXP_EFFECT_BLUE,
23284910c45SDerek J. Clark 	OXP_EFFECT_YELLOW,
23384910c45SDerek J. Clark 	OXP_EFFECT_TEAL,
23484910c45SDerek J. Clark 	OXP_EFFECT_PURPLE,
23584910c45SDerek J. Clark 	OXP_EFFECT_FOGGY,
23684910c45SDerek J. Clark 	OXP_EFFECT_MONO_LIST, /* placeholder for effect_index_show */
23784910c45SDerek J. Clark };
23884910c45SDerek J. Clark 
23984910c45SDerek J. Clark /* These belong to rgb_effect_index, but we want to hide them from
24084910c45SDerek J. Clark  * rgb_effect_text
24184910c45SDerek J. Clark  */
24284910c45SDerek J. Clark 
24384910c45SDerek J. Clark #define OXP_GET_PROPERTY 0xfc
24484910c45SDerek J. Clark #define OXP_SET_PROPERTY 0xfd
24584910c45SDerek J. Clark #define OXP_EFFECT_MONO_TRUE 0xfe /* actual index for monocolor */
24684910c45SDerek J. Clark 
24784910c45SDerek J. Clark static const char *const oxp_rgb_effect_text[] = {
24884910c45SDerek J. Clark 	[OXP_UNKNOWN] = "unknown",
24984910c45SDerek J. Clark 	[OXP_EFFECT_AURORA] = "aurora",
25084910c45SDerek J. Clark 	[OXP_EFFECT_BIRTHDAY] = "birthday_cake",
25184910c45SDerek J. Clark 	[OXP_EFFECT_FLOWING] = "flowing_light",
25284910c45SDerek J. Clark 	[OXP_EFFECT_CHROMA_1] = "chroma_popping",
25384910c45SDerek J. Clark 	[OXP_EFFECT_NEON] = "neon",
25484910c45SDerek J. Clark 	[OXP_EFFECT_CHROMA_2] = "chroma_breathing",
25584910c45SDerek J. Clark 	[OXP_EFFECT_DREAMY] = "dreamy",
25684910c45SDerek J. Clark 	[OXP_EFFECT_WARM] = "warm_sun",
25784910c45SDerek J. Clark 	[OXP_EFFECT_CYBERPUNK] = "cyberpunk",
25884910c45SDerek J. Clark 	[OXP_EFFECT_SEA] = "sea_foam",
25984910c45SDerek J. Clark 	[OXP_EFFECT_SUNSET] = "sunset_afterglow",
26084910c45SDerek J. Clark 	[OXP_EFFECT_COLORFUL] = "colorful",
26184910c45SDerek J. Clark 	[OXP_EFFECT_MONSTER] = "monster_woke",
26284910c45SDerek J. Clark 	[OXP_EFFECT_GREEN] = "green_breathing",
26384910c45SDerek J. Clark 	[OXP_EFFECT_BLUE] = "blue_breathing",
26484910c45SDerek J. Clark 	[OXP_EFFECT_YELLOW] = "yellow_breathing",
26584910c45SDerek J. Clark 	[OXP_EFFECT_TEAL] = "teal_breathing",
26684910c45SDerek J. Clark 	[OXP_EFFECT_PURPLE] = "purple_breathing",
26784910c45SDerek J. Clark 	[OXP_EFFECT_FOGGY] = "foggy_haze",
26884910c45SDerek J. Clark 	[OXP_EFFECT_MONO_LIST] = "monocolor",
26984910c45SDerek J. Clark };
27084910c45SDerek J. Clark 
27199bde1dfSDerek J. Clark enum oxp_rumble_side_index {
27299bde1dfSDerek J. Clark 	OXP_RUMBLE_LEFT = 0x00,
27399bde1dfSDerek J. Clark 	OXP_RUMBLE_RIGHT,
27499bde1dfSDerek J. Clark };
27599bde1dfSDerek J. Clark 
27684910c45SDerek J. Clark struct oxp_gen_1_rgb_report {
27784910c45SDerek J. Clark 	u8 report_id;
27884910c45SDerek J. Clark 	u8 message_id;
27984910c45SDerek J. Clark 	u8 padding_2[2];
28084910c45SDerek J. Clark 	u8 effect;
28184910c45SDerek J. Clark 	u8 enabled;
28284910c45SDerek J. Clark 	u8 speed;
28384910c45SDerek J. Clark 	u8 brightness;
28484910c45SDerek J. Clark 	u8 red;
28584910c45SDerek J. Clark 	u8 green;
28684910c45SDerek J. Clark 	u8 blue;
28784910c45SDerek J. Clark } __packed;
28884910c45SDerek J. Clark 
289252c4bf1SDerek J. Clark struct oxp_gen_2_rgb_report {
290252c4bf1SDerek J. Clark 	u8 report_id;
291252c4bf1SDerek J. Clark 	u8 header_id;
292252c4bf1SDerek J. Clark 	u8 padding_2;
293252c4bf1SDerek J. Clark 	u8 message_id;
294252c4bf1SDerek J. Clark 	u8 padding_4[2];
295252c4bf1SDerek J. Clark 	u8 enabled;
296252c4bf1SDerek J. Clark 	u8 speed;
297252c4bf1SDerek J. Clark 	u8 brightness;
298252c4bf1SDerek J. Clark 	u8 red;
299252c4bf1SDerek J. Clark 	u8 green;
300252c4bf1SDerek J. Clark 	u8 blue;
301252c4bf1SDerek J. Clark 	u8 padding_12[3];
302252c4bf1SDerek J. Clark 	u8 effect;
303252c4bf1SDerek J. Clark } __packed;
304252c4bf1SDerek J. Clark 
305e4c850a6SDerek J. Clark struct oxp_attr {
306e4c850a6SDerek J. Clark 	u8 index;
307e4c850a6SDerek J. Clark };
308e4c850a6SDerek J. Clark 
30984910c45SDerek J. Clark static u16 get_usage_page(struct hid_device *hdev)
31084910c45SDerek J. Clark {
31184910c45SDerek J. Clark 	return hdev->collection[0].usage >> 16;
31284910c45SDerek J. Clark }
31384910c45SDerek J. Clark 
31484910c45SDerek J. Clark static int oxp_hid_raw_event_gen_1(struct hid_device *hdev,
31584910c45SDerek J. Clark 				   struct hid_report *report, u8 *data,
31684910c45SDerek J. Clark 				   int size)
31784910c45SDerek J. Clark {
31884910c45SDerek J. Clark 	struct led_classdev_mc *led_mc = drvdata.led_mc;
31984910c45SDerek J. Clark 	struct oxp_gen_1_rgb_report *rgb_rep;
32084910c45SDerek J. Clark 
32184910c45SDerek J. Clark 	if (data[1] != OXP_FID_GEN1_RGB_REPLY)
32284910c45SDerek J. Clark 		return 0;
32384910c45SDerek J. Clark 
32484910c45SDerek J. Clark 	rgb_rep = (struct oxp_gen_1_rgb_report *)data;
32584910c45SDerek J. Clark 	/* Ensure we save monocolor as the list value */
32684910c45SDerek J. Clark 	drvdata.rgb_effect = rgb_rep->effect == OXP_EFFECT_MONO_TRUE ?
32784910c45SDerek J. Clark 			     OXP_EFFECT_MONO_LIST :
32884910c45SDerek J. Clark 			     rgb_rep->effect;
32984910c45SDerek J. Clark 	drvdata.rgb_speed = rgb_rep->speed;
33084910c45SDerek J. Clark 	drvdata.rgb_en = rgb_rep->enabled == 0 ? OXP_FEAT_DISABLED :
33184910c45SDerek J. Clark 						 OXP_FEAT_ENABLED;
33284910c45SDerek J. Clark 	drvdata.rgb_brightness = rgb_rep->brightness;
33384910c45SDerek J. Clark 	led_mc->led_cdev.brightness = rgb_rep->brightness / 4 *
33484910c45SDerek J. Clark 				      led_mc->led_cdev.max_brightness;
33584910c45SDerek J. Clark 	/* If monocolor had less than 100% brightness on the previous boot,
33684910c45SDerek J. Clark 	 * there will be no reliable way to determine the real intensity.
33784910c45SDerek J. Clark 	 * Since intensity scaling is used with a hardware brightness set at max,
33884910c45SDerek J. Clark 	 * our brightness will always look like 100%. Use the last set value to
33984910c45SDerek J. Clark 	 * prevent successive boots from lowering the brightness further.
34084910c45SDerek J. Clark 	 * Brightness will be "wrong" but the effect will remain the same visually.
34184910c45SDerek J. Clark 	 */
34284910c45SDerek J. Clark 	led_mc->subled_info[0].intensity = rgb_rep->red;
34384910c45SDerek J. Clark 	led_mc->subled_info[1].intensity = rgb_rep->green;
34484910c45SDerek J. Clark 	led_mc->subled_info[2].intensity = rgb_rep->blue;
34584910c45SDerek J. Clark 
34684910c45SDerek J. Clark 	return 0;
34784910c45SDerek J. Clark }
34884910c45SDerek J. Clark 
3492f424f28SDerek J. Clark static int oxp_gen_2_property_out(enum oxp_function_index fid, u8 *data, u8 data_size);
350e4c850a6SDerek J. Clark static int oxp_set_buttons(void);
35199bde1dfSDerek J. Clark static int oxp_rumble_intensity_set(u8 intensity);
3522f424f28SDerek J. Clark 
3532f424f28SDerek J. Clark static void oxp_mcu_init_fn(struct work_struct *work)
3542f424f28SDerek J. Clark {
3552f424f28SDerek J. Clark 	u8 gp_mode_data[3] = { OXP_GP_MODE_DEBUG, 0x01, 0x02 };
3562f424f28SDerek J. Clark 	int ret;
3572f424f28SDerek J. Clark 
358e4c850a6SDerek J. Clark 	/* Re-apply the button mapping */
359e4c850a6SDerek J. Clark 	ret = oxp_set_buttons();
360e4c850a6SDerek J. Clark 	if (ret)
361e4c850a6SDerek J. Clark 		dev_err(&drvdata.hdev->dev,
362e4c850a6SDerek J. Clark 			"Error: Failed to set button mapping: %i\n", ret);
363e4c850a6SDerek J. Clark 
3642f424f28SDerek J. Clark 	/* Cycle the gamepad mode */
3652f424f28SDerek J. Clark 	ret = oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, gp_mode_data, 3);
3662f424f28SDerek J. Clark 	if (ret)
3672f424f28SDerek J. Clark 		dev_err(&drvdata.hdev->dev,
3682f424f28SDerek J. Clark 			"Error: Failed to set gamepad mode: %i\n", ret);
3692f424f28SDerek J. Clark 
3702f424f28SDerek J. Clark 	/* Remainder only applies for xinput mode */
3712f424f28SDerek J. Clark 	if (drvdata.gamepad_mode == OXP_GP_MODE_DEBUG)
3722f424f28SDerek J. Clark 		return;
3732f424f28SDerek J. Clark 
3742f424f28SDerek J. Clark 	gp_mode_data[0] = OXP_GP_MODE_XINPUT;
3752f424f28SDerek J. Clark 	ret = oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, gp_mode_data, 3);
3762f424f28SDerek J. Clark 	if (ret)
3772f424f28SDerek J. Clark 		dev_err(&drvdata.hdev->dev,
3782f424f28SDerek J. Clark 			"Error: Failed to set gamepad mode: %i\n", ret);
37999bde1dfSDerek J. Clark 
38099bde1dfSDerek J. Clark 	/* Set vibration level */
38199bde1dfSDerek J. Clark 	ret = oxp_rumble_intensity_set(drvdata.rumble_intensity);
38299bde1dfSDerek J. Clark 	if (ret)
38399bde1dfSDerek J. Clark 		dev_err(&drvdata.hdev->dev,
38499bde1dfSDerek J. Clark 			"Error: Failed to set rumble intensity: %i\n", ret);
3852f424f28SDerek J. Clark }
3862f424f28SDerek J. Clark 
387252c4bf1SDerek J. Clark static int oxp_hid_raw_event_gen_2(struct hid_device *hdev,
388252c4bf1SDerek J. Clark 				   struct hid_report *report, u8 *data,
389252c4bf1SDerek J. Clark 				   int size)
390252c4bf1SDerek J. Clark {
391252c4bf1SDerek J. Clark 	struct led_classdev_mc *led_mc = drvdata.led_mc;
392252c4bf1SDerek J. Clark 	struct oxp_gen_2_rgb_report *rgb_rep;
393252c4bf1SDerek J. Clark 
394252c4bf1SDerek J. Clark 	if (data[0] != OXP_FID_GEN2_STATUS_EVENT)
395252c4bf1SDerek J. Clark 		return 0;
396252c4bf1SDerek J. Clark 
3972f424f28SDerek J. Clark 	/* Sent ~6s after resume event, indicating the MCU has fully reset.
3982f424f28SDerek J. Clark 	 * Re-apply our settings after this has been received.
3992f424f28SDerek J. Clark 	 */
4002f424f28SDerek J. Clark 	if (data[3] == OXP_EFFECT_MONO_TRUE) {
401*8bb7c0fdSMarco Crivellari 		mod_delayed_work(system_dfl_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50));
4022f424f28SDerek J. Clark 		return 0;
4032f424f28SDerek J. Clark 	}
4042f424f28SDerek J. Clark 
405252c4bf1SDerek J. Clark 	if (data[3] != OXP_GET_PROPERTY)
406252c4bf1SDerek J. Clark 		return 0;
407252c4bf1SDerek J. Clark 
408252c4bf1SDerek J. Clark 	rgb_rep = (struct oxp_gen_2_rgb_report *)data;
409252c4bf1SDerek J. Clark 	/* Ensure we save monocolor as the list value */
410252c4bf1SDerek J. Clark 	drvdata.rgb_effect = rgb_rep->effect == OXP_EFFECT_MONO_TRUE ?
411252c4bf1SDerek J. Clark 			     OXP_EFFECT_MONO_LIST :
412252c4bf1SDerek J. Clark 			     rgb_rep->effect;
413252c4bf1SDerek J. Clark 	drvdata.rgb_speed = rgb_rep->speed;
414252c4bf1SDerek J. Clark 	drvdata.rgb_en = rgb_rep->enabled == 0 ? OXP_FEAT_DISABLED :
415252c4bf1SDerek J. Clark 						 OXP_FEAT_ENABLED;
416252c4bf1SDerek J. Clark 	drvdata.rgb_brightness = rgb_rep->brightness;
417252c4bf1SDerek J. Clark 	led_mc->led_cdev.brightness = rgb_rep->brightness / 4 *
418252c4bf1SDerek J. Clark 				      led_mc->led_cdev.max_brightness;
419252c4bf1SDerek J. Clark 	/* If monocolor had less than 100% brightness on the previous boot,
420252c4bf1SDerek J. Clark 	 * there will be no reliable way to determine the real intensity.
421252c4bf1SDerek J. Clark 	 * Since intensity scaling is used with a hardware brightness set at max,
422252c4bf1SDerek J. Clark 	 * our brightness will always look like 100%. Use the last set value to
423252c4bf1SDerek J. Clark 	 * prevent successive boots from lowering the brightness further.
424252c4bf1SDerek J. Clark 	 * Brightness will be "wrong" but the effect will remain the same visually.
425252c4bf1SDerek J. Clark 	 */
426252c4bf1SDerek J. Clark 	led_mc->subled_info[0].intensity = rgb_rep->red;
427252c4bf1SDerek J. Clark 	led_mc->subled_info[1].intensity = rgb_rep->green;
428252c4bf1SDerek J. Clark 	led_mc->subled_info[2].intensity = rgb_rep->blue;
429252c4bf1SDerek J. Clark 
430252c4bf1SDerek J. Clark 	return 0;
431252c4bf1SDerek J. Clark }
432252c4bf1SDerek J. Clark 
43384910c45SDerek J. Clark static int oxp_hid_raw_event(struct hid_device *hdev, struct hid_report *report,
43484910c45SDerek J. Clark 			     u8 *data, int size)
43584910c45SDerek J. Clark {
43684910c45SDerek J. Clark 	u16 up = get_usage_page(hdev);
43784910c45SDerek J. Clark 
43884910c45SDerek J. Clark 	dev_dbg(&hdev->dev, "raw event data: [%*ph]\n", OXP_PACKET_SIZE, data);
43984910c45SDerek J. Clark 
44084910c45SDerek J. Clark 	switch (up) {
44184910c45SDerek J. Clark 	case GEN1_USAGE_PAGE:
44284910c45SDerek J. Clark 		return oxp_hid_raw_event_gen_1(hdev, report, data, size);
443252c4bf1SDerek J. Clark 	case GEN2_USAGE_PAGE:
444252c4bf1SDerek J. Clark 		return oxp_hid_raw_event_gen_2(hdev, report, data, size);
44584910c45SDerek J. Clark 	default:
44684910c45SDerek J. Clark 		break;
44784910c45SDerek J. Clark 	}
44884910c45SDerek J. Clark 
44984910c45SDerek J. Clark 	return 0;
45084910c45SDerek J. Clark }
45184910c45SDerek J. Clark 
45284910c45SDerek J. Clark static int mcu_property_out(u8 *header, size_t header_size, u8 *data,
45384910c45SDerek J. Clark 			    size_t data_size, u8 *footer, size_t footer_size)
45484910c45SDerek J. Clark {
45584910c45SDerek J. Clark 	unsigned char *dmabuf __free(kfree) = kzalloc(OXP_PACKET_SIZE, GFP_KERNEL);
45684910c45SDerek J. Clark 	int ret;
45784910c45SDerek J. Clark 
45884910c45SDerek J. Clark 	if (!dmabuf)
45984910c45SDerek J. Clark 		return -ENOMEM;
46084910c45SDerek J. Clark 
46184910c45SDerek J. Clark 	if (header_size + data_size + footer_size > OXP_PACKET_SIZE)
46284910c45SDerek J. Clark 		return -EINVAL;
46384910c45SDerek J. Clark 
46484910c45SDerek J. Clark 	guard(mutex)(&drvdata.cfg_mutex);
46584910c45SDerek J. Clark 	memcpy(dmabuf, header, header_size);
46684910c45SDerek J. Clark 	memcpy(dmabuf + header_size, data, data_size);
46784910c45SDerek J. Clark 	if (footer_size)
46884910c45SDerek J. Clark 		memcpy(dmabuf + OXP_PACKET_SIZE - footer_size, footer, footer_size);
46984910c45SDerek J. Clark 
47084910c45SDerek J. Clark 	dev_dbg(&drvdata.hdev->dev, "raw data: [%*ph]\n", OXP_PACKET_SIZE, dmabuf);
47184910c45SDerek J. Clark 
47284910c45SDerek J. Clark 	ret = hid_hw_output_report(drvdata.hdev, dmabuf, OXP_PACKET_SIZE);
47384910c45SDerek J. Clark 	if (ret < 0)
47484910c45SDerek J. Clark 		return ret;
47584910c45SDerek J. Clark 
47684910c45SDerek J. Clark 	/* MCU takes 200ms to be ready for another command. */
47784910c45SDerek J. Clark 	msleep(200);
47884910c45SDerek J. Clark 	return ret == OXP_PACKET_SIZE ? 0 : -EIO;
47984910c45SDerek J. Clark }
48084910c45SDerek J. Clark 
48184910c45SDerek J. Clark static int oxp_gen_1_property_out(enum oxp_function_index fid, u8 *data,
48284910c45SDerek J. Clark 				  u8 data_size)
48384910c45SDerek J. Clark {
48484910c45SDerek J. Clark 	u8 header[] = { fid, GEN1_MESSAGE_ID };
48584910c45SDerek J. Clark 	size_t header_size = ARRAY_SIZE(header);
48684910c45SDerek J. Clark 
48784910c45SDerek J. Clark 	return mcu_property_out(header, header_size, data, data_size, NULL, 0);
48884910c45SDerek J. Clark }
48984910c45SDerek J. Clark 
490252c4bf1SDerek J. Clark static int oxp_gen_2_property_out(enum oxp_function_index fid, u8 *data,
491252c4bf1SDerek J. Clark 				  u8 data_size)
492252c4bf1SDerek J. Clark {
493252c4bf1SDerek J. Clark 	u8 header[] = { fid, GEN2_MESSAGE_ID, 0x01 };
494252c4bf1SDerek J. Clark 	u8 footer[] = { GEN2_MESSAGE_ID, fid };
495252c4bf1SDerek J. Clark 	size_t header_size = ARRAY_SIZE(header);
496252c4bf1SDerek J. Clark 	size_t footer_size = ARRAY_SIZE(footer);
497252c4bf1SDerek J. Clark 
498252c4bf1SDerek J. Clark 	return mcu_property_out(header, header_size, data, data_size, footer,
499252c4bf1SDerek J. Clark 				footer_size);
500252c4bf1SDerek J. Clark }
501252c4bf1SDerek J. Clark 
5022f424f28SDerek J. Clark static ssize_t gamepad_mode_store(struct device *dev,
5032f424f28SDerek J. Clark 				  struct device_attribute *attr, const char *buf,
5042f424f28SDerek J. Clark 				  size_t count)
5052f424f28SDerek J. Clark {
5062f424f28SDerek J. Clark 	u16 up = get_usage_page(drvdata.hdev);
5072f424f28SDerek J. Clark 	u8 data[3] = { 0x00, 0x01, 0x02 };
5082f424f28SDerek J. Clark 	int ret = -EINVAL;
5092f424f28SDerek J. Clark 	int i;
5102f424f28SDerek J. Clark 
5112f424f28SDerek J. Clark 	if (up != GEN2_USAGE_PAGE)
5122f424f28SDerek J. Clark 		return ret;
5132f424f28SDerek J. Clark 
5142f424f28SDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(oxp_gamepad_mode_text); i++) {
5152f424f28SDerek J. Clark 		if (oxp_gamepad_mode_text[i] && sysfs_streq(buf, oxp_gamepad_mode_text[i])) {
5162f424f28SDerek J. Clark 			ret = i;
5172f424f28SDerek J. Clark 			break;
5182f424f28SDerek J. Clark 		}
5192f424f28SDerek J. Clark 	}
5202f424f28SDerek J. Clark 	if (ret < 0)
5212f424f28SDerek J. Clark 		return ret;
5222f424f28SDerek J. Clark 
5232f424f28SDerek J. Clark 	data[0] = ret;
5242f424f28SDerek J. Clark 
5252f424f28SDerek J. Clark 	ret = oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, data, 3);
5262f424f28SDerek J. Clark 	if (ret)
5272f424f28SDerek J. Clark 		return ret;
5282f424f28SDerek J. Clark 
5292f424f28SDerek J. Clark 	drvdata.gamepad_mode = data[0];
5302f424f28SDerek J. Clark 
53199bde1dfSDerek J. Clark 	if (drvdata.gamepad_mode == OXP_GP_MODE_DEBUG)
53299bde1dfSDerek J. Clark 		return count;
53399bde1dfSDerek J. Clark 
53499bde1dfSDerek J. Clark 	/* Re-apply rumble settings as switching gamepad mode will override */
53599bde1dfSDerek J. Clark 	ret = oxp_rumble_intensity_set(drvdata.rumble_intensity);
53699bde1dfSDerek J. Clark 	if (ret)
53799bde1dfSDerek J. Clark 		return ret;
53899bde1dfSDerek J. Clark 
5392f424f28SDerek J. Clark 	return count;
5402f424f28SDerek J. Clark }
5412f424f28SDerek J. Clark 
5422f424f28SDerek J. Clark static ssize_t gamepad_mode_show(struct device *dev,
5432f424f28SDerek J. Clark 				 struct device_attribute *attr, char *buf)
5442f424f28SDerek J. Clark {
5452f424f28SDerek J. Clark 	return sysfs_emit(buf, "%s\n", oxp_gamepad_mode_text[drvdata.gamepad_mode]);
5462f424f28SDerek J. Clark }
5472f424f28SDerek J. Clark static DEVICE_ATTR_RW(gamepad_mode);
5482f424f28SDerek J. Clark 
5492f424f28SDerek J. Clark static ssize_t gamepad_mode_index_show(struct device *dev,
5502f424f28SDerek J. Clark 				       struct device_attribute *attr,
5512f424f28SDerek J. Clark 				       char *buf)
5522f424f28SDerek J. Clark {
5532f424f28SDerek J. Clark 	ssize_t count = 0;
5542f424f28SDerek J. Clark 	unsigned int i;
5552f424f28SDerek J. Clark 
5562f424f28SDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(oxp_gamepad_mode_text); i++) {
5572f424f28SDerek J. Clark 		if (!oxp_gamepad_mode_text[i] ||
5582f424f28SDerek J. Clark 		    oxp_gamepad_mode_text[i][0] == '\0')
5592f424f28SDerek J. Clark 			continue;
5602f424f28SDerek J. Clark 
5612f424f28SDerek J. Clark 		count += sysfs_emit_at(buf, count, "%s ", oxp_gamepad_mode_text[i]);
5622f424f28SDerek J. Clark 	}
5632f424f28SDerek J. Clark 
5642f424f28SDerek J. Clark 	if (count)
5652f424f28SDerek J. Clark 		buf[count - 1] = '\n';
5662f424f28SDerek J. Clark 
5672f424f28SDerek J. Clark 	return count;
5682f424f28SDerek J. Clark }
5692f424f28SDerek J. Clark static DEVICE_ATTR_RO(gamepad_mode_index);
5702f424f28SDerek J. Clark 
571e4c850a6SDerek J. Clark static void oxp_set_defaults_bmap_1(struct oxp_bmap_page_1 *bmap)
572e4c850a6SDerek J. Clark {
573e4c850a6SDerek J. Clark 	bmap->btn_a.button_idx = BUTTON_A;
574e4c850a6SDerek J. Clark 	bmap->btn_a.mapping_idx = 0;
575e4c850a6SDerek J. Clark 	bmap->btn_b.button_idx = BUTTON_B;
576e4c850a6SDerek J. Clark 	bmap->btn_b.mapping_idx = 1;
577e4c850a6SDerek J. Clark 	bmap->btn_x.button_idx = BUTTON_X;
578e4c850a6SDerek J. Clark 	bmap->btn_x.mapping_idx = 2;
579e4c850a6SDerek J. Clark 	bmap->btn_y.button_idx = BUTTON_Y;
580e4c850a6SDerek J. Clark 	bmap->btn_y.mapping_idx = 3;
581e4c850a6SDerek J. Clark 	bmap->btn_lb.button_idx = BUTTON_LB;
582e4c850a6SDerek J. Clark 	bmap->btn_lb.mapping_idx = 4;
583e4c850a6SDerek J. Clark 	bmap->btn_rb.button_idx = BUTTON_RB;
584e4c850a6SDerek J. Clark 	bmap->btn_rb.mapping_idx = 5;
585e4c850a6SDerek J. Clark 	bmap->btn_lt.button_idx = BUTTON_LT;
586e4c850a6SDerek J. Clark 	bmap->btn_lt.mapping_idx = 6;
587e4c850a6SDerek J. Clark 	bmap->btn_rt.button_idx = BUTTON_RT;
588e4c850a6SDerek J. Clark 	bmap->btn_rt.mapping_idx = 7;
589e4c850a6SDerek J. Clark 	bmap->btn_start.button_idx = BUTTON_START;
590e4c850a6SDerek J. Clark 	bmap->btn_start.mapping_idx = 8;
591e4c850a6SDerek J. Clark }
592e4c850a6SDerek J. Clark 
593e4c850a6SDerek J. Clark static void oxp_set_defaults_bmap_2(struct oxp_bmap_page_2 *bmap)
594e4c850a6SDerek J. Clark {
595e4c850a6SDerek J. Clark 	bmap->btn_select.button_idx = BUTTON_SELECT;
596e4c850a6SDerek J. Clark 	bmap->btn_select.mapping_idx = 9;
597e4c850a6SDerek J. Clark 	bmap->btn_l3.button_idx = BUTTON_L3;
598e4c850a6SDerek J. Clark 	bmap->btn_l3.mapping_idx = 10;
599e4c850a6SDerek J. Clark 	bmap->btn_r3.button_idx = BUTTON_R3;
600e4c850a6SDerek J. Clark 	bmap->btn_r3.mapping_idx = 11;
601e4c850a6SDerek J. Clark 	bmap->btn_dup.button_idx = BUTTON_DUP;
602e4c850a6SDerek J. Clark 	bmap->btn_dup.mapping_idx = 12;
603e4c850a6SDerek J. Clark 	bmap->btn_ddown.button_idx = BUTTON_DDOWN;
604e4c850a6SDerek J. Clark 	bmap->btn_ddown.mapping_idx = 13;
605e4c850a6SDerek J. Clark 	bmap->btn_dleft.button_idx = BUTTON_DLEFT;
606e4c850a6SDerek J. Clark 	bmap->btn_dleft.mapping_idx = 14;
607e4c850a6SDerek J. Clark 	bmap->btn_dright.button_idx = BUTTON_DRIGHT;
608e4c850a6SDerek J. Clark 	bmap->btn_dright.mapping_idx = 15;
609e4c850a6SDerek J. Clark 	bmap->btn_m1.button_idx = BUTTON_M1;
610e4c850a6SDerek J. Clark 	bmap->btn_m1.mapping_idx = 48; /* KEY_F15 */
611e4c850a6SDerek J. Clark 	bmap->btn_m2.button_idx = BUTTON_M2;
612e4c850a6SDerek J. Clark 	bmap->btn_m2.mapping_idx = 49; /* KEY_F16 */
613e4c850a6SDerek J. Clark }
614e4c850a6SDerek J. Clark 
615e4c850a6SDerek J. Clark static void oxp_page_fill_data(char *buf, const struct oxp_button_idx *buttons,
616e4c850a6SDerek J. Clark 			       size_t len)
617e4c850a6SDerek J. Clark {
618e4c850a6SDerek J. Clark 	size_t offset_increment = sizeof(u8) + sizeof(struct oxp_button_idx);
619e4c850a6SDerek J. Clark 	size_t offset = 5;
620e4c850a6SDerek J. Clark 	unsigned int i;
621e4c850a6SDerek J. Clark 
622e4c850a6SDerek J. Clark 	for (i = 0; i < len; i++, offset += offset_increment) {
623e4c850a6SDerek J. Clark 		buf[offset] = (u8)buttons[i].button_idx;
624e4c850a6SDerek J. Clark 		memcpy(buf + offset + 1,
625e4c850a6SDerek J. Clark 		       &oxp_button_table[buttons[i].mapping_idx].data,
626e4c850a6SDerek J. Clark 		       sizeof(struct oxp_button_data));
627e4c850a6SDerek J. Clark 	}
628e4c850a6SDerek J. Clark }
629e4c850a6SDerek J. Clark 
630e4c850a6SDerek J. Clark static int oxp_set_buttons(void)
631e4c850a6SDerek J. Clark {
632e4c850a6SDerek J. Clark 	u8 page_1[59] = { 0x02, 0x38, 0x20, 0x01, 0x01 };
633e4c850a6SDerek J. Clark 	u8 page_2[59] = { 0x02, 0x38, 0x20, 0x02, 0x01 };
634e4c850a6SDerek J. Clark 	u16 up = get_usage_page(drvdata.hdev);
635e4c850a6SDerek J. Clark 	int ret;
636e4c850a6SDerek J. Clark 
637e4c850a6SDerek J. Clark 	if (up != GEN2_USAGE_PAGE)
638e4c850a6SDerek J. Clark 		return -EINVAL;
639e4c850a6SDerek J. Clark 
640e4c850a6SDerek J. Clark 	const struct oxp_button_idx p1[] = {
641e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_a),
642e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_b),
643e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_x),
644e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_y),
645e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_lb),
646e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_rb),
647e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_lt),
648e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_rt),
649e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_start),
650e4c850a6SDerek J. Clark 	};
651e4c850a6SDerek J. Clark 
652e4c850a6SDerek J. Clark 	const struct oxp_button_idx p2[] = {
653e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_select),
654e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_l3),
655e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_r3),
656e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_dup),
657e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_ddown),
658e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_dleft),
659e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_dright),
660e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_m1),
661e4c850a6SDerek J. Clark 		OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_m2),
662e4c850a6SDerek J. Clark 	};
663e4c850a6SDerek J. Clark 
664e4c850a6SDerek J. Clark 	oxp_page_fill_data(page_1, p1, ARRAY_SIZE(p1));
665e4c850a6SDerek J. Clark 	oxp_page_fill_data(page_2, p2, ARRAY_SIZE(p2));
666e4c850a6SDerek J. Clark 
667e4c850a6SDerek J. Clark 	ret = oxp_gen_2_property_out(OXP_FID_GEN2_KEY_STATE, page_1, ARRAY_SIZE(page_1));
668e4c850a6SDerek J. Clark 	if (ret)
669e4c850a6SDerek J. Clark 		return ret;
670e4c850a6SDerek J. Clark 
671e4c850a6SDerek J. Clark 	return oxp_gen_2_property_out(OXP_FID_GEN2_KEY_STATE, page_2, ARRAY_SIZE(page_2));
672e4c850a6SDerek J. Clark }
673e4c850a6SDerek J. Clark 
674e4c850a6SDerek J. Clark static void oxp_reset_buttons(void)
675e4c850a6SDerek J. Clark {
676e4c850a6SDerek J. Clark 	oxp_set_defaults_bmap_1(drvdata.bmap_1);
677e4c850a6SDerek J. Clark 	oxp_set_defaults_bmap_2(drvdata.bmap_2);
678e4c850a6SDerek J. Clark }
679e4c850a6SDerek J. Clark 
680e4c850a6SDerek J. Clark static ssize_t reset_buttons_store(struct device *dev,
681e4c850a6SDerek J. Clark 				   struct device_attribute *attr, const char *buf,
682e4c850a6SDerek J. Clark 				   size_t count)
683e4c850a6SDerek J. Clark {
684e4c850a6SDerek J. Clark 	int val, ret;
685e4c850a6SDerek J. Clark 
686e4c850a6SDerek J. Clark 	ret = kstrtoint(buf, 10, &val);
687e4c850a6SDerek J. Clark 	if (ret)
688e4c850a6SDerek J. Clark 		return ret;
689e4c850a6SDerek J. Clark 
690e4c850a6SDerek J. Clark 	if (val != 1)
691e4c850a6SDerek J. Clark 		return -EINVAL;
692e4c850a6SDerek J. Clark 
693e4c850a6SDerek J. Clark 	oxp_reset_buttons();
694e4c850a6SDerek J. Clark 	ret = oxp_set_buttons();
695e4c850a6SDerek J. Clark 	if (ret)
696e4c850a6SDerek J. Clark 		return ret;
697e4c850a6SDerek J. Clark 
698e4c850a6SDerek J. Clark 	return count;
699e4c850a6SDerek J. Clark }
700e4c850a6SDerek J. Clark static DEVICE_ATTR_WO(reset_buttons);
701e4c850a6SDerek J. Clark 
702e4c850a6SDerek J. Clark static void oxp_btn_queue_fn(struct work_struct *work)
703e4c850a6SDerek J. Clark {
704e4c850a6SDerek J. Clark 	int ret;
705e4c850a6SDerek J. Clark 
706e4c850a6SDerek J. Clark 	ret = oxp_set_buttons();
707e4c850a6SDerek J. Clark 	if (ret)
708e4c850a6SDerek J. Clark 		dev_err(&drvdata.hdev->dev,
709e4c850a6SDerek J. Clark 			"Error: Failed to write button mapping: %i\n", ret);
710e4c850a6SDerek J. Clark }
711e4c850a6SDerek J. Clark 
712e4c850a6SDerek J. Clark static int oxp_button_idx_from_str(const char *buf)
713e4c850a6SDerek J. Clark {
714e4c850a6SDerek J. Clark 	int i;
715e4c850a6SDerek J. Clark 
716e4c850a6SDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(oxp_button_table); i++)
717e4c850a6SDerek J. Clark 		if (sysfs_streq(buf, oxp_button_table[i].name))
718e4c850a6SDerek J. Clark 			return i;
719e4c850a6SDerek J. Clark 
720e4c850a6SDerek J. Clark 	return -EINVAL;
721e4c850a6SDerek J. Clark }
722e4c850a6SDerek J. Clark 
723e4c850a6SDerek J. Clark static ssize_t map_button_store(struct device *dev,
724e4c850a6SDerek J. Clark 				struct device_attribute *attr, const char *buf,
725e4c850a6SDerek J. Clark 				size_t count, u8 index)
726e4c850a6SDerek J. Clark {
727e4c850a6SDerek J. Clark 	int idx;
728e4c850a6SDerek J. Clark 
729e4c850a6SDerek J. Clark 	idx = oxp_button_idx_from_str(buf);
730e4c850a6SDerek J. Clark 	if (idx < 0)
731e4c850a6SDerek J. Clark 		return idx;
732e4c850a6SDerek J. Clark 
733e4c850a6SDerek J. Clark 	switch (index) {
734e4c850a6SDerek J. Clark 	case BUTTON_A:
735e4c850a6SDerek J. Clark 		drvdata.bmap_1->btn_a.mapping_idx = idx;
736e4c850a6SDerek J. Clark 		break;
737e4c850a6SDerek J. Clark 	case BUTTON_B:
738e4c850a6SDerek J. Clark 		drvdata.bmap_1->btn_b.mapping_idx = idx;
739e4c850a6SDerek J. Clark 		break;
740e4c850a6SDerek J. Clark 	case BUTTON_X:
741e4c850a6SDerek J. Clark 		drvdata.bmap_1->btn_x.mapping_idx = idx;
742e4c850a6SDerek J. Clark 		break;
743e4c850a6SDerek J. Clark 	case BUTTON_Y:
744e4c850a6SDerek J. Clark 		drvdata.bmap_1->btn_y.mapping_idx = idx;
745e4c850a6SDerek J. Clark 		break;
746e4c850a6SDerek J. Clark 	case BUTTON_LB:
747e4c850a6SDerek J. Clark 		drvdata.bmap_1->btn_lb.mapping_idx = idx;
748e4c850a6SDerek J. Clark 		break;
749e4c850a6SDerek J. Clark 	case BUTTON_RB:
750e4c850a6SDerek J. Clark 		drvdata.bmap_1->btn_rb.mapping_idx = idx;
751e4c850a6SDerek J. Clark 		break;
752e4c850a6SDerek J. Clark 	case BUTTON_LT:
753e4c850a6SDerek J. Clark 		drvdata.bmap_1->btn_lt.mapping_idx = idx;
754e4c850a6SDerek J. Clark 		break;
755e4c850a6SDerek J. Clark 	case BUTTON_RT:
756e4c850a6SDerek J. Clark 		drvdata.bmap_1->btn_rt.mapping_idx = idx;
757e4c850a6SDerek J. Clark 		break;
758e4c850a6SDerek J. Clark 	case BUTTON_START:
759e4c850a6SDerek J. Clark 		drvdata.bmap_1->btn_start.mapping_idx = idx;
760e4c850a6SDerek J. Clark 		break;
761e4c850a6SDerek J. Clark 	case BUTTON_SELECT:
762e4c850a6SDerek J. Clark 		drvdata.bmap_2->btn_select.mapping_idx = idx;
763e4c850a6SDerek J. Clark 		break;
764e4c850a6SDerek J. Clark 	case BUTTON_L3:
765e4c850a6SDerek J. Clark 		drvdata.bmap_2->btn_l3.mapping_idx = idx;
766e4c850a6SDerek J. Clark 		break;
767e4c850a6SDerek J. Clark 	case BUTTON_R3:
768e4c850a6SDerek J. Clark 		drvdata.bmap_2->btn_r3.mapping_idx = idx;
769e4c850a6SDerek J. Clark 		break;
770e4c850a6SDerek J. Clark 	case BUTTON_DUP:
771e4c850a6SDerek J. Clark 		drvdata.bmap_2->btn_dup.mapping_idx = idx;
772e4c850a6SDerek J. Clark 		break;
773e4c850a6SDerek J. Clark 	case BUTTON_DDOWN:
774e4c850a6SDerek J. Clark 		drvdata.bmap_2->btn_ddown.mapping_idx = idx;
775e4c850a6SDerek J. Clark 		break;
776e4c850a6SDerek J. Clark 	case BUTTON_DLEFT:
777e4c850a6SDerek J. Clark 		drvdata.bmap_2->btn_dleft.mapping_idx = idx;
778e4c850a6SDerek J. Clark 		break;
779e4c850a6SDerek J. Clark 	case BUTTON_DRIGHT:
780e4c850a6SDerek J. Clark 		drvdata.bmap_2->btn_dright.mapping_idx = idx;
781e4c850a6SDerek J. Clark 		break;
782e4c850a6SDerek J. Clark 	case BUTTON_M1:
783e4c850a6SDerek J. Clark 		drvdata.bmap_2->btn_m1.mapping_idx = idx;
784e4c850a6SDerek J. Clark 		break;
785e4c850a6SDerek J. Clark 	case BUTTON_M2:
786e4c850a6SDerek J. Clark 		drvdata.bmap_2->btn_m2.mapping_idx = idx;
787e4c850a6SDerek J. Clark 		break;
788e4c850a6SDerek J. Clark 	default:
789e4c850a6SDerek J. Clark 		return -EINVAL;
790e4c850a6SDerek J. Clark 	}
791*8bb7c0fdSMarco Crivellari 	mod_delayed_work(system_dfl_wq, &drvdata.oxp_btn_queue, msecs_to_jiffies(50));
792e4c850a6SDerek J. Clark 	return count;
793e4c850a6SDerek J. Clark }
794e4c850a6SDerek J. Clark 
795e4c850a6SDerek J. Clark static ssize_t map_button_show(struct device *dev,
796e4c850a6SDerek J. Clark 			       struct device_attribute *attr, char *buf,
797e4c850a6SDerek J. Clark 			       u8 index)
798e4c850a6SDerek J. Clark {
799e4c850a6SDerek J. Clark 	u8 i;
800e4c850a6SDerek J. Clark 
801e4c850a6SDerek J. Clark 	switch (index) {
802e4c850a6SDerek J. Clark 	case BUTTON_A:
803e4c850a6SDerek J. Clark 		i = drvdata.bmap_1->btn_a.mapping_idx;
804e4c850a6SDerek J. Clark 		break;
805e4c850a6SDerek J. Clark 	case BUTTON_B:
806e4c850a6SDerek J. Clark 		i = drvdata.bmap_1->btn_b.mapping_idx;
807e4c850a6SDerek J. Clark 		break;
808e4c850a6SDerek J. Clark 	case BUTTON_X:
809e4c850a6SDerek J. Clark 		i = drvdata.bmap_1->btn_x.mapping_idx;
810e4c850a6SDerek J. Clark 		break;
811e4c850a6SDerek J. Clark 	case BUTTON_Y:
812e4c850a6SDerek J. Clark 		i = drvdata.bmap_1->btn_y.mapping_idx;
813e4c850a6SDerek J. Clark 		break;
814e4c850a6SDerek J. Clark 	case BUTTON_LB:
815e4c850a6SDerek J. Clark 		i = drvdata.bmap_1->btn_lb.mapping_idx;
816e4c850a6SDerek J. Clark 		break;
817e4c850a6SDerek J. Clark 	case BUTTON_RB:
818e4c850a6SDerek J. Clark 		i = drvdata.bmap_1->btn_rb.mapping_idx;
819e4c850a6SDerek J. Clark 		break;
820e4c850a6SDerek J. Clark 	case BUTTON_LT:
821e4c850a6SDerek J. Clark 		i = drvdata.bmap_1->btn_lt.mapping_idx;
822e4c850a6SDerek J. Clark 		break;
823e4c850a6SDerek J. Clark 	case BUTTON_RT:
824e4c850a6SDerek J. Clark 		i = drvdata.bmap_1->btn_rt.mapping_idx;
825e4c850a6SDerek J. Clark 		break;
826e4c850a6SDerek J. Clark 	case BUTTON_START:
827e4c850a6SDerek J. Clark 		i = drvdata.bmap_1->btn_start.mapping_idx;
828e4c850a6SDerek J. Clark 		break;
829e4c850a6SDerek J. Clark 	case BUTTON_SELECT:
830e4c850a6SDerek J. Clark 		i = drvdata.bmap_2->btn_select.mapping_idx;
831e4c850a6SDerek J. Clark 		break;
832e4c850a6SDerek J. Clark 	case BUTTON_L3:
833e4c850a6SDerek J. Clark 		i = drvdata.bmap_2->btn_l3.mapping_idx;
834e4c850a6SDerek J. Clark 		break;
835e4c850a6SDerek J. Clark 	case BUTTON_R3:
836e4c850a6SDerek J. Clark 		i = drvdata.bmap_2->btn_r3.mapping_idx;
837e4c850a6SDerek J. Clark 		break;
838e4c850a6SDerek J. Clark 	case BUTTON_DUP:
839e4c850a6SDerek J. Clark 		i = drvdata.bmap_2->btn_dup.mapping_idx;
840e4c850a6SDerek J. Clark 		break;
841e4c850a6SDerek J. Clark 	case BUTTON_DDOWN:
842e4c850a6SDerek J. Clark 		i = drvdata.bmap_2->btn_ddown.mapping_idx;
843e4c850a6SDerek J. Clark 		break;
844e4c850a6SDerek J. Clark 	case BUTTON_DLEFT:
845e4c850a6SDerek J. Clark 		i = drvdata.bmap_2->btn_dleft.mapping_idx;
846e4c850a6SDerek J. Clark 		break;
847e4c850a6SDerek J. Clark 	case BUTTON_DRIGHT:
848e4c850a6SDerek J. Clark 		i = drvdata.bmap_2->btn_dright.mapping_idx;
849e4c850a6SDerek J. Clark 		break;
850e4c850a6SDerek J. Clark 	case BUTTON_M1:
851e4c850a6SDerek J. Clark 		i = drvdata.bmap_2->btn_m1.mapping_idx;
852e4c850a6SDerek J. Clark 		break;
853e4c850a6SDerek J. Clark 	case BUTTON_M2:
854e4c850a6SDerek J. Clark 		i = drvdata.bmap_2->btn_m2.mapping_idx;
855e4c850a6SDerek J. Clark 		break;
856e4c850a6SDerek J. Clark 	default:
857e4c850a6SDerek J. Clark 		return -EINVAL;
858e4c850a6SDerek J. Clark 	}
859e4c850a6SDerek J. Clark 
860e4c850a6SDerek J. Clark 	if (i >= ARRAY_SIZE(oxp_button_table))
861e4c850a6SDerek J. Clark 		return -EINVAL;
862e4c850a6SDerek J. Clark 
863e4c850a6SDerek J. Clark 	return sysfs_emit(buf, "%s\n", oxp_button_table[i].name);
864e4c850a6SDerek J. Clark }
865e4c850a6SDerek J. Clark 
866e4c850a6SDerek J. Clark static ssize_t button_mapping_options_show(struct device *dev,
867e4c850a6SDerek J. Clark 					   struct device_attribute *attr, char *buf)
868e4c850a6SDerek J. Clark {
869e4c850a6SDerek J. Clark 	ssize_t count = 0;
870e4c850a6SDerek J. Clark 	unsigned int i;
871e4c850a6SDerek J. Clark 
872e4c850a6SDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(oxp_button_table); i++)
873e4c850a6SDerek J. Clark 		count += sysfs_emit_at(buf, count, "%s ", oxp_button_table[i].name);
874e4c850a6SDerek J. Clark 
875e4c850a6SDerek J. Clark 	if (count)
876e4c850a6SDerek J. Clark 		buf[count - 1] = '\n';
877e4c850a6SDerek J. Clark 
878e4c850a6SDerek J. Clark 	return count;
879e4c850a6SDerek J. Clark }
880e4c850a6SDerek J. Clark static DEVICE_ATTR_RO(button_mapping_options);
881e4c850a6SDerek J. Clark 
88299bde1dfSDerek J. Clark static int oxp_rumble_intensity_set(u8 intensity)
88399bde1dfSDerek J. Clark {
88499bde1dfSDerek J. Clark 	u8 header[15] = { 0x02, 0x38, 0x02, 0xe3, 0x39, 0xe3, 0x39, 0xe3,
88599bde1dfSDerek J. Clark 			  0x39, 0x01, intensity, 0x05, 0xe3, 0x39, 0xe3 };
88699bde1dfSDerek J. Clark 	u8 footer[9] = { 0x39, 0xe3, 0x39, 0xe3, 0xe3, 0x02, 0x04, 0x39, 0x39 };
88799bde1dfSDerek J. Clark 	size_t footer_size = ARRAY_SIZE(footer);
88899bde1dfSDerek J. Clark 	size_t header_size = ARRAY_SIZE(header);
88999bde1dfSDerek J. Clark 	u8 data[59] = { 0x0 };
89099bde1dfSDerek J. Clark 	size_t data_size = ARRAY_SIZE(data);
89199bde1dfSDerek J. Clark 
89299bde1dfSDerek J. Clark 	memcpy(data, header, header_size);
89399bde1dfSDerek J. Clark 	memcpy(data + data_size - footer_size, footer, footer_size);
89499bde1dfSDerek J. Clark 
89599bde1dfSDerek J. Clark 	return oxp_gen_2_property_out(OXP_FID_GEN2_RUMBLE_SET, data, data_size);
89699bde1dfSDerek J. Clark }
89799bde1dfSDerek J. Clark 
89899bde1dfSDerek J. Clark static ssize_t rumble_intensity_store(struct device *dev,
89999bde1dfSDerek J. Clark 				      struct device_attribute *attr, const char *buf,
90099bde1dfSDerek J. Clark 				      size_t count)
90199bde1dfSDerek J. Clark {
90299bde1dfSDerek J. Clark 	int ret;
90399bde1dfSDerek J. Clark 	u8 val;
90499bde1dfSDerek J. Clark 
90599bde1dfSDerek J. Clark 	ret = kstrtou8(buf, 10, &val);
90699bde1dfSDerek J. Clark 	if (ret)
90799bde1dfSDerek J. Clark 		return ret;
90899bde1dfSDerek J. Clark 
90999bde1dfSDerek J. Clark 	if (val < 0 || val > 5)
91099bde1dfSDerek J. Clark 		return -EINVAL;
91199bde1dfSDerek J. Clark 
91299bde1dfSDerek J. Clark 	ret = oxp_rumble_intensity_set(val);
91399bde1dfSDerek J. Clark 	if (ret)
91499bde1dfSDerek J. Clark 		return ret;
91599bde1dfSDerek J. Clark 
91699bde1dfSDerek J. Clark 	drvdata.rumble_intensity = val;
91799bde1dfSDerek J. Clark 
91899bde1dfSDerek J. Clark 	return count;
91999bde1dfSDerek J. Clark }
92099bde1dfSDerek J. Clark 
92199bde1dfSDerek J. Clark static ssize_t rumble_intensity_show(struct device *dev,
92299bde1dfSDerek J. Clark 				     struct device_attribute *attr, char *buf)
92399bde1dfSDerek J. Clark {
92499bde1dfSDerek J. Clark 	return sysfs_emit(buf, "%i\n", drvdata.rumble_intensity);
92599bde1dfSDerek J. Clark }
92699bde1dfSDerek J. Clark static DEVICE_ATTR_RW(rumble_intensity);
92799bde1dfSDerek J. Clark 
92899bde1dfSDerek J. Clark static ssize_t rumble_intensity_range_show(struct device *dev,
92999bde1dfSDerek J. Clark 					   struct device_attribute *attr, char *buf)
93099bde1dfSDerek J. Clark {
93199bde1dfSDerek J. Clark 	return sysfs_emit(buf, "0-5\n");
93299bde1dfSDerek J. Clark }
93399bde1dfSDerek J. Clark static DEVICE_ATTR_RO(rumble_intensity_range);
93499bde1dfSDerek J. Clark 
935e4c850a6SDerek J. Clark #define OXP_DEVICE_ATTR_RW(_name, _group)                                     \
936e4c850a6SDerek J. Clark 	static ssize_t _name##_store(struct device *dev,                      \
937e4c850a6SDerek J. Clark 				     struct device_attribute *attr,           \
938e4c850a6SDerek J. Clark 				     const char *buf, size_t count)           \
939e4c850a6SDerek J. Clark 	{                                                                     \
940e4c850a6SDerek J. Clark 		return _group##_store(dev, attr, buf, count, _name.index);    \
941e4c850a6SDerek J. Clark 	}                                                                     \
942e4c850a6SDerek J. Clark 	static ssize_t _name##_show(struct device *dev,                       \
943e4c850a6SDerek J. Clark 				    struct device_attribute *attr, char *buf) \
944e4c850a6SDerek J. Clark 	{                                                                     \
945e4c850a6SDerek J. Clark 		return _group##_show(dev, attr, buf, _name.index);            \
946e4c850a6SDerek J. Clark 	}                                                                     \
947e4c850a6SDerek J. Clark 	static DEVICE_ATTR_RW(_name)
948e4c850a6SDerek J. Clark 
949e4c850a6SDerek J. Clark static struct oxp_attr button_a = { BUTTON_A };
950e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_a, map_button);
951e4c850a6SDerek J. Clark 
952e4c850a6SDerek J. Clark static struct oxp_attr button_b = { BUTTON_B };
953e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_b, map_button);
954e4c850a6SDerek J. Clark 
955e4c850a6SDerek J. Clark static struct oxp_attr button_x = { BUTTON_X };
956e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_x, map_button);
957e4c850a6SDerek J. Clark 
958e4c850a6SDerek J. Clark static struct oxp_attr button_y = { BUTTON_Y };
959e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_y, map_button);
960e4c850a6SDerek J. Clark 
961e4c850a6SDerek J. Clark static struct oxp_attr button_lb = { BUTTON_LB };
962e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_lb, map_button);
963e4c850a6SDerek J. Clark 
964e4c850a6SDerek J. Clark static struct oxp_attr button_rb = { BUTTON_RB };
965e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_rb, map_button);
966e4c850a6SDerek J. Clark 
967e4c850a6SDerek J. Clark static struct oxp_attr button_lt = { BUTTON_LT };
968e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_lt, map_button);
969e4c850a6SDerek J. Clark 
970e4c850a6SDerek J. Clark static struct oxp_attr button_rt = { BUTTON_RT };
971e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_rt, map_button);
972e4c850a6SDerek J. Clark 
973e4c850a6SDerek J. Clark static struct oxp_attr button_start = { BUTTON_START };
974e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_start, map_button);
975e4c850a6SDerek J. Clark 
976e4c850a6SDerek J. Clark static struct oxp_attr button_select = { BUTTON_SELECT };
977e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_select, map_button);
978e4c850a6SDerek J. Clark 
979e4c850a6SDerek J. Clark static struct oxp_attr button_l3 = { BUTTON_L3 };
980e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_l3, map_button);
981e4c850a6SDerek J. Clark 
982e4c850a6SDerek J. Clark static struct oxp_attr button_r3 = { BUTTON_R3 };
983e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_r3, map_button);
984e4c850a6SDerek J. Clark 
985e4c850a6SDerek J. Clark static struct oxp_attr button_d_up = { BUTTON_DUP };
986e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_d_up, map_button);
987e4c850a6SDerek J. Clark 
988e4c850a6SDerek J. Clark static struct oxp_attr button_d_down = { BUTTON_DDOWN };
989e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_d_down, map_button);
990e4c850a6SDerek J. Clark 
991e4c850a6SDerek J. Clark static struct oxp_attr button_d_left = { BUTTON_DLEFT };
992e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_d_left, map_button);
993e4c850a6SDerek J. Clark 
994e4c850a6SDerek J. Clark static struct oxp_attr button_d_right = { BUTTON_DRIGHT };
995e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_d_right, map_button);
996e4c850a6SDerek J. Clark 
997e4c850a6SDerek J. Clark static struct oxp_attr button_m1 = { BUTTON_M1 };
998e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_m1, map_button);
999e4c850a6SDerek J. Clark 
1000e4c850a6SDerek J. Clark static struct oxp_attr button_m2 = { BUTTON_M2 };
1001e4c850a6SDerek J. Clark OXP_DEVICE_ATTR_RW(button_m2, map_button);
1002e4c850a6SDerek J. Clark 
10032f424f28SDerek J. Clark static struct attribute *oxp_cfg_attrs[] = {
1004e4c850a6SDerek J. Clark 	&dev_attr_button_a.attr,
1005e4c850a6SDerek J. Clark 	&dev_attr_button_b.attr,
1006e4c850a6SDerek J. Clark 	&dev_attr_button_d_down.attr,
1007e4c850a6SDerek J. Clark 	&dev_attr_button_d_left.attr,
1008e4c850a6SDerek J. Clark 	&dev_attr_button_d_right.attr,
1009e4c850a6SDerek J. Clark 	&dev_attr_button_d_up.attr,
1010e4c850a6SDerek J. Clark 	&dev_attr_button_l3.attr,
1011e4c850a6SDerek J. Clark 	&dev_attr_button_lb.attr,
1012e4c850a6SDerek J. Clark 	&dev_attr_button_lt.attr,
1013e4c850a6SDerek J. Clark 	&dev_attr_button_m1.attr,
1014e4c850a6SDerek J. Clark 	&dev_attr_button_m2.attr,
1015e4c850a6SDerek J. Clark 	&dev_attr_button_mapping_options.attr,
1016e4c850a6SDerek J. Clark 	&dev_attr_button_r3.attr,
1017e4c850a6SDerek J. Clark 	&dev_attr_button_rb.attr,
1018e4c850a6SDerek J. Clark 	&dev_attr_button_rt.attr,
1019e4c850a6SDerek J. Clark 	&dev_attr_button_select.attr,
1020e4c850a6SDerek J. Clark 	&dev_attr_button_start.attr,
1021e4c850a6SDerek J. Clark 	&dev_attr_button_x.attr,
1022e4c850a6SDerek J. Clark 	&dev_attr_button_y.attr,
10232f424f28SDerek J. Clark 	&dev_attr_gamepad_mode.attr,
10242f424f28SDerek J. Clark 	&dev_attr_gamepad_mode_index.attr,
1025e4c850a6SDerek J. Clark 	&dev_attr_reset_buttons.attr,
102699bde1dfSDerek J. Clark 	&dev_attr_rumble_intensity.attr,
102799bde1dfSDerek J. Clark 	&dev_attr_rumble_intensity_range.attr,
10282f424f28SDerek J. Clark 	NULL,
10292f424f28SDerek J. Clark };
10302f424f28SDerek J. Clark 
10312f424f28SDerek J. Clark static const struct attribute_group oxp_cfg_attrs_group = {
10322f424f28SDerek J. Clark 	.attrs = oxp_cfg_attrs,
10332f424f28SDerek J. Clark };
10342f424f28SDerek J. Clark 
103584910c45SDerek J. Clark static int oxp_rgb_status_store(u8 enabled, u8 speed, u8 brightness)
103684910c45SDerek J. Clark {
103784910c45SDerek J. Clark 	u16 up = get_usage_page(drvdata.hdev);
103884910c45SDerek J. Clark 	u8 *data;
103984910c45SDerek J. Clark 
104084910c45SDerek J. Clark 	/* Always default to max brightness and use intensity scaling when in
104184910c45SDerek J. Clark 	 * monocolor mode.
104284910c45SDerek J. Clark 	 */
104384910c45SDerek J. Clark 	switch (up) {
104484910c45SDerek J. Clark 	case GEN1_USAGE_PAGE:
104584910c45SDerek J. Clark 		data = (u8[4]) { OXP_SET_PROPERTY, enabled, speed, brightness };
104684910c45SDerek J. Clark 		if (drvdata.rgb_effect == OXP_EFFECT_MONO_LIST)
104784910c45SDerek J. Clark 			data[3] = 0x04;
104884910c45SDerek J. Clark 		return oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, 4);
1049252c4bf1SDerek J. Clark 	case GEN2_USAGE_PAGE:
1050252c4bf1SDerek J. Clark 		data = (u8[6]) { OXP_SET_PROPERTY, 0x00, 0x02, enabled, speed, brightness };
1051252c4bf1SDerek J. Clark 		if (drvdata.rgb_effect == OXP_EFFECT_MONO_LIST)
1052252c4bf1SDerek J. Clark 			data[5] = 0x04;
1053252c4bf1SDerek J. Clark 		return oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, 6);
105484910c45SDerek J. Clark 	default:
105584910c45SDerek J. Clark 		return -ENODEV;
105684910c45SDerek J. Clark 	}
105784910c45SDerek J. Clark }
105884910c45SDerek J. Clark 
105984910c45SDerek J. Clark static ssize_t oxp_rgb_status_show(void)
106084910c45SDerek J. Clark {
106184910c45SDerek J. Clark 	u16 up = get_usage_page(drvdata.hdev);
106284910c45SDerek J. Clark 	u8 *data;
106384910c45SDerek J. Clark 
106484910c45SDerek J. Clark 	switch (up) {
106584910c45SDerek J. Clark 	case GEN1_USAGE_PAGE:
106684910c45SDerek J. Clark 		data = (u8[1]) { OXP_GET_PROPERTY };
106784910c45SDerek J. Clark 		return oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, 1);
1068252c4bf1SDerek J. Clark 	case GEN2_USAGE_PAGE:
1069252c4bf1SDerek J. Clark 		data = (u8[3]) { OXP_GET_PROPERTY, 0x00, 0x02 };
1070252c4bf1SDerek J. Clark 		return oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, 3);
107184910c45SDerek J. Clark 	default:
107284910c45SDerek J. Clark 		return -ENODEV;
107384910c45SDerek J. Clark 	}
107484910c45SDerek J. Clark }
107584910c45SDerek J. Clark 
107684910c45SDerek J. Clark static int oxp_rgb_color_set(void)
107784910c45SDerek J. Clark {
107884910c45SDerek J. Clark 	u8 max_br = drvdata.led_mc->led_cdev.max_brightness;
107984910c45SDerek J. Clark 	u8 br = drvdata.led_mc->led_cdev.brightness;
108084910c45SDerek J. Clark 	u16 up = get_usage_page(drvdata.hdev);
108184910c45SDerek J. Clark 	u8 green, red, blue;
108284910c45SDerek J. Clark 	size_t size;
108384910c45SDerek J. Clark 	u8 *data;
108484910c45SDerek J. Clark 	int i;
108584910c45SDerek J. Clark 
108684910c45SDerek J. Clark 	red = br * drvdata.led_mc->subled_info[0].intensity / max_br;
108784910c45SDerek J. Clark 	green = br * drvdata.led_mc->subled_info[1].intensity / max_br;
108884910c45SDerek J. Clark 	blue = br * drvdata.led_mc->subled_info[2].intensity / max_br;
108984910c45SDerek J. Clark 
109084910c45SDerek J. Clark 	switch (up) {
109184910c45SDerek J. Clark 	case GEN1_USAGE_PAGE:
109284910c45SDerek J. Clark 		size = 55;
109384910c45SDerek J. Clark 		data = (u8[55]) { OXP_EFFECT_MONO_TRUE };
109484910c45SDerek J. Clark 
109584910c45SDerek J. Clark 		for (i = 0; i < (size - 1) / 3; i++) {
109684910c45SDerek J. Clark 			data[3 * i + 1] = red;
109784910c45SDerek J. Clark 			data[3 * i + 2] = green;
109884910c45SDerek J. Clark 			data[3 * i + 3] = blue;
109984910c45SDerek J. Clark 		}
110084910c45SDerek J. Clark 		return oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, size);
1101252c4bf1SDerek J. Clark 	case GEN2_USAGE_PAGE:
1102252c4bf1SDerek J. Clark 		size = 57;
1103252c4bf1SDerek J. Clark 		data = (u8[57]) { OXP_EFFECT_MONO_TRUE, 0x00, 0x02 };
1104252c4bf1SDerek J. Clark 
1105252c4bf1SDerek J. Clark 		for (i = 1; i < size / 3; i++) {
1106252c4bf1SDerek J. Clark 			data[3 * i] = red;
1107252c4bf1SDerek J. Clark 			data[3 * i + 1] = green;
1108252c4bf1SDerek J. Clark 			data[3 * i + 2] = blue;
1109252c4bf1SDerek J. Clark 		}
1110252c4bf1SDerek J. Clark 		return oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, size);
111184910c45SDerek J. Clark 	default:
111284910c45SDerek J. Clark 		return -ENODEV;
111384910c45SDerek J. Clark 	}
111484910c45SDerek J. Clark }
111584910c45SDerek J. Clark 
111684910c45SDerek J. Clark static int oxp_rgb_effect_set(u8 effect)
111784910c45SDerek J. Clark {
111884910c45SDerek J. Clark 	u16 up = get_usage_page(drvdata.hdev);
111984910c45SDerek J. Clark 	u8 *data;
112084910c45SDerek J. Clark 	int ret;
112184910c45SDerek J. Clark 
112284910c45SDerek J. Clark 	switch (effect) {
112384910c45SDerek J. Clark 	case OXP_EFFECT_AURORA:
112484910c45SDerek J. Clark 	case OXP_EFFECT_BIRTHDAY:
112584910c45SDerek J. Clark 	case OXP_EFFECT_FLOWING:
112684910c45SDerek J. Clark 	case OXP_EFFECT_CHROMA_1:
112784910c45SDerek J. Clark 	case OXP_EFFECT_NEON:
112884910c45SDerek J. Clark 	case OXP_EFFECT_CHROMA_2:
112984910c45SDerek J. Clark 	case OXP_EFFECT_DREAMY:
113084910c45SDerek J. Clark 	case OXP_EFFECT_WARM:
113184910c45SDerek J. Clark 	case OXP_EFFECT_CYBERPUNK:
113284910c45SDerek J. Clark 	case OXP_EFFECT_SEA:
113384910c45SDerek J. Clark 	case OXP_EFFECT_SUNSET:
113484910c45SDerek J. Clark 	case OXP_EFFECT_COLORFUL:
113584910c45SDerek J. Clark 	case OXP_EFFECT_MONSTER:
113684910c45SDerek J. Clark 	case OXP_EFFECT_GREEN:
113784910c45SDerek J. Clark 	case OXP_EFFECT_BLUE:
113884910c45SDerek J. Clark 	case OXP_EFFECT_YELLOW:
113984910c45SDerek J. Clark 	case OXP_EFFECT_TEAL:
114084910c45SDerek J. Clark 	case OXP_EFFECT_PURPLE:
114184910c45SDerek J. Clark 	case OXP_EFFECT_FOGGY:
114284910c45SDerek J. Clark 		switch (up) {
114384910c45SDerek J. Clark 		case GEN1_USAGE_PAGE:
114484910c45SDerek J. Clark 			data = (u8[1]) { effect };
114584910c45SDerek J. Clark 			ret = oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, 1);
114684910c45SDerek J. Clark 			break;
1147252c4bf1SDerek J. Clark 		case GEN2_USAGE_PAGE:
1148252c4bf1SDerek J. Clark 			data = (u8[3]) { effect, 0x00, 0x02 };
1149252c4bf1SDerek J. Clark 			ret = oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, 3);
1150252c4bf1SDerek J. Clark 			break;
115184910c45SDerek J. Clark 		default:
115284910c45SDerek J. Clark 			ret = -ENODEV;
115384910c45SDerek J. Clark 		}
115484910c45SDerek J. Clark 		break;
115584910c45SDerek J. Clark 	case OXP_EFFECT_MONO_LIST:
115684910c45SDerek J. Clark 		ret = oxp_rgb_color_set();
115784910c45SDerek J. Clark 		break;
115884910c45SDerek J. Clark 	default:
115984910c45SDerek J. Clark 		return -EINVAL;
116084910c45SDerek J. Clark 	}
116184910c45SDerek J. Clark 
116284910c45SDerek J. Clark 	if (ret)
116384910c45SDerek J. Clark 		return ret;
116484910c45SDerek J. Clark 
116584910c45SDerek J. Clark 	drvdata.rgb_effect = effect;
116684910c45SDerek J. Clark 
116784910c45SDerek J. Clark 	return 0;
116884910c45SDerek J. Clark }
116984910c45SDerek J. Clark 
117084910c45SDerek J. Clark static ssize_t enabled_store(struct device *dev, struct device_attribute *attr,
117184910c45SDerek J. Clark 			     const char *buf, size_t count)
117284910c45SDerek J. Clark {
117384910c45SDerek J. Clark 	int ret;
117484910c45SDerek J. Clark 	u8 val;
117584910c45SDerek J. Clark 
117684910c45SDerek J. Clark 	ret = sysfs_match_string(oxp_feature_en_text, buf);
117784910c45SDerek J. Clark 	if (ret < 0)
117884910c45SDerek J. Clark 		return ret;
117984910c45SDerek J. Clark 	val = ret;
118084910c45SDerek J. Clark 
118184910c45SDerek J. Clark 	ret = oxp_rgb_status_store(val, drvdata.rgb_speed,
118284910c45SDerek J. Clark 				   drvdata.rgb_brightness);
118384910c45SDerek J. Clark 	if (ret)
118484910c45SDerek J. Clark 		return ret;
118584910c45SDerek J. Clark 
118684910c45SDerek J. Clark 	drvdata.rgb_en = val;
118784910c45SDerek J. Clark 	return count;
118884910c45SDerek J. Clark }
118984910c45SDerek J. Clark 
119084910c45SDerek J. Clark static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
119184910c45SDerek J. Clark 			    char *buf)
119284910c45SDerek J. Clark {
119384910c45SDerek J. Clark 	int ret;
119484910c45SDerek J. Clark 
119584910c45SDerek J. Clark 	ret = oxp_rgb_status_show();
119684910c45SDerek J. Clark 	if (ret)
119784910c45SDerek J. Clark 		return ret;
119884910c45SDerek J. Clark 
119984910c45SDerek J. Clark 	if (drvdata.rgb_en >= ARRAY_SIZE(oxp_feature_en_text))
120084910c45SDerek J. Clark 		return -EINVAL;
120184910c45SDerek J. Clark 
120284910c45SDerek J. Clark 	return sysfs_emit(buf, "%s\n", oxp_feature_en_text[drvdata.rgb_en]);
120384910c45SDerek J. Clark }
120484910c45SDerek J. Clark static DEVICE_ATTR_RW(enabled);
120584910c45SDerek J. Clark 
120684910c45SDerek J. Clark static ssize_t enabled_index_show(struct device *dev,
120784910c45SDerek J. Clark 				  struct device_attribute *attr, char *buf)
120884910c45SDerek J. Clark {
120984910c45SDerek J. Clark 	size_t count = 0;
121084910c45SDerek J. Clark 	unsigned int i;
121184910c45SDerek J. Clark 
121284910c45SDerek J. Clark 	for (i = 0; i < ARRAY_SIZE(oxp_feature_en_text); i++)
121384910c45SDerek J. Clark 		count += sysfs_emit_at(buf, count, "%s ", oxp_feature_en_text[i]);
121484910c45SDerek J. Clark 
121584910c45SDerek J. Clark 	if (count)
121684910c45SDerek J. Clark 		buf[count - 1] = '\n';
121784910c45SDerek J. Clark 
121884910c45SDerek J. Clark 	return count;
121984910c45SDerek J. Clark }
122084910c45SDerek J. Clark static DEVICE_ATTR_RO(enabled_index);
122184910c45SDerek J. Clark 
122284910c45SDerek J. Clark static ssize_t effect_store(struct device *dev, struct device_attribute *attr,
122384910c45SDerek J. Clark 			    const char *buf, size_t count)
122484910c45SDerek J. Clark {
122584910c45SDerek J. Clark 	int ret;
122684910c45SDerek J. Clark 	u8 val;
122784910c45SDerek J. Clark 
122884910c45SDerek J. Clark 	ret = sysfs_match_string(oxp_rgb_effect_text, buf);
122984910c45SDerek J. Clark 	if (ret < 0)
123084910c45SDerek J. Clark 		return ret;
123184910c45SDerek J. Clark 
123284910c45SDerek J. Clark 	val = ret;
123384910c45SDerek J. Clark 
123484910c45SDerek J. Clark 	ret = oxp_rgb_status_store(drvdata.rgb_en, drvdata.rgb_speed,
123584910c45SDerek J. Clark 				   drvdata.rgb_brightness);
123684910c45SDerek J. Clark 	if (ret)
123784910c45SDerek J. Clark 		return ret;
123884910c45SDerek J. Clark 
123984910c45SDerek J. Clark 	ret = oxp_rgb_effect_set(val);
124084910c45SDerek J. Clark 	if (ret)
124184910c45SDerek J. Clark 		return ret;
124284910c45SDerek J. Clark 
124384910c45SDerek J. Clark 	return count;
124484910c45SDerek J. Clark }
124584910c45SDerek J. Clark 
124684910c45SDerek J. Clark static ssize_t effect_show(struct device *dev, struct device_attribute *attr,
124784910c45SDerek J. Clark 			   char *buf)
124884910c45SDerek J. Clark {
124984910c45SDerek J. Clark 	int ret;
125084910c45SDerek J. Clark 
125184910c45SDerek J. Clark 	ret = oxp_rgb_status_show();
125284910c45SDerek J. Clark 	if (ret)
125384910c45SDerek J. Clark 		return ret;
125484910c45SDerek J. Clark 
125584910c45SDerek J. Clark 	if (drvdata.rgb_effect >= ARRAY_SIZE(oxp_rgb_effect_text))
125684910c45SDerek J. Clark 		return -EINVAL;
125784910c45SDerek J. Clark 
125884910c45SDerek J. Clark 	return sysfs_emit(buf, "%s\n", oxp_rgb_effect_text[drvdata.rgb_effect]);
125984910c45SDerek J. Clark }
126084910c45SDerek J. Clark 
126184910c45SDerek J. Clark static DEVICE_ATTR_RW(effect);
126284910c45SDerek J. Clark 
126384910c45SDerek J. Clark static ssize_t effect_index_show(struct device *dev,
126484910c45SDerek J. Clark 				 struct device_attribute *attr, char *buf)
126584910c45SDerek J. Clark {
126684910c45SDerek J. Clark 	size_t count = 0;
126784910c45SDerek J. Clark 	unsigned int i;
126884910c45SDerek J. Clark 
126984910c45SDerek J. Clark 	for (i = 1; i < ARRAY_SIZE(oxp_rgb_effect_text); i++)
127084910c45SDerek J. Clark 		count += sysfs_emit_at(buf, count, "%s ", oxp_rgb_effect_text[i]);
127184910c45SDerek J. Clark 
127284910c45SDerek J. Clark 	if (count)
127384910c45SDerek J. Clark 		buf[count - 1] = '\n';
127484910c45SDerek J. Clark 
127584910c45SDerek J. Clark 	return count;
127684910c45SDerek J. Clark }
127784910c45SDerek J. Clark static DEVICE_ATTR_RO(effect_index);
127884910c45SDerek J. Clark 
127984910c45SDerek J. Clark static ssize_t speed_store(struct device *dev, struct device_attribute *attr,
128084910c45SDerek J. Clark 			   const char *buf, size_t count)
128184910c45SDerek J. Clark {
128284910c45SDerek J. Clark 	int ret;
128384910c45SDerek J. Clark 	u8 val;
128484910c45SDerek J. Clark 
128584910c45SDerek J. Clark 	ret = kstrtou8(buf, 10, &val);
128684910c45SDerek J. Clark 	if (ret)
128784910c45SDerek J. Clark 		return ret;
128884910c45SDerek J. Clark 
128984910c45SDerek J. Clark 	if (val > 9)
129084910c45SDerek J. Clark 		return -EINVAL;
129184910c45SDerek J. Clark 
129284910c45SDerek J. Clark 	ret = oxp_rgb_status_store(drvdata.rgb_en, val, drvdata.rgb_brightness);
129384910c45SDerek J. Clark 	if (ret)
129484910c45SDerek J. Clark 		return ret;
129584910c45SDerek J. Clark 
129684910c45SDerek J. Clark 	drvdata.rgb_speed = val;
129784910c45SDerek J. Clark 	return count;
129884910c45SDerek J. Clark }
129984910c45SDerek J. Clark 
130084910c45SDerek J. Clark static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
130184910c45SDerek J. Clark 			  char *buf)
130284910c45SDerek J. Clark {
130384910c45SDerek J. Clark 	int ret;
130484910c45SDerek J. Clark 
130584910c45SDerek J. Clark 	ret = oxp_rgb_status_show();
130684910c45SDerek J. Clark 	if (ret)
130784910c45SDerek J. Clark 		return ret;
130884910c45SDerek J. Clark 
130984910c45SDerek J. Clark 	if (drvdata.rgb_speed > 9)
131084910c45SDerek J. Clark 		return -EINVAL;
131184910c45SDerek J. Clark 
131284910c45SDerek J. Clark 	return sysfs_emit(buf, "%hhu\n", drvdata.rgb_speed);
131384910c45SDerek J. Clark }
131484910c45SDerek J. Clark static DEVICE_ATTR_RW(speed);
131584910c45SDerek J. Clark 
131684910c45SDerek J. Clark static ssize_t speed_range_show(struct device *dev,
131784910c45SDerek J. Clark 				struct device_attribute *attr, char *buf)
131884910c45SDerek J. Clark {
131984910c45SDerek J. Clark 	return sysfs_emit(buf, "0-9\n");
132084910c45SDerek J. Clark }
132184910c45SDerek J. Clark static DEVICE_ATTR_RO(speed_range);
132284910c45SDerek J. Clark 
132384910c45SDerek J. Clark static void oxp_rgb_queue_fn(struct work_struct *work)
132484910c45SDerek J. Clark {
132584910c45SDerek J. Clark 	unsigned int max_brightness = drvdata.led_mc->led_cdev.max_brightness;
132684910c45SDerek J. Clark 	unsigned int brightness = drvdata.led_mc->led_cdev.brightness;
132784910c45SDerek J. Clark 	u8 val = 4 * brightness / max_brightness;
132884910c45SDerek J. Clark 	int ret;
132984910c45SDerek J. Clark 
133084910c45SDerek J. Clark 	if (drvdata.rgb_brightness != val) {
133184910c45SDerek J. Clark 		ret = oxp_rgb_status_store(drvdata.rgb_en, drvdata.rgb_speed, val);
133284910c45SDerek J. Clark 		if (ret)
133384910c45SDerek J. Clark 			dev_err(drvdata.led_mc->led_cdev.dev,
133484910c45SDerek J. Clark 				"Error: Failed to write RGB Status: %i\n", ret);
133584910c45SDerek J. Clark 
133684910c45SDerek J. Clark 		drvdata.rgb_brightness = val;
133784910c45SDerek J. Clark 	}
133884910c45SDerek J. Clark 
133984910c45SDerek J. Clark 	if (drvdata.rgb_effect != OXP_EFFECT_MONO_LIST)
134084910c45SDerek J. Clark 		return;
134184910c45SDerek J. Clark 
134284910c45SDerek J. Clark 	ret = oxp_rgb_effect_set(drvdata.rgb_effect);
134384910c45SDerek J. Clark 	if (ret)
134484910c45SDerek J. Clark 		dev_err(drvdata.led_mc->led_cdev.dev, "Error: Failed to write RGB color: %i\n",
134584910c45SDerek J. Clark 			ret);
134684910c45SDerek J. Clark }
134784910c45SDerek J. Clark 
134884910c45SDerek J. Clark static void oxp_rgb_brightness_set(struct led_classdev *led_cdev,
134984910c45SDerek J. Clark 				   enum led_brightness brightness)
135084910c45SDerek J. Clark {
135184910c45SDerek J. Clark 	led_cdev->brightness = brightness;
1352*8bb7c0fdSMarco Crivellari 	mod_delayed_work(system_dfl_wq, &drvdata.oxp_rgb_queue, msecs_to_jiffies(50));
135384910c45SDerek J. Clark }
135484910c45SDerek J. Clark 
135584910c45SDerek J. Clark static struct attribute *oxp_rgb_attrs[] = {
135684910c45SDerek J. Clark 	&dev_attr_effect.attr,
135784910c45SDerek J. Clark 	&dev_attr_effect_index.attr,
135884910c45SDerek J. Clark 	&dev_attr_enabled.attr,
135984910c45SDerek J. Clark 	&dev_attr_enabled_index.attr,
136084910c45SDerek J. Clark 	&dev_attr_speed.attr,
136184910c45SDerek J. Clark 	&dev_attr_speed_range.attr,
136284910c45SDerek J. Clark 	NULL,
136384910c45SDerek J. Clark };
136484910c45SDerek J. Clark 
136584910c45SDerek J. Clark static const struct attribute_group oxp_rgb_attr_group = {
136684910c45SDerek J. Clark 	.attrs = oxp_rgb_attrs,
136784910c45SDerek J. Clark };
136884910c45SDerek J. Clark 
136984910c45SDerek J. Clark static struct mc_subled oxp_rgb_subled_info[] = {
137084910c45SDerek J. Clark 	{
137184910c45SDerek J. Clark 		.color_index = LED_COLOR_ID_RED,
137284910c45SDerek J. Clark 		.intensity = 0x24,
137384910c45SDerek J. Clark 		.channel = 0x1,
137484910c45SDerek J. Clark 	},
137584910c45SDerek J. Clark 	{
137684910c45SDerek J. Clark 		.color_index = LED_COLOR_ID_GREEN,
137784910c45SDerek J. Clark 		.intensity = 0x22,
137884910c45SDerek J. Clark 		.channel = 0x2,
137984910c45SDerek J. Clark 	},
138084910c45SDerek J. Clark 	{
138184910c45SDerek J. Clark 		.color_index = LED_COLOR_ID_BLUE,
138284910c45SDerek J. Clark 		.intensity = 0x99,
138384910c45SDerek J. Clark 		.channel = 0x3,
138484910c45SDerek J. Clark 	},
138584910c45SDerek J. Clark };
138684910c45SDerek J. Clark 
138784910c45SDerek J. Clark static struct led_classdev_mc oxp_cdev_rgb = {
138884910c45SDerek J. Clark 	.led_cdev = {
138984910c45SDerek J. Clark 		.name = "oxp:rgb:joystick_rings",
139084910c45SDerek J. Clark 		.color = LED_COLOR_ID_RGB,
139184910c45SDerek J. Clark 		.brightness = 0x64,
139284910c45SDerek J. Clark 		.max_brightness = 0x64,
139384910c45SDerek J. Clark 		.brightness_set = oxp_rgb_brightness_set,
139484910c45SDerek J. Clark 	},
139584910c45SDerek J. Clark 	.num_colors = ARRAY_SIZE(oxp_rgb_subled_info),
139684910c45SDerek J. Clark 	.subled_info = oxp_rgb_subled_info,
139784910c45SDerek J. Clark };
139884910c45SDerek J. Clark 
1399252c4bf1SDerek J. Clark struct quirk_entry {
1400252c4bf1SDerek J. Clark 	bool hybrid_mcu;
1401252c4bf1SDerek J. Clark };
1402252c4bf1SDerek J. Clark 
1403252c4bf1SDerek J. Clark static struct quirk_entry quirk_hybrid_mcu = {
1404252c4bf1SDerek J. Clark 	.hybrid_mcu = true,
1405252c4bf1SDerek J. Clark };
1406252c4bf1SDerek J. Clark 
1407252c4bf1SDerek J. Clark static const struct dmi_system_id oxp_hybrid_mcu_list[] = {
1408252c4bf1SDerek J. Clark 	{
1409252c4bf1SDerek J. Clark 		.ident = "OneXPlayer Apex",
1410252c4bf1SDerek J. Clark 		.matches = {
1411252c4bf1SDerek J. Clark 			DMI_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"),
1412252c4bf1SDerek J. Clark 			DMI_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER APEX"),
1413252c4bf1SDerek J. Clark 		},
1414252c4bf1SDerek J. Clark 		.driver_data = &quirk_hybrid_mcu,
1415252c4bf1SDerek J. Clark 	},
1416252c4bf1SDerek J. Clark 	{
1417252c4bf1SDerek J. Clark 		.ident = "OneXPlayer G1 AMD",
1418252c4bf1SDerek J. Clark 		.matches = {
1419252c4bf1SDerek J. Clark 			DMI_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"),
1420252c4bf1SDerek J. Clark 			DMI_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER G1 A"),
1421252c4bf1SDerek J. Clark 		},
1422252c4bf1SDerek J. Clark 		.driver_data = &quirk_hybrid_mcu,
1423252c4bf1SDerek J. Clark 	},
1424252c4bf1SDerek J. Clark 	{
1425252c4bf1SDerek J. Clark 		.ident = "OneXPlayer G1 Intel",
1426252c4bf1SDerek J. Clark 		.matches = {
1427252c4bf1SDerek J. Clark 			DMI_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"),
1428252c4bf1SDerek J. Clark 			DMI_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER G1 i"),
1429252c4bf1SDerek J. Clark 		},
1430252c4bf1SDerek J. Clark 		.driver_data = &quirk_hybrid_mcu,
1431252c4bf1SDerek J. Clark 	},
1432252c4bf1SDerek J. Clark 	{},
1433252c4bf1SDerek J. Clark };
1434252c4bf1SDerek J. Clark 
1435252c4bf1SDerek J. Clark static bool oxp_hybrid_mcu_device(void)
1436252c4bf1SDerek J. Clark {
1437252c4bf1SDerek J. Clark 	const struct dmi_system_id *dmi_id;
1438252c4bf1SDerek J. Clark 	struct quirk_entry *quirks;
1439252c4bf1SDerek J. Clark 
1440252c4bf1SDerek J. Clark 	dmi_id = dmi_first_match(oxp_hybrid_mcu_list);
1441252c4bf1SDerek J. Clark 	if (!dmi_id)
1442252c4bf1SDerek J. Clark 		return false;
1443252c4bf1SDerek J. Clark 
1444252c4bf1SDerek J. Clark 	quirks = dmi_id->driver_data;
1445252c4bf1SDerek J. Clark 
1446252c4bf1SDerek J. Clark 	return quirks->hybrid_mcu;
1447252c4bf1SDerek J. Clark }
1448252c4bf1SDerek J. Clark 
144984910c45SDerek J. Clark static int oxp_cfg_probe(struct hid_device *hdev, u16 up)
145084910c45SDerek J. Clark {
1451e4c850a6SDerek J. Clark 	struct oxp_bmap_page_1 *bmap_1;
1452e4c850a6SDerek J. Clark 	struct oxp_bmap_page_2 *bmap_2;
145384910c45SDerek J. Clark 	int ret;
145484910c45SDerek J. Clark 
145584910c45SDerek J. Clark 	hid_set_drvdata(hdev, &drvdata);
145684910c45SDerek J. Clark 	mutex_init(&drvdata.cfg_mutex);
145784910c45SDerek J. Clark 	drvdata.hdev = hdev;
1458252c4bf1SDerek J. Clark 
1459252c4bf1SDerek J. Clark 	if (up == GEN2_USAGE_PAGE && oxp_hybrid_mcu_device())
1460252c4bf1SDerek J. Clark 		goto skip_rgb;
1461252c4bf1SDerek J. Clark 
146284910c45SDerek J. Clark 	drvdata.led_mc = &oxp_cdev_rgb;
146384910c45SDerek J. Clark 
146484910c45SDerek J. Clark 	INIT_DELAYED_WORK(&drvdata.oxp_rgb_queue, oxp_rgb_queue_fn);
146584910c45SDerek J. Clark 	ret = devm_led_classdev_multicolor_register(&hdev->dev, &oxp_cdev_rgb);
146684910c45SDerek J. Clark 	if (ret)
146784910c45SDerek J. Clark 		return dev_err_probe(&hdev->dev, ret,
146884910c45SDerek J. Clark 				     "Failed to create RGB device\n");
146984910c45SDerek J. Clark 
147084910c45SDerek J. Clark 	ret = devm_device_add_group(drvdata.led_mc->led_cdev.dev,
147184910c45SDerek J. Clark 				    &oxp_rgb_attr_group);
147284910c45SDerek J. Clark 	if (ret)
147384910c45SDerek J. Clark 		return dev_err_probe(drvdata.led_mc->led_cdev.dev, ret,
147484910c45SDerek J. Clark 				     "Failed to create RGB configuration attributes\n");
147584910c45SDerek J. Clark 
147684910c45SDerek J. Clark 	ret = oxp_rgb_status_show();
147784910c45SDerek J. Clark 	if (ret)
147884910c45SDerek J. Clark 		dev_warn(drvdata.led_mc->led_cdev.dev,
147984910c45SDerek J. Clark 			 "Failed to query RGB initial state: %i\n", ret);
148084910c45SDerek J. Clark 
14812f424f28SDerek J. Clark 	/* Below features are only implemented in gen 2 */
14822f424f28SDerek J. Clark 	if (up != GEN2_USAGE_PAGE)
14832f424f28SDerek J. Clark 		return 0;
14842f424f28SDerek J. Clark 
1485252c4bf1SDerek J. Clark skip_rgb:
1486e4c850a6SDerek J. Clark 	bmap_1 = devm_kzalloc(&hdev->dev, sizeof(struct oxp_bmap_page_1), GFP_KERNEL);
1487e4c850a6SDerek J. Clark 	if (!bmap_1)
1488e4c850a6SDerek J. Clark 		return dev_err_probe(&hdev->dev, -ENOMEM,
1489e4c850a6SDerek J. Clark 				     "Unable to allocate button map page 1\n");
1490e4c850a6SDerek J. Clark 
1491e4c850a6SDerek J. Clark 	bmap_2 = devm_kzalloc(&hdev->dev, sizeof(struct oxp_bmap_page_2), GFP_KERNEL);
1492e4c850a6SDerek J. Clark 	if (!bmap_2)
1493e4c850a6SDerek J. Clark 		return dev_err_probe(&hdev->dev, -ENOMEM,
1494e4c850a6SDerek J. Clark 				     "Unable to allocate button map page 2\n");
1495e4c850a6SDerek J. Clark 
1496e4c850a6SDerek J. Clark 	drvdata.bmap_1 = bmap_1;
1497e4c850a6SDerek J. Clark 	drvdata.bmap_2 = bmap_2;
1498e4c850a6SDerek J. Clark 	oxp_reset_buttons();
1499e4c850a6SDerek J. Clark 	INIT_DELAYED_WORK(&drvdata.oxp_btn_queue, oxp_btn_queue_fn);
1500e4c850a6SDerek J. Clark 
15012f424f28SDerek J. Clark 	drvdata.gamepad_mode = OXP_GP_MODE_XINPUT;
150299bde1dfSDerek J. Clark 	drvdata.rumble_intensity = 5;
15032f424f28SDerek J. Clark 
15042f424f28SDerek J. Clark 	INIT_DELAYED_WORK(&drvdata.oxp_mcu_init, oxp_mcu_init_fn);
1505*8bb7c0fdSMarco Crivellari 	mod_delayed_work(system_dfl_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50));
15062f424f28SDerek J. Clark 
15072f424f28SDerek J. Clark 	ret = devm_device_add_group(&hdev->dev, &oxp_cfg_attrs_group);
15082f424f28SDerek J. Clark 	if (ret)
15092f424f28SDerek J. Clark 		return dev_err_probe(&hdev->dev, ret,
15102f424f28SDerek J. Clark 				     "Failed to attach configuration attributes\n");
15112f424f28SDerek J. Clark 
151284910c45SDerek J. Clark 	return 0;
151384910c45SDerek J. Clark }
151484910c45SDerek J. Clark 
151584910c45SDerek J. Clark static int oxp_hid_probe(struct hid_device *hdev,
151684910c45SDerek J. Clark 			 const struct hid_device_id *id)
151784910c45SDerek J. Clark {
151884910c45SDerek J. Clark 	int ret;
151984910c45SDerek J. Clark 	u16 up;
152084910c45SDerek J. Clark 
152184910c45SDerek J. Clark 	ret = hid_parse(hdev);
152284910c45SDerek J. Clark 	if (ret)
152384910c45SDerek J. Clark 		return dev_err_probe(&hdev->dev, ret, "Failed to parse HID device\n");
152484910c45SDerek J. Clark 
152584910c45SDerek J. Clark 	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
152684910c45SDerek J. Clark 	if (ret)
152784910c45SDerek J. Clark 		return dev_err_probe(&hdev->dev, ret, "Failed to start HID device\n");
152884910c45SDerek J. Clark 
152984910c45SDerek J. Clark 	ret = hid_hw_open(hdev);
153084910c45SDerek J. Clark 	if (ret) {
153184910c45SDerek J. Clark 		hid_hw_stop(hdev);
153284910c45SDerek J. Clark 		return dev_err_probe(&hdev->dev, ret, "Failed to open HID device\n");
153384910c45SDerek J. Clark 	}
153484910c45SDerek J. Clark 
153584910c45SDerek J. Clark 	up = get_usage_page(hdev);
153684910c45SDerek J. Clark 	dev_dbg(&hdev->dev, "Got usage page %04x\n", up);
153784910c45SDerek J. Clark 
153884910c45SDerek J. Clark 	switch (up) {
153984910c45SDerek J. Clark 	case GEN1_USAGE_PAGE:
1540252c4bf1SDerek J. Clark 	case GEN2_USAGE_PAGE:
154184910c45SDerek J. Clark 		ret = oxp_cfg_probe(hdev, up);
154284910c45SDerek J. Clark 		if (ret) {
154384910c45SDerek J. Clark 			hid_hw_close(hdev);
154484910c45SDerek J. Clark 			hid_hw_stop(hdev);
154584910c45SDerek J. Clark 		}
154684910c45SDerek J. Clark 
154784910c45SDerek J. Clark 		return ret;
154884910c45SDerek J. Clark 	default:
154984910c45SDerek J. Clark 		return 0;
155084910c45SDerek J. Clark 	}
155184910c45SDerek J. Clark }
155284910c45SDerek J. Clark 
155384910c45SDerek J. Clark static void oxp_hid_remove(struct hid_device *hdev)
155484910c45SDerek J. Clark {
155584910c45SDerek J. Clark 	cancel_delayed_work(&drvdata.oxp_rgb_queue);
1556e4c850a6SDerek J. Clark 	cancel_delayed_work(&drvdata.oxp_btn_queue);
15572f424f28SDerek J. Clark 	cancel_delayed_work(&drvdata.oxp_mcu_init);
155884910c45SDerek J. Clark 	hid_hw_close(hdev);
155984910c45SDerek J. Clark 	hid_hw_stop(hdev);
156084910c45SDerek J. Clark }
156184910c45SDerek J. Clark 
156284910c45SDerek J. Clark static const struct hid_device_id oxp_devices[] = {
156384910c45SDerek J. Clark 	{ HID_USB_DEVICE(USB_VENDOR_ID_CRSC, USB_DEVICE_ID_ONEXPLAYER_GEN1) },
1564252c4bf1SDerek J. Clark 	{ HID_USB_DEVICE(USB_VENDOR_ID_WCH, USB_DEVICE_ID_ONEXPLAYER_GEN2) },
156584910c45SDerek J. Clark 	{}
156684910c45SDerek J. Clark };
156784910c45SDerek J. Clark 
156884910c45SDerek J. Clark MODULE_DEVICE_TABLE(hid, oxp_devices);
156984910c45SDerek J. Clark static struct hid_driver hid_oxp = {
157084910c45SDerek J. Clark 	.name = "hid-oxp",
157184910c45SDerek J. Clark 	.id_table = oxp_devices,
157284910c45SDerek J. Clark 	.probe = oxp_hid_probe,
157384910c45SDerek J. Clark 	.remove = oxp_hid_remove,
157484910c45SDerek J. Clark 	.raw_event = oxp_hid_raw_event,
157584910c45SDerek J. Clark };
157684910c45SDerek J. Clark module_hid_driver(hid_oxp);
157784910c45SDerek J. Clark 
157884910c45SDerek J. Clark MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
157984910c45SDerek J. Clark MODULE_DESCRIPTION("Driver for OneXPlayer HID Interfaces");
158084910c45SDerek J. Clark MODULE_LICENSE("GPL");
1581