xref: /openbmc/linux/drivers/hid/hid-steam.c (revision 8ebc80a25f9d9bf7a8e368b266d5b740c485c362)
1c164d6abSRodrigo Rivas Costa // SPDX-License-Identifier: GPL-2.0+
2c164d6abSRodrigo Rivas Costa /*
3c164d6abSRodrigo Rivas Costa  * HID driver for Valve Steam Controller
4c164d6abSRodrigo Rivas Costa  *
5c164d6abSRodrigo Rivas Costa  * Copyright (c) 2018 Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>
69ba9498bSVicki Pfau  * Copyright (c) 2022 Valve Software
7c164d6abSRodrigo Rivas Costa  *
8c164d6abSRodrigo Rivas Costa  * Supports both the wired and wireless interfaces.
9c164d6abSRodrigo Rivas Costa  *
10c164d6abSRodrigo Rivas Costa  * This controller has a builtin emulation of mouse and keyboard: the right pad
11c164d6abSRodrigo Rivas Costa  * can be used as a mouse, the shoulder buttons are mouse buttons, A and B
12c164d6abSRodrigo Rivas Costa  * buttons are ENTER and ESCAPE, and so on. This is implemented as additional
13c164d6abSRodrigo Rivas Costa  * HID interfaces.
14c164d6abSRodrigo Rivas Costa  *
15c164d6abSRodrigo Rivas Costa  * This is known as the "lizard mode", because apparently lizards like to use
16c164d6abSRodrigo Rivas Costa  * the computer from the coach, without a proper mouse and keyboard.
17c164d6abSRodrigo Rivas Costa  *
18c164d6abSRodrigo Rivas Costa  * This driver will disable the lizard mode when the input device is opened
19c164d6abSRodrigo Rivas Costa  * and re-enable it when the input device is closed, so as not to break user
20c164d6abSRodrigo Rivas Costa  * mode behaviour. The lizard_mode parameter can be used to change that.
21c164d6abSRodrigo Rivas Costa  *
22c164d6abSRodrigo Rivas Costa  * There are a few user space applications (notably Steam Client) that use
23c164d6abSRodrigo Rivas Costa  * the hidraw interface directly to create input devices (XTest, uinput...).
24c164d6abSRodrigo Rivas Costa  * In order to avoid breaking them this driver creates a layered hidraw device,
25c164d6abSRodrigo Rivas Costa  * so it can detect when the client is running and then:
26c164d6abSRodrigo Rivas Costa  *  - it will not send any command to the controller.
27385a4886SRodrigo Rivas Costa  *  - this input device will be removed, to avoid double input of the same
28c164d6abSRodrigo Rivas Costa  *    user action.
29385a4886SRodrigo Rivas Costa  * When the client is closed, this input device will be created again.
30c164d6abSRodrigo Rivas Costa  *
31c164d6abSRodrigo Rivas Costa  * For additional functions, such as changing the right-pad margin or switching
32c164d6abSRodrigo Rivas Costa  * the led, you can use the user-space tool at:
33c164d6abSRodrigo Rivas Costa  *
34c164d6abSRodrigo Rivas Costa  *   https://github.com/rodrigorc/steamctrl
35c164d6abSRodrigo Rivas Costa  */
36c164d6abSRodrigo Rivas Costa 
37c164d6abSRodrigo Rivas Costa #include <linux/device.h>
38c164d6abSRodrigo Rivas Costa #include <linux/input.h>
39c164d6abSRodrigo Rivas Costa #include <linux/hid.h>
40c164d6abSRodrigo Rivas Costa #include <linux/module.h>
41c164d6abSRodrigo Rivas Costa #include <linux/workqueue.h>
42c164d6abSRodrigo Rivas Costa #include <linux/mutex.h>
43c164d6abSRodrigo Rivas Costa #include <linux/rcupdate.h>
44c164d6abSRodrigo Rivas Costa #include <linux/delay.h>
45f8271979SRodrigo Rivas Costa #include <linux/power_supply.h>
46c164d6abSRodrigo Rivas Costa #include "hid-ids.h"
47c164d6abSRodrigo Rivas Costa 
48c164d6abSRodrigo Rivas Costa MODULE_LICENSE("GPL");
49c164d6abSRodrigo Rivas Costa MODULE_AUTHOR("Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>");
50c164d6abSRodrigo Rivas Costa 
51c164d6abSRodrigo Rivas Costa static bool lizard_mode = true;
52c164d6abSRodrigo Rivas Costa 
53c164d6abSRodrigo Rivas Costa static DEFINE_MUTEX(steam_devices_lock);
54c164d6abSRodrigo Rivas Costa static LIST_HEAD(steam_devices);
55c164d6abSRodrigo Rivas Costa 
56c164d6abSRodrigo Rivas Costa #define STEAM_QUIRK_WIRELESS		BIT(0)
579ba9498bSVicki Pfau #define STEAM_QUIRK_DECK		BIT(1)
58c164d6abSRodrigo Rivas Costa 
59c164d6abSRodrigo Rivas Costa /* Touch pads are 40 mm in diameter and 65535 units */
60c164d6abSRodrigo Rivas Costa #define STEAM_PAD_RESOLUTION 1638
61c164d6abSRodrigo Rivas Costa /* Trigger runs are about 5 mm and 256 units */
62c164d6abSRodrigo Rivas Costa #define STEAM_TRIGGER_RESOLUTION 51
63c164d6abSRodrigo Rivas Costa /* Joystick runs are about 5 mm and 256 units */
64c164d6abSRodrigo Rivas Costa #define STEAM_JOYSTICK_RESOLUTION 51
659ba9498bSVicki Pfau /* Trigger runs are about 6 mm and 32768 units */
669ba9498bSVicki Pfau #define STEAM_DECK_TRIGGER_RESOLUTION 5461
679ba9498bSVicki Pfau /* Joystick runs are about 5 mm and 32768 units */
689ba9498bSVicki Pfau #define STEAM_DECK_JOYSTICK_RESOLUTION 6553
694b996b61SMax Maisel /* Accelerometer has 16 bit resolution and a range of +/- 2g */
704b996b61SMax Maisel #define STEAM_DECK_ACCEL_RES_PER_G 16384
714b996b61SMax Maisel #define STEAM_DECK_ACCEL_RANGE 32768
724b996b61SMax Maisel #define STEAM_DECK_ACCEL_FUZZ 32
734b996b61SMax Maisel /* Gyroscope has 16 bit resolution and a range of +/- 2000 dps */
744b996b61SMax Maisel #define STEAM_DECK_GYRO_RES_PER_DPS 16
754b996b61SMax Maisel #define STEAM_DECK_GYRO_RANGE 32768
764b996b61SMax Maisel #define STEAM_DECK_GYRO_FUZZ 1
77c164d6abSRodrigo Rivas Costa 
78c164d6abSRodrigo Rivas Costa #define STEAM_PAD_FUZZ 256
79c164d6abSRodrigo Rivas Costa 
80c164d6abSRodrigo Rivas Costa /*
81c164d6abSRodrigo Rivas Costa  * Commands that can be sent in a feature report.
82622ee123SVicki Pfau  * Thanks to Valve and SDL for the names.
83c164d6abSRodrigo Rivas Costa  */
84622ee123SVicki Pfau enum {
85622ee123SVicki Pfau 	ID_SET_DIGITAL_MAPPINGS		= 0x80,
86622ee123SVicki Pfau 	ID_CLEAR_DIGITAL_MAPPINGS	= 0x81,
87622ee123SVicki Pfau 	ID_GET_DIGITAL_MAPPINGS		= 0x82,
88622ee123SVicki Pfau 	ID_GET_ATTRIBUTES_VALUES	= 0x83,
89622ee123SVicki Pfau 	ID_GET_ATTRIBUTE_LABEL		= 0x84,
90622ee123SVicki Pfau 	ID_SET_DEFAULT_DIGITAL_MAPPINGS	= 0x85,
91622ee123SVicki Pfau 	ID_FACTORY_RESET		= 0x86,
92622ee123SVicki Pfau 	ID_SET_SETTINGS_VALUES		= 0x87,
93622ee123SVicki Pfau 	ID_CLEAR_SETTINGS_VALUES	= 0x88,
94622ee123SVicki Pfau 	ID_GET_SETTINGS_VALUES		= 0x89,
95622ee123SVicki Pfau 	ID_GET_SETTING_LABEL		= 0x8A,
96622ee123SVicki Pfau 	ID_GET_SETTINGS_MAXS		= 0x8B,
97622ee123SVicki Pfau 	ID_GET_SETTINGS_DEFAULTS	= 0x8C,
98622ee123SVicki Pfau 	ID_SET_CONTROLLER_MODE		= 0x8D,
99622ee123SVicki Pfau 	ID_LOAD_DEFAULT_SETTINGS	= 0x8E,
100622ee123SVicki Pfau 	ID_TRIGGER_HAPTIC_PULSE		= 0x8F,
101622ee123SVicki Pfau 	ID_TURN_OFF_CONTROLLER		= 0x9F,
102c164d6abSRodrigo Rivas Costa 
103622ee123SVicki Pfau 	ID_GET_DEVICE_INFO		= 0xA1,
104c164d6abSRodrigo Rivas Costa 
105622ee123SVicki Pfau 	ID_CALIBRATE_TRACKPADS		= 0xA7,
106622ee123SVicki Pfau 	ID_RESERVED_0			= 0xA8,
107622ee123SVicki Pfau 	ID_SET_SERIAL_NUMBER		= 0xA9,
108622ee123SVicki Pfau 	ID_GET_TRACKPAD_CALIBRATION	= 0xAA,
109622ee123SVicki Pfau 	ID_GET_TRACKPAD_FACTORY_CALIBRATION = 0xAB,
110622ee123SVicki Pfau 	ID_GET_TRACKPAD_RAW_DATA	= 0xAC,
111622ee123SVicki Pfau 	ID_ENABLE_PAIRING		= 0xAD,
112622ee123SVicki Pfau 	ID_GET_STRING_ATTRIBUTE		= 0xAE,
113622ee123SVicki Pfau 	ID_RADIO_ERASE_RECORDS		= 0xAF,
114622ee123SVicki Pfau 	ID_RADIO_WRITE_RECORD		= 0xB0,
115622ee123SVicki Pfau 	ID_SET_DONGLE_SETTING		= 0xB1,
116622ee123SVicki Pfau 	ID_DONGLE_DISCONNECT_DEVICE	= 0xB2,
117622ee123SVicki Pfau 	ID_DONGLE_COMMIT_DEVICE		= 0xB3,
118622ee123SVicki Pfau 	ID_DONGLE_GET_WIRELESS_STATE	= 0xB4,
119622ee123SVicki Pfau 	ID_CALIBRATE_GYRO		= 0xB5,
120622ee123SVicki Pfau 	ID_PLAY_AUDIO			= 0xB6,
121622ee123SVicki Pfau 	ID_AUDIO_UPDATE_START		= 0xB7,
122622ee123SVicki Pfau 	ID_AUDIO_UPDATE_DATA		= 0xB8,
123622ee123SVicki Pfau 	ID_AUDIO_UPDATE_COMPLETE	= 0xB9,
124622ee123SVicki Pfau 	ID_GET_CHIPID			= 0xBA,
125622ee123SVicki Pfau 
126622ee123SVicki Pfau 	ID_CALIBRATE_JOYSTICK		= 0xBF,
127622ee123SVicki Pfau 	ID_CALIBRATE_ANALOG_TRIGGERS	= 0xC0,
128622ee123SVicki Pfau 	ID_SET_AUDIO_MAPPING		= 0xC1,
129622ee123SVicki Pfau 	ID_CHECK_GYRO_FW_LOAD		= 0xC2,
130622ee123SVicki Pfau 	ID_CALIBRATE_ANALOG		= 0xC3,
131622ee123SVicki Pfau 	ID_DONGLE_GET_CONNECTED_SLOTS	= 0xC4,
132622ee123SVicki Pfau 
133622ee123SVicki Pfau 	ID_RESET_IMU			= 0xCE,
134622ee123SVicki Pfau 
135622ee123SVicki Pfau 	ID_TRIGGER_HAPTIC_CMD		= 0xEA,
136622ee123SVicki Pfau 	ID_TRIGGER_RUMBLE_CMD		= 0xEB,
137622ee123SVicki Pfau };
138622ee123SVicki Pfau 
139622ee123SVicki Pfau /* Settings IDs */
140622ee123SVicki Pfau enum {
141622ee123SVicki Pfau 	/* 0 */
142622ee123SVicki Pfau 	SETTING_MOUSE_SENSITIVITY,
143622ee123SVicki Pfau 	SETTING_MOUSE_ACCELERATION,
144622ee123SVicki Pfau 	SETTING_TRACKBALL_ROTATION_ANGLE,
145622ee123SVicki Pfau 	SETTING_HAPTIC_INTENSITY_UNUSED,
146622ee123SVicki Pfau 	SETTING_LEFT_GAMEPAD_STICK_ENABLED,
147622ee123SVicki Pfau 	SETTING_RIGHT_GAMEPAD_STICK_ENABLED,
148622ee123SVicki Pfau 	SETTING_USB_DEBUG_MODE,
149622ee123SVicki Pfau 	SETTING_LEFT_TRACKPAD_MODE,
150622ee123SVicki Pfau 	SETTING_RIGHT_TRACKPAD_MODE,
151622ee123SVicki Pfau 	SETTING_MOUSE_POINTER_ENABLED,
152622ee123SVicki Pfau 
153622ee123SVicki Pfau 	/* 10 */
154622ee123SVicki Pfau 	SETTING_DPAD_DEADZONE,
155622ee123SVicki Pfau 	SETTING_MINIMUM_MOMENTUM_VEL,
156622ee123SVicki Pfau 	SETTING_MOMENTUM_DECAY_AMMOUNT,
157622ee123SVicki Pfau 	SETTING_TRACKPAD_RELATIVE_MODE_TICKS_PER_PIXEL,
158622ee123SVicki Pfau 	SETTING_HAPTIC_INCREMENT,
159622ee123SVicki Pfau 	SETTING_DPAD_ANGLE_SIN,
160622ee123SVicki Pfau 	SETTING_DPAD_ANGLE_COS,
161622ee123SVicki Pfau 	SETTING_MOMENTUM_VERTICAL_DIVISOR,
162622ee123SVicki Pfau 	SETTING_MOMENTUM_MAXIMUM_VELOCITY,
163622ee123SVicki Pfau 	SETTING_TRACKPAD_Z_ON,
164622ee123SVicki Pfau 
165622ee123SVicki Pfau 	/* 20 */
166622ee123SVicki Pfau 	SETTING_TRACKPAD_Z_OFF,
167622ee123SVicki Pfau 	SETTING_SENSITIVY_SCALE_AMMOUNT,
168622ee123SVicki Pfau 	SETTING_LEFT_TRACKPAD_SECONDARY_MODE,
169622ee123SVicki Pfau 	SETTING_RIGHT_TRACKPAD_SECONDARY_MODE,
170622ee123SVicki Pfau 	SETTING_SMOOTH_ABSOLUTE_MOUSE,
171622ee123SVicki Pfau 	SETTING_STEAMBUTTON_POWEROFF_TIME,
172622ee123SVicki Pfau 	SETTING_UNUSED_1,
173622ee123SVicki Pfau 	SETTING_TRACKPAD_OUTER_RADIUS,
174622ee123SVicki Pfau 	SETTING_TRACKPAD_Z_ON_LEFT,
175622ee123SVicki Pfau 	SETTING_TRACKPAD_Z_OFF_LEFT,
176622ee123SVicki Pfau 
177622ee123SVicki Pfau 	/* 30 */
178622ee123SVicki Pfau 	SETTING_TRACKPAD_OUTER_SPIN_VEL,
179622ee123SVicki Pfau 	SETTING_TRACKPAD_OUTER_SPIN_RADIUS,
180622ee123SVicki Pfau 	SETTING_TRACKPAD_OUTER_SPIN_HORIZONTAL_ONLY,
181622ee123SVicki Pfau 	SETTING_TRACKPAD_RELATIVE_MODE_DEADZONE,
182622ee123SVicki Pfau 	SETTING_TRACKPAD_RELATIVE_MODE_MAX_VEL,
183622ee123SVicki Pfau 	SETTING_TRACKPAD_RELATIVE_MODE_INVERT_Y,
184622ee123SVicki Pfau 	SETTING_TRACKPAD_DOUBLE_TAP_BEEP_ENABLED,
185622ee123SVicki Pfau 	SETTING_TRACKPAD_DOUBLE_TAP_BEEP_PERIOD,
186622ee123SVicki Pfau 	SETTING_TRACKPAD_DOUBLE_TAP_BEEP_COUNT,
187622ee123SVicki Pfau 	SETTING_TRACKPAD_OUTER_RADIUS_RELEASE_ON_TRANSITION,
188622ee123SVicki Pfau 
189622ee123SVicki Pfau 	/* 40 */
190622ee123SVicki Pfau 	SETTING_RADIAL_MODE_ANGLE,
191622ee123SVicki Pfau 	SETTING_HAPTIC_INTENSITY_MOUSE_MODE,
192622ee123SVicki Pfau 	SETTING_LEFT_DPAD_REQUIRES_CLICK,
193622ee123SVicki Pfau 	SETTING_RIGHT_DPAD_REQUIRES_CLICK,
194622ee123SVicki Pfau 	SETTING_LED_BASELINE_BRIGHTNESS,
195622ee123SVicki Pfau 	SETTING_LED_USER_BRIGHTNESS,
196622ee123SVicki Pfau 	SETTING_ENABLE_RAW_JOYSTICK,
197622ee123SVicki Pfau 	SETTING_ENABLE_FAST_SCAN,
198622ee123SVicki Pfau 	SETTING_IMU_MODE,
199622ee123SVicki Pfau 	SETTING_WIRELESS_PACKET_VERSION,
200622ee123SVicki Pfau 
201622ee123SVicki Pfau 	/* 50 */
202622ee123SVicki Pfau 	SETTING_SLEEP_INACTIVITY_TIMEOUT,
203622ee123SVicki Pfau 	SETTING_TRACKPAD_NOISE_THRESHOLD,
204622ee123SVicki Pfau 	SETTING_LEFT_TRACKPAD_CLICK_PRESSURE,
205622ee123SVicki Pfau 	SETTING_RIGHT_TRACKPAD_CLICK_PRESSURE,
206622ee123SVicki Pfau 	SETTING_LEFT_BUMPER_CLICK_PRESSURE,
207622ee123SVicki Pfau 	SETTING_RIGHT_BUMPER_CLICK_PRESSURE,
208622ee123SVicki Pfau 	SETTING_LEFT_GRIP_CLICK_PRESSURE,
209622ee123SVicki Pfau 	SETTING_RIGHT_GRIP_CLICK_PRESSURE,
210622ee123SVicki Pfau 	SETTING_LEFT_GRIP2_CLICK_PRESSURE,
211622ee123SVicki Pfau 	SETTING_RIGHT_GRIP2_CLICK_PRESSURE,
212622ee123SVicki Pfau 
213622ee123SVicki Pfau 	/* 60 */
214622ee123SVicki Pfau 	SETTING_PRESSURE_MODE,
215622ee123SVicki Pfau 	SETTING_CONTROLLER_TEST_MODE,
216622ee123SVicki Pfau 	SETTING_TRIGGER_MODE,
217622ee123SVicki Pfau 	SETTING_TRACKPAD_Z_THRESHOLD,
218622ee123SVicki Pfau 	SETTING_FRAME_RATE,
219622ee123SVicki Pfau 	SETTING_TRACKPAD_FILT_CTRL,
220622ee123SVicki Pfau 	SETTING_TRACKPAD_CLIP,
221622ee123SVicki Pfau 	SETTING_DEBUG_OUTPUT_SELECT,
222622ee123SVicki Pfau 	SETTING_TRIGGER_THRESHOLD_PERCENT,
223622ee123SVicki Pfau 	SETTING_TRACKPAD_FREQUENCY_HOPPING,
224622ee123SVicki Pfau 
225622ee123SVicki Pfau 	/* 70 */
226622ee123SVicki Pfau 	SETTING_HAPTICS_ENABLED,
227622ee123SVicki Pfau 	SETTING_STEAM_WATCHDOG_ENABLE,
228622ee123SVicki Pfau 	SETTING_TIMP_TOUCH_THRESHOLD_ON,
229622ee123SVicki Pfau 	SETTING_TIMP_TOUCH_THRESHOLD_OFF,
230622ee123SVicki Pfau 	SETTING_FREQ_HOPPING,
231622ee123SVicki Pfau 	SETTING_TEST_CONTROL,
232622ee123SVicki Pfau 	SETTING_HAPTIC_MASTER_GAIN_DB,
233622ee123SVicki Pfau 	SETTING_THUMB_TOUCH_THRESH,
234622ee123SVicki Pfau 	SETTING_DEVICE_POWER_STATUS,
235622ee123SVicki Pfau 	SETTING_HAPTIC_INTENSITY,
236622ee123SVicki Pfau 
237622ee123SVicki Pfau 	/* 80 */
238622ee123SVicki Pfau 	SETTING_STABILIZER_ENABLED,
239622ee123SVicki Pfau 	SETTING_TIMP_MODE_MTE,
240622ee123SVicki Pfau };
241622ee123SVicki Pfau 
242622ee123SVicki Pfau /* Input report identifiers */
243622ee123SVicki Pfau enum
244622ee123SVicki Pfau {
245622ee123SVicki Pfau 	ID_CONTROLLER_STATE = 1,
246622ee123SVicki Pfau 	ID_CONTROLLER_DEBUG = 2,
247622ee123SVicki Pfau 	ID_CONTROLLER_WIRELESS = 3,
248622ee123SVicki Pfau 	ID_CONTROLLER_STATUS = 4,
249622ee123SVicki Pfau 	ID_CONTROLLER_DEBUG2 = 5,
250622ee123SVicki Pfau 	ID_CONTROLLER_SECONDARY_STATE = 6,
251622ee123SVicki Pfau 	ID_CONTROLLER_BLE_STATE = 7,
252622ee123SVicki Pfau 	ID_CONTROLLER_DECK_STATE = 9
253622ee123SVicki Pfau };
254622ee123SVicki Pfau 
255622ee123SVicki Pfau /* String attribute idenitifiers */
256622ee123SVicki Pfau enum {
257622ee123SVicki Pfau 	ATTRIB_STR_BOARD_SERIAL,
258622ee123SVicki Pfau 	ATTRIB_STR_UNIT_SERIAL,
259622ee123SVicki Pfau };
260c164d6abSRodrigo Rivas Costa 
261c164d6abSRodrigo Rivas Costa /* Values for GYRO_MODE (bitmask) */
262622ee123SVicki Pfau enum {
263622ee123SVicki Pfau 	SETTING_GYRO_MODE_OFF			= 0,
264622ee123SVicki Pfau 	SETTING_GYRO_MODE_STEERING		= BIT(0),
265622ee123SVicki Pfau 	SETTING_GYRO_MODE_TILT			= BIT(1),
266622ee123SVicki Pfau 	SETTING_GYRO_MODE_SEND_ORIENTATION	= BIT(2),
267622ee123SVicki Pfau 	SETTING_GYRO_MODE_SEND_RAW_ACCEL	= BIT(3),
268622ee123SVicki Pfau 	SETTING_GYRO_MODE_SEND_RAW_GYRO		= BIT(4),
269622ee123SVicki Pfau };
270622ee123SVicki Pfau 
271622ee123SVicki Pfau /* Trackpad modes */
272622ee123SVicki Pfau enum {
273622ee123SVicki Pfau 	TRACKPAD_ABSOLUTE_MOUSE,
274622ee123SVicki Pfau 	TRACKPAD_RELATIVE_MOUSE,
275622ee123SVicki Pfau 	TRACKPAD_DPAD_FOUR_WAY_DISCRETE,
276622ee123SVicki Pfau 	TRACKPAD_DPAD_FOUR_WAY_OVERLAP,
277622ee123SVicki Pfau 	TRACKPAD_DPAD_EIGHT_WAY,
278622ee123SVicki Pfau 	TRACKPAD_RADIAL_MODE,
279622ee123SVicki Pfau 	TRACKPAD_ABSOLUTE_DPAD,
280622ee123SVicki Pfau 	TRACKPAD_NONE,
281622ee123SVicki Pfau 	TRACKPAD_GESTURE_KEYBOARD,
282622ee123SVicki Pfau };
283c164d6abSRodrigo Rivas Costa 
284c68606bdSVicki Pfau /* Pad identifiers for the deck */
285c68606bdSVicki Pfau #define STEAM_PAD_LEFT 0
286c68606bdSVicki Pfau #define STEAM_PAD_RIGHT 1
287c68606bdSVicki Pfau #define STEAM_PAD_BOTH 2
288c68606bdSVicki Pfau 
289c164d6abSRodrigo Rivas Costa /* Other random constants */
290c164d6abSRodrigo Rivas Costa #define STEAM_SERIAL_LEN 10
291c164d6abSRodrigo Rivas Costa 
292c164d6abSRodrigo Rivas Costa struct steam_device {
293c164d6abSRodrigo Rivas Costa 	struct list_head list;
294c164d6abSRodrigo Rivas Costa 	spinlock_t lock;
295c164d6abSRodrigo Rivas Costa 	struct hid_device *hdev, *client_hdev;
2969544708cSVicki Pfau 	struct mutex report_mutex;
297385a4886SRodrigo Rivas Costa 	bool client_opened;
298c164d6abSRodrigo Rivas Costa 	struct input_dev __rcu *input;
2994b996b61SMax Maisel 	struct input_dev __rcu *sensors;
300c164d6abSRodrigo Rivas Costa 	unsigned long quirks;
301c164d6abSRodrigo Rivas Costa 	struct work_struct work_connect;
302c164d6abSRodrigo Rivas Costa 	bool connected;
303c164d6abSRodrigo Rivas Costa 	char serial_no[STEAM_SERIAL_LEN + 1];
304f8271979SRodrigo Rivas Costa 	struct power_supply_desc battery_desc;
305f8271979SRodrigo Rivas Costa 	struct power_supply __rcu *battery;
306f8271979SRodrigo Rivas Costa 	u8 battery_charge;
307f8271979SRodrigo Rivas Costa 	u16 voltage;
308c68606bdSVicki Pfau 	struct delayed_work mode_switch;
309c68606bdSVicki Pfau 	bool did_mode_switch;
310c68606bdSVicki Pfau 	bool gamepad_mode;
3119cd61c81SVicki Pfau 	struct work_struct rumble_work;
3129cd61c81SVicki Pfau 	u16 rumble_left;
3139cd61c81SVicki Pfau 	u16 rumble_right;
3144b996b61SMax Maisel 	unsigned int sensor_timestamp_us;
315e1147961SVicki Pfau 	struct work_struct unregister_work;
316c164d6abSRodrigo Rivas Costa };
317c164d6abSRodrigo Rivas Costa 
steam_recv_report(struct steam_device * steam,u8 * data,int size)318c164d6abSRodrigo Rivas Costa static int steam_recv_report(struct steam_device *steam,
319c164d6abSRodrigo Rivas Costa 		u8 *data, int size)
320c164d6abSRodrigo Rivas Costa {
321c164d6abSRodrigo Rivas Costa 	struct hid_report *r;
322c164d6abSRodrigo Rivas Costa 	u8 *buf;
323c164d6abSRodrigo Rivas Costa 	int ret;
324c164d6abSRodrigo Rivas Costa 
325c164d6abSRodrigo Rivas Costa 	r = steam->hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0];
326cd11d1a6SLee Jones 	if (!r) {
327cd11d1a6SLee Jones 		hid_err(steam->hdev, "No HID_FEATURE_REPORT submitted -  nothing to read\n");
328cd11d1a6SLee Jones 		return -EINVAL;
329cd11d1a6SLee Jones 	}
330cd11d1a6SLee Jones 
331c164d6abSRodrigo Rivas Costa 	if (hid_report_len(r) < 64)
332c164d6abSRodrigo Rivas Costa 		return -EINVAL;
333c164d6abSRodrigo Rivas Costa 
334c164d6abSRodrigo Rivas Costa 	buf = hid_alloc_report_buf(r, GFP_KERNEL);
335c164d6abSRodrigo Rivas Costa 	if (!buf)
336c164d6abSRodrigo Rivas Costa 		return -ENOMEM;
337c164d6abSRodrigo Rivas Costa 
338c164d6abSRodrigo Rivas Costa 	/*
339c164d6abSRodrigo Rivas Costa 	 * The report ID is always 0, so strip the first byte from the output.
340c164d6abSRodrigo Rivas Costa 	 * hid_report_len() is not counting the report ID, so +1 to the length
341c164d6abSRodrigo Rivas Costa 	 * or else we get a EOVERFLOW. We are safe from a buffer overflow
342c164d6abSRodrigo Rivas Costa 	 * because hid_alloc_report_buf() allocates +7 bytes.
343c164d6abSRodrigo Rivas Costa 	 */
344c164d6abSRodrigo Rivas Costa 	ret = hid_hw_raw_request(steam->hdev, 0x00,
345c164d6abSRodrigo Rivas Costa 			buf, hid_report_len(r) + 1,
346c164d6abSRodrigo Rivas Costa 			HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
347c164d6abSRodrigo Rivas Costa 	if (ret > 0)
348c164d6abSRodrigo Rivas Costa 		memcpy(data, buf + 1, min(size, ret - 1));
349c164d6abSRodrigo Rivas Costa 	kfree(buf);
350c164d6abSRodrigo Rivas Costa 	return ret;
351c164d6abSRodrigo Rivas Costa }
352c164d6abSRodrigo Rivas Costa 
steam_send_report(struct steam_device * steam,u8 * cmd,int size)353c164d6abSRodrigo Rivas Costa static int steam_send_report(struct steam_device *steam,
354c164d6abSRodrigo Rivas Costa 		u8 *cmd, int size)
355c164d6abSRodrigo Rivas Costa {
356c164d6abSRodrigo Rivas Costa 	struct hid_report *r;
357c164d6abSRodrigo Rivas Costa 	u8 *buf;
358c164d6abSRodrigo Rivas Costa 	unsigned int retries = 50;
359c164d6abSRodrigo Rivas Costa 	int ret;
360c164d6abSRodrigo Rivas Costa 
361c164d6abSRodrigo Rivas Costa 	r = steam->hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0];
362cd11d1a6SLee Jones 	if (!r) {
363cd11d1a6SLee Jones 		hid_err(steam->hdev, "No HID_FEATURE_REPORT submitted -  nothing to read\n");
364cd11d1a6SLee Jones 		return -EINVAL;
365cd11d1a6SLee Jones 	}
366cd11d1a6SLee Jones 
367c164d6abSRodrigo Rivas Costa 	if (hid_report_len(r) < 64)
368c164d6abSRodrigo Rivas Costa 		return -EINVAL;
369c164d6abSRodrigo Rivas Costa 
370c164d6abSRodrigo Rivas Costa 	buf = hid_alloc_report_buf(r, GFP_KERNEL);
371c164d6abSRodrigo Rivas Costa 	if (!buf)
372c164d6abSRodrigo Rivas Costa 		return -ENOMEM;
373c164d6abSRodrigo Rivas Costa 
374c164d6abSRodrigo Rivas Costa 	/* The report ID is always 0 */
375c164d6abSRodrigo Rivas Costa 	memcpy(buf + 1, cmd, size);
376c164d6abSRodrigo Rivas Costa 
377c164d6abSRodrigo Rivas Costa 	/*
378c164d6abSRodrigo Rivas Costa 	 * Sometimes the wireless controller fails with EPIPE
379c164d6abSRodrigo Rivas Costa 	 * when sending a feature report.
380c164d6abSRodrigo Rivas Costa 	 * Doing a HID_REQ_GET_REPORT and waiting for a while
381c164d6abSRodrigo Rivas Costa 	 * seems to fix that.
382c164d6abSRodrigo Rivas Costa 	 */
383c164d6abSRodrigo Rivas Costa 	do {
384c164d6abSRodrigo Rivas Costa 		ret = hid_hw_raw_request(steam->hdev, 0,
3859ba9498bSVicki Pfau 				buf, max(size, 64) + 1,
386c164d6abSRodrigo Rivas Costa 				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
387c164d6abSRodrigo Rivas Costa 		if (ret != -EPIPE)
388c164d6abSRodrigo Rivas Costa 			break;
389c164d6abSRodrigo Rivas Costa 		msleep(20);
390c164d6abSRodrigo Rivas Costa 	} while (--retries);
391c164d6abSRodrigo Rivas Costa 
392c164d6abSRodrigo Rivas Costa 	kfree(buf);
393c164d6abSRodrigo Rivas Costa 	if (ret < 0)
394c164d6abSRodrigo Rivas Costa 		hid_err(steam->hdev, "%s: error %d (%*ph)\n", __func__,
395c164d6abSRodrigo Rivas Costa 				ret, size, cmd);
396c164d6abSRodrigo Rivas Costa 	return ret;
397c164d6abSRodrigo Rivas Costa }
398c164d6abSRodrigo Rivas Costa 
steam_send_report_byte(struct steam_device * steam,u8 cmd)399c164d6abSRodrigo Rivas Costa static inline int steam_send_report_byte(struct steam_device *steam, u8 cmd)
400c164d6abSRodrigo Rivas Costa {
401c164d6abSRodrigo Rivas Costa 	return steam_send_report(steam, &cmd, 1);
402c164d6abSRodrigo Rivas Costa }
403c164d6abSRodrigo Rivas Costa 
steam_write_settings(struct steam_device * steam,...)404622ee123SVicki Pfau static int steam_write_settings(struct steam_device *steam,
405c164d6abSRodrigo Rivas Costa 		/* u8 reg, u16 val */...)
406c164d6abSRodrigo Rivas Costa {
407c164d6abSRodrigo Rivas Costa 	/* Send: 0x87 len (reg valLo valHi)* */
408c164d6abSRodrigo Rivas Costa 	u8 reg;
409c164d6abSRodrigo Rivas Costa 	u16 val;
410622ee123SVicki Pfau 	u8 cmd[64] = {ID_SET_SETTINGS_VALUES, 0x00};
4119ba9498bSVicki Pfau 	int ret;
412c164d6abSRodrigo Rivas Costa 	va_list args;
413c164d6abSRodrigo Rivas Costa 
414c164d6abSRodrigo Rivas Costa 	va_start(args, steam);
415c164d6abSRodrigo Rivas Costa 	for (;;) {
416c164d6abSRodrigo Rivas Costa 		reg = va_arg(args, int);
417c164d6abSRodrigo Rivas Costa 		if (reg == 0)
418c164d6abSRodrigo Rivas Costa 			break;
419c164d6abSRodrigo Rivas Costa 		val = va_arg(args, int);
420c164d6abSRodrigo Rivas Costa 		cmd[cmd[1] + 2] = reg;
421c164d6abSRodrigo Rivas Costa 		cmd[cmd[1] + 3] = val & 0xff;
422c164d6abSRodrigo Rivas Costa 		cmd[cmd[1] + 4] = val >> 8;
423c164d6abSRodrigo Rivas Costa 		cmd[1] += 3;
424c164d6abSRodrigo Rivas Costa 	}
425c164d6abSRodrigo Rivas Costa 	va_end(args);
426c164d6abSRodrigo Rivas Costa 
4279ba9498bSVicki Pfau 	ret = steam_send_report(steam, cmd, 2 + cmd[1]);
4289ba9498bSVicki Pfau 	if (ret < 0)
4299ba9498bSVicki Pfau 		return ret;
4309ba9498bSVicki Pfau 
4319ba9498bSVicki Pfau 	/*
4329ba9498bSVicki Pfau 	 * Sometimes a lingering report for this command can
4339ba9498bSVicki Pfau 	 * get read back instead of the last set report if
4349ba9498bSVicki Pfau 	 * this isn't explicitly queried
4359ba9498bSVicki Pfau 	 */
4369ba9498bSVicki Pfau 	return steam_recv_report(steam, cmd, 2 + cmd[1]);
437c164d6abSRodrigo Rivas Costa }
438c164d6abSRodrigo Rivas Costa 
steam_get_serial(struct steam_device * steam)439c164d6abSRodrigo Rivas Costa static int steam_get_serial(struct steam_device *steam)
440c164d6abSRodrigo Rivas Costa {
441c164d6abSRodrigo Rivas Costa 	/*
442c164d6abSRodrigo Rivas Costa 	 * Send: 0xae 0x15 0x01
443c164d6abSRodrigo Rivas Costa 	 * Recv: 0xae 0x15 0x01 serialnumber (10 chars)
444c164d6abSRodrigo Rivas Costa 	 */
4459544708cSVicki Pfau 	int ret = 0;
446622ee123SVicki Pfau 	u8 cmd[] = {ID_GET_STRING_ATTRIBUTE, 0x15, ATTRIB_STR_UNIT_SERIAL};
447c164d6abSRodrigo Rivas Costa 	u8 reply[3 + STEAM_SERIAL_LEN + 1];
448c164d6abSRodrigo Rivas Costa 
4499544708cSVicki Pfau 	mutex_lock(&steam->report_mutex);
450c164d6abSRodrigo Rivas Costa 	ret = steam_send_report(steam, cmd, sizeof(cmd));
451c164d6abSRodrigo Rivas Costa 	if (ret < 0)
4529544708cSVicki Pfau 		goto out;
453c164d6abSRodrigo Rivas Costa 	ret = steam_recv_report(steam, reply, sizeof(reply));
454c164d6abSRodrigo Rivas Costa 	if (ret < 0)
4559544708cSVicki Pfau 		goto out;
456622ee123SVicki Pfau 	if (reply[0] != ID_GET_STRING_ATTRIBUTE || reply[1] != 0x15 || reply[2] != ATTRIB_STR_UNIT_SERIAL) {
4579544708cSVicki Pfau 		ret = -EIO;
4589544708cSVicki Pfau 		goto out;
4599544708cSVicki Pfau 	}
460c164d6abSRodrigo Rivas Costa 	reply[3 + STEAM_SERIAL_LEN] = 0;
461eeeec27dSWolfram Sang 	strscpy(steam->serial_no, reply + 3, sizeof(steam->serial_no));
4629544708cSVicki Pfau out:
4639544708cSVicki Pfau 	mutex_unlock(&steam->report_mutex);
4649544708cSVicki Pfau 	return ret;
465c164d6abSRodrigo Rivas Costa }
466c164d6abSRodrigo Rivas Costa 
467c164d6abSRodrigo Rivas Costa /*
468c164d6abSRodrigo Rivas Costa  * This command requests the wireless adaptor to post an event
469c164d6abSRodrigo Rivas Costa  * with the connection status. Useful if this driver is loaded when
470c164d6abSRodrigo Rivas Costa  * the controller is already connected.
471c164d6abSRodrigo Rivas Costa  */
steam_request_conn_status(struct steam_device * steam)472c164d6abSRodrigo Rivas Costa static inline int steam_request_conn_status(struct steam_device *steam)
473c164d6abSRodrigo Rivas Costa {
4749544708cSVicki Pfau 	int ret;
4759544708cSVicki Pfau 	mutex_lock(&steam->report_mutex);
476622ee123SVicki Pfau 	ret = steam_send_report_byte(steam, ID_DONGLE_GET_WIRELESS_STATE);
4779544708cSVicki Pfau 	mutex_unlock(&steam->report_mutex);
4789544708cSVicki Pfau 	return ret;
479c164d6abSRodrigo Rivas Costa }
480c164d6abSRodrigo Rivas Costa 
481c68606bdSVicki Pfau /*
482c68606bdSVicki Pfau  * Send a haptic pulse to the trackpads
483c68606bdSVicki Pfau  * Duration and interval are measured in microseconds, count is the number
484c68606bdSVicki Pfau  * of pulses to send for duration time with interval microseconds between them
485c68606bdSVicki Pfau  * and gain is measured in decibels, ranging from -24 to +6
486c68606bdSVicki Pfau  */
steam_haptic_pulse(struct steam_device * steam,u8 pad,u16 duration,u16 interval,u16 count,u8 gain)487c68606bdSVicki Pfau static inline int steam_haptic_pulse(struct steam_device *steam, u8 pad,
488c68606bdSVicki Pfau 				u16 duration, u16 interval, u16 count, u8 gain)
489c68606bdSVicki Pfau {
490c68606bdSVicki Pfau 	int ret;
491c68606bdSVicki Pfau 	u8 report[10] = {ID_TRIGGER_HAPTIC_PULSE, 8};
492c68606bdSVicki Pfau 
493c68606bdSVicki Pfau 	/* Left and right are swapped on this report for legacy reasons */
494c68606bdSVicki Pfau 	if (pad < STEAM_PAD_BOTH)
495c68606bdSVicki Pfau 		pad ^= 1;
496c68606bdSVicki Pfau 
497c68606bdSVicki Pfau 	report[2] = pad;
498c68606bdSVicki Pfau 	report[3] = duration & 0xFF;
499c68606bdSVicki Pfau 	report[4] = duration >> 8;
500c68606bdSVicki Pfau 	report[5] = interval & 0xFF;
501c68606bdSVicki Pfau 	report[6] = interval >> 8;
502c68606bdSVicki Pfau 	report[7] = count & 0xFF;
503c68606bdSVicki Pfau 	report[8] = count >> 8;
504c68606bdSVicki Pfau 	report[9] = gain;
505c68606bdSVicki Pfau 
506c68606bdSVicki Pfau 	mutex_lock(&steam->report_mutex);
507c68606bdSVicki Pfau 	ret = steam_send_report(steam, report, sizeof(report));
508c68606bdSVicki Pfau 	mutex_unlock(&steam->report_mutex);
509c68606bdSVicki Pfau 	return ret;
510c68606bdSVicki Pfau }
511c68606bdSVicki Pfau 
steam_haptic_rumble(struct steam_device * steam,u16 intensity,u16 left_speed,u16 right_speed,u8 left_gain,u8 right_gain)5129cd61c81SVicki Pfau static inline int steam_haptic_rumble(struct steam_device *steam,
5139cd61c81SVicki Pfau 				u16 intensity, u16 left_speed, u16 right_speed,
5149cd61c81SVicki Pfau 				u8 left_gain, u8 right_gain)
5159cd61c81SVicki Pfau {
5169544708cSVicki Pfau 	int ret;
517622ee123SVicki Pfau 	u8 report[11] = {ID_TRIGGER_RUMBLE_CMD, 9};
5189cd61c81SVicki Pfau 
5199cd61c81SVicki Pfau 	report[3] = intensity & 0xFF;
5209cd61c81SVicki Pfau 	report[4] = intensity >> 8;
5219cd61c81SVicki Pfau 	report[5] = left_speed & 0xFF;
5229cd61c81SVicki Pfau 	report[6] = left_speed >> 8;
5239cd61c81SVicki Pfau 	report[7] = right_speed & 0xFF;
5249cd61c81SVicki Pfau 	report[8] = right_speed >> 8;
5259cd61c81SVicki Pfau 	report[9] = left_gain;
5269cd61c81SVicki Pfau 	report[10] = right_gain;
5279cd61c81SVicki Pfau 
5289544708cSVicki Pfau 	mutex_lock(&steam->report_mutex);
5299544708cSVicki Pfau 	ret = steam_send_report(steam, report, sizeof(report));
5309544708cSVicki Pfau 	mutex_unlock(&steam->report_mutex);
5319544708cSVicki Pfau 	return ret;
5329cd61c81SVicki Pfau }
5339cd61c81SVicki Pfau 
steam_haptic_rumble_cb(struct work_struct * work)5349cd61c81SVicki Pfau static void steam_haptic_rumble_cb(struct work_struct *work)
5359cd61c81SVicki Pfau {
5369cd61c81SVicki Pfau 	struct steam_device *steam = container_of(work, struct steam_device,
5379cd61c81SVicki Pfau 							rumble_work);
5389cd61c81SVicki Pfau 	steam_haptic_rumble(steam, 0, steam->rumble_left,
5399cd61c81SVicki Pfau 		steam->rumble_right, 2, 0);
5409cd61c81SVicki Pfau }
5419cd61c81SVicki Pfau 
5429cd61c81SVicki Pfau #ifdef CONFIG_STEAM_FF
steam_play_effect(struct input_dev * dev,void * data,struct ff_effect * effect)5439cd61c81SVicki Pfau static int steam_play_effect(struct input_dev *dev, void *data,
5449cd61c81SVicki Pfau 				struct ff_effect *effect)
5459cd61c81SVicki Pfau {
5469cd61c81SVicki Pfau 	struct steam_device *steam = input_get_drvdata(dev);
5479cd61c81SVicki Pfau 
5489cd61c81SVicki Pfau 	steam->rumble_left = effect->u.rumble.strong_magnitude;
5499cd61c81SVicki Pfau 	steam->rumble_right = effect->u.rumble.weak_magnitude;
5509cd61c81SVicki Pfau 
5519cd61c81SVicki Pfau 	return schedule_work(&steam->rumble_work);
5529cd61c81SVicki Pfau }
5539cd61c81SVicki Pfau #endif
5549cd61c81SVicki Pfau 
steam_set_lizard_mode(struct steam_device * steam,bool enable)555c164d6abSRodrigo Rivas Costa static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
556c164d6abSRodrigo Rivas Costa {
557c68606bdSVicki Pfau 	if (steam->gamepad_mode)
558c68606bdSVicki Pfau 		enable = false;
559c68606bdSVicki Pfau 
560c164d6abSRodrigo Rivas Costa 	if (enable) {
5619544708cSVicki Pfau 		mutex_lock(&steam->report_mutex);
562c164d6abSRodrigo Rivas Costa 		/* enable esc, enter, cursors */
563622ee123SVicki Pfau 		steam_send_report_byte(steam, ID_SET_DEFAULT_DIGITAL_MAPPINGS);
564622ee123SVicki Pfau 		/* reset settings */
565622ee123SVicki Pfau 		steam_send_report_byte(steam, ID_LOAD_DEFAULT_SETTINGS);
5669544708cSVicki Pfau 		mutex_unlock(&steam->report_mutex);
567c164d6abSRodrigo Rivas Costa 	} else {
5689544708cSVicki Pfau 		mutex_lock(&steam->report_mutex);
569c164d6abSRodrigo Rivas Costa 		/* disable esc, enter, cursor */
570622ee123SVicki Pfau 		steam_send_report_byte(steam, ID_CLEAR_DIGITAL_MAPPINGS);
5719ba9498bSVicki Pfau 
5729ba9498bSVicki Pfau 		if (steam->quirks & STEAM_QUIRK_DECK) {
573622ee123SVicki Pfau 			steam_write_settings(steam,
574622ee123SVicki Pfau 				SETTING_LEFT_TRACKPAD_MODE, TRACKPAD_NONE, /* disable mouse */
575622ee123SVicki Pfau 				SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_NONE, /* disable mouse */
576622ee123SVicki Pfau 				SETTING_LEFT_TRACKPAD_CLICK_PRESSURE, 0xFFFF, /* disable haptic click */
577622ee123SVicki Pfau 				SETTING_RIGHT_TRACKPAD_CLICK_PRESSURE, 0xFFFF, /* disable haptic click */
578622ee123SVicki Pfau 				SETTING_STEAM_WATCHDOG_ENABLE, 0, /* disable watchdog that tests if Steam is active */
579c164d6abSRodrigo Rivas Costa 				0);
5809544708cSVicki Pfau 			mutex_unlock(&steam->report_mutex);
5819ba9498bSVicki Pfau 		} else {
582622ee123SVicki Pfau 			steam_write_settings(steam,
583622ee123SVicki Pfau 				SETTING_LEFT_TRACKPAD_MODE, TRACKPAD_NONE, /* disable mouse */
584622ee123SVicki Pfau 				SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_NONE, /* disable mouse */
5859ba9498bSVicki Pfau 				0);
5869544708cSVicki Pfau 			mutex_unlock(&steam->report_mutex);
5879ba9498bSVicki Pfau 		}
588c164d6abSRodrigo Rivas Costa 	}
589c164d6abSRodrigo Rivas Costa }
590c164d6abSRodrigo Rivas Costa 
steam_input_open(struct input_dev * dev)591c164d6abSRodrigo Rivas Costa static int steam_input_open(struct input_dev *dev)
592c164d6abSRodrigo Rivas Costa {
593c164d6abSRodrigo Rivas Costa 	struct steam_device *steam = input_get_drvdata(dev);
5949544708cSVicki Pfau 	unsigned long flags;
5959544708cSVicki Pfau 	bool set_lizard_mode;
596c164d6abSRodrigo Rivas Costa 
597c68606bdSVicki Pfau 	/*
598c68606bdSVicki Pfau 	 * Disabling lizard mode automatically is only done on the Steam
599c68606bdSVicki Pfau 	 * Controller. On the Steam Deck, this is toggled manually by holding
600c68606bdSVicki Pfau 	 * the options button instead, handled by steam_mode_switch_cb.
601c68606bdSVicki Pfau 	 */
602c68606bdSVicki Pfau 	if (!(steam->quirks & STEAM_QUIRK_DECK)) {
6039544708cSVicki Pfau 		spin_lock_irqsave(&steam->lock, flags);
6049544708cSVicki Pfau 		set_lizard_mode = !steam->client_opened && lizard_mode;
6059544708cSVicki Pfau 		spin_unlock_irqrestore(&steam->lock, flags);
6069544708cSVicki Pfau 		if (set_lizard_mode)
607c164d6abSRodrigo Rivas Costa 			steam_set_lizard_mode(steam, false);
608c68606bdSVicki Pfau 	}
6099544708cSVicki Pfau 
610c164d6abSRodrigo Rivas Costa 	return 0;
611c164d6abSRodrigo Rivas Costa }
612c164d6abSRodrigo Rivas Costa 
steam_input_close(struct input_dev * dev)613c164d6abSRodrigo Rivas Costa static void steam_input_close(struct input_dev *dev)
614c164d6abSRodrigo Rivas Costa {
615c164d6abSRodrigo Rivas Costa 	struct steam_device *steam = input_get_drvdata(dev);
6169544708cSVicki Pfau 	unsigned long flags;
6179544708cSVicki Pfau 	bool set_lizard_mode;
618c164d6abSRodrigo Rivas Costa 
619c68606bdSVicki Pfau 	if (!(steam->quirks & STEAM_QUIRK_DECK)) {
6209544708cSVicki Pfau 		spin_lock_irqsave(&steam->lock, flags);
6219544708cSVicki Pfau 		set_lizard_mode = !steam->client_opened && lizard_mode;
6229544708cSVicki Pfau 		spin_unlock_irqrestore(&steam->lock, flags);
6239544708cSVicki Pfau 		if (set_lizard_mode)
624c164d6abSRodrigo Rivas Costa 			steam_set_lizard_mode(steam, true);
625c164d6abSRodrigo Rivas Costa 	}
626c68606bdSVicki Pfau }
627c164d6abSRodrigo Rivas Costa 
628f8271979SRodrigo Rivas Costa static enum power_supply_property steam_battery_props[] = {
629f8271979SRodrigo Rivas Costa 	POWER_SUPPLY_PROP_PRESENT,
630f8271979SRodrigo Rivas Costa 	POWER_SUPPLY_PROP_SCOPE,
631f8271979SRodrigo Rivas Costa 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
632f8271979SRodrigo Rivas Costa 	POWER_SUPPLY_PROP_CAPACITY,
633f8271979SRodrigo Rivas Costa };
634f8271979SRodrigo Rivas Costa 
steam_battery_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)635f8271979SRodrigo Rivas Costa static int steam_battery_get_property(struct power_supply *psy,
636f8271979SRodrigo Rivas Costa 				enum power_supply_property psp,
637f8271979SRodrigo Rivas Costa 				union power_supply_propval *val)
638f8271979SRodrigo Rivas Costa {
639f8271979SRodrigo Rivas Costa 	struct steam_device *steam = power_supply_get_drvdata(psy);
640f8271979SRodrigo Rivas Costa 	unsigned long flags;
641f8271979SRodrigo Rivas Costa 	s16 volts;
642f8271979SRodrigo Rivas Costa 	u8 batt;
643f8271979SRodrigo Rivas Costa 	int ret = 0;
644f8271979SRodrigo Rivas Costa 
645f8271979SRodrigo Rivas Costa 	spin_lock_irqsave(&steam->lock, flags);
646f8271979SRodrigo Rivas Costa 	volts = steam->voltage;
647f8271979SRodrigo Rivas Costa 	batt = steam->battery_charge;
648f8271979SRodrigo Rivas Costa 	spin_unlock_irqrestore(&steam->lock, flags);
649f8271979SRodrigo Rivas Costa 
650f8271979SRodrigo Rivas Costa 	switch (psp) {
651f8271979SRodrigo Rivas Costa 	case POWER_SUPPLY_PROP_PRESENT:
652f8271979SRodrigo Rivas Costa 		val->intval = 1;
653f8271979SRodrigo Rivas Costa 		break;
654f8271979SRodrigo Rivas Costa 	case POWER_SUPPLY_PROP_SCOPE:
655f8271979SRodrigo Rivas Costa 		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
656f8271979SRodrigo Rivas Costa 		break;
657f8271979SRodrigo Rivas Costa 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
658f8271979SRodrigo Rivas Costa 		val->intval = volts * 1000; /* mV -> uV */
659f8271979SRodrigo Rivas Costa 		break;
660f8271979SRodrigo Rivas Costa 	case POWER_SUPPLY_PROP_CAPACITY:
661f8271979SRodrigo Rivas Costa 		val->intval = batt;
662f8271979SRodrigo Rivas Costa 		break;
663f8271979SRodrigo Rivas Costa 	default:
664f8271979SRodrigo Rivas Costa 		ret = -EINVAL;
665f8271979SRodrigo Rivas Costa 		break;
666f8271979SRodrigo Rivas Costa 	}
667f8271979SRodrigo Rivas Costa 	return ret;
668f8271979SRodrigo Rivas Costa }
669f8271979SRodrigo Rivas Costa 
steam_battery_register(struct steam_device * steam)670f8271979SRodrigo Rivas Costa static int steam_battery_register(struct steam_device *steam)
671f8271979SRodrigo Rivas Costa {
672f8271979SRodrigo Rivas Costa 	struct power_supply *battery;
673f8271979SRodrigo Rivas Costa 	struct power_supply_config battery_cfg = { .drv_data = steam, };
674f8271979SRodrigo Rivas Costa 	unsigned long flags;
675f8271979SRodrigo Rivas Costa 	int ret;
676f8271979SRodrigo Rivas Costa 
677f8271979SRodrigo Rivas Costa 	steam->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
678f8271979SRodrigo Rivas Costa 	steam->battery_desc.properties = steam_battery_props;
679f8271979SRodrigo Rivas Costa 	steam->battery_desc.num_properties = ARRAY_SIZE(steam_battery_props);
680f8271979SRodrigo Rivas Costa 	steam->battery_desc.get_property = steam_battery_get_property;
681f8271979SRodrigo Rivas Costa 	steam->battery_desc.name = devm_kasprintf(&steam->hdev->dev,
682f8271979SRodrigo Rivas Costa 			GFP_KERNEL, "steam-controller-%s-battery",
683f8271979SRodrigo Rivas Costa 			steam->serial_no);
684f8271979SRodrigo Rivas Costa 	if (!steam->battery_desc.name)
685f8271979SRodrigo Rivas Costa 		return -ENOMEM;
686f8271979SRodrigo Rivas Costa 
687f8271979SRodrigo Rivas Costa 	/* avoid the warning of 0% battery while waiting for the first info */
688f8271979SRodrigo Rivas Costa 	spin_lock_irqsave(&steam->lock, flags);
689f8271979SRodrigo Rivas Costa 	steam->voltage = 3000;
690f8271979SRodrigo Rivas Costa 	steam->battery_charge = 100;
691f8271979SRodrigo Rivas Costa 	spin_unlock_irqrestore(&steam->lock, flags);
692f8271979SRodrigo Rivas Costa 
693f8271979SRodrigo Rivas Costa 	battery = power_supply_register(&steam->hdev->dev,
694f8271979SRodrigo Rivas Costa 			&steam->battery_desc, &battery_cfg);
695f8271979SRodrigo Rivas Costa 	if (IS_ERR(battery)) {
696f8271979SRodrigo Rivas Costa 		ret = PTR_ERR(battery);
697f8271979SRodrigo Rivas Costa 		hid_err(steam->hdev,
698f8271979SRodrigo Rivas Costa 				"%s:power_supply_register failed with error %d\n",
699f8271979SRodrigo Rivas Costa 				__func__, ret);
700f8271979SRodrigo Rivas Costa 		return ret;
701f8271979SRodrigo Rivas Costa 	}
702f8271979SRodrigo Rivas Costa 	rcu_assign_pointer(steam->battery, battery);
703f8271979SRodrigo Rivas Costa 	power_supply_powers(battery, &steam->hdev->dev);
704f8271979SRodrigo Rivas Costa 	return 0;
705f8271979SRodrigo Rivas Costa }
706f8271979SRodrigo Rivas Costa 
steam_input_register(struct steam_device * steam)707385a4886SRodrigo Rivas Costa static int steam_input_register(struct steam_device *steam)
708c164d6abSRodrigo Rivas Costa {
709c164d6abSRodrigo Rivas Costa 	struct hid_device *hdev = steam->hdev;
710c164d6abSRodrigo Rivas Costa 	struct input_dev *input;
711c164d6abSRodrigo Rivas Costa 	int ret;
712c164d6abSRodrigo Rivas Costa 
713c164d6abSRodrigo Rivas Costa 	rcu_read_lock();
714c164d6abSRodrigo Rivas Costa 	input = rcu_dereference(steam->input);
715c164d6abSRodrigo Rivas Costa 	rcu_read_unlock();
716c164d6abSRodrigo Rivas Costa 	if (input) {
717c164d6abSRodrigo Rivas Costa 		dbg_hid("%s: already connected\n", __func__);
718c164d6abSRodrigo Rivas Costa 		return 0;
719c164d6abSRodrigo Rivas Costa 	}
720c164d6abSRodrigo Rivas Costa 
721c164d6abSRodrigo Rivas Costa 	input = input_allocate_device();
722c164d6abSRodrigo Rivas Costa 	if (!input)
723c164d6abSRodrigo Rivas Costa 		return -ENOMEM;
724c164d6abSRodrigo Rivas Costa 
725c164d6abSRodrigo Rivas Costa 	input_set_drvdata(input, steam);
726c164d6abSRodrigo Rivas Costa 	input->dev.parent = &hdev->dev;
727c164d6abSRodrigo Rivas Costa 	input->open = steam_input_open;
728c164d6abSRodrigo Rivas Costa 	input->close = steam_input_close;
729c164d6abSRodrigo Rivas Costa 
7309ba9498bSVicki Pfau 	input->name = (steam->quirks & STEAM_QUIRK_WIRELESS) ? "Wireless Steam Controller" :
7319ba9498bSVicki Pfau 		(steam->quirks & STEAM_QUIRK_DECK) ? "Steam Deck" :
732c164d6abSRodrigo Rivas Costa 		"Steam Controller";
733c164d6abSRodrigo Rivas Costa 	input->phys = hdev->phys;
734c164d6abSRodrigo Rivas Costa 	input->uniq = steam->serial_no;
735c164d6abSRodrigo Rivas Costa 	input->id.bustype = hdev->bus;
736c164d6abSRodrigo Rivas Costa 	input->id.vendor = hdev->vendor;
737c164d6abSRodrigo Rivas Costa 	input->id.product = hdev->product;
738c164d6abSRodrigo Rivas Costa 	input->id.version = hdev->version;
739c164d6abSRodrigo Rivas Costa 
740c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_TR2);
741c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_TL2);
742c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_TR);
743c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_TL);
744c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_Y);
745c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_B);
746c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_X);
747c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_A);
748c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_DPAD_UP);
749c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_DPAD_RIGHT);
750c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_DPAD_LEFT);
751c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_DPAD_DOWN);
752c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_SELECT);
753c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_MODE);
754c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_START);
755c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_THUMBR);
756c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_THUMBL);
757c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_THUMB);
758c164d6abSRodrigo Rivas Costa 	input_set_capability(input, EV_KEY, BTN_THUMB2);
7599ba9498bSVicki Pfau 	if (steam->quirks & STEAM_QUIRK_DECK) {
7609ba9498bSVicki Pfau 		input_set_capability(input, EV_KEY, BTN_BASE);
7619ba9498bSVicki Pfau 		input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY1);
7629ba9498bSVicki Pfau 		input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY2);
7639ba9498bSVicki Pfau 		input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY3);
7649ba9498bSVicki Pfau 		input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY4);
7659ba9498bSVicki Pfau 	} else {
7669ba9498bSVicki Pfau 		input_set_capability(input, EV_KEY, BTN_GEAR_DOWN);
7679ba9498bSVicki Pfau 		input_set_capability(input, EV_KEY, BTN_GEAR_UP);
7689ba9498bSVicki Pfau 	}
769c164d6abSRodrigo Rivas Costa 
770c164d6abSRodrigo Rivas Costa 	input_set_abs_params(input, ABS_X, -32767, 32767, 0, 0);
771c164d6abSRodrigo Rivas Costa 	input_set_abs_params(input, ABS_Y, -32767, 32767, 0, 0);
7729ba9498bSVicki Pfau 
773c164d6abSRodrigo Rivas Costa 	input_set_abs_params(input, ABS_HAT0X, -32767, 32767,
774c164d6abSRodrigo Rivas Costa 			STEAM_PAD_FUZZ, 0);
775c164d6abSRodrigo Rivas Costa 	input_set_abs_params(input, ABS_HAT0Y, -32767, 32767,
776c164d6abSRodrigo Rivas Costa 			STEAM_PAD_FUZZ, 0);
7779ba9498bSVicki Pfau 
7789ba9498bSVicki Pfau 	if (steam->quirks & STEAM_QUIRK_DECK) {
7799ba9498bSVicki Pfau 		input_set_abs_params(input, ABS_HAT2Y, 0, 32767, 0, 0);
7809ba9498bSVicki Pfau 		input_set_abs_params(input, ABS_HAT2X, 0, 32767, 0, 0);
7819ba9498bSVicki Pfau 
7829ba9498bSVicki Pfau 		input_set_abs_params(input, ABS_RX, -32767, 32767, 0, 0);
7839ba9498bSVicki Pfau 		input_set_abs_params(input, ABS_RY, -32767, 32767, 0, 0);
7849ba9498bSVicki Pfau 
7859ba9498bSVicki Pfau 		input_set_abs_params(input, ABS_HAT1X, -32767, 32767,
7869ba9498bSVicki Pfau 				STEAM_PAD_FUZZ, 0);
7879ba9498bSVicki Pfau 		input_set_abs_params(input, ABS_HAT1Y, -32767, 32767,
7889ba9498bSVicki Pfau 				STEAM_PAD_FUZZ, 0);
7899ba9498bSVicki Pfau 
7909ba9498bSVicki Pfau 		input_abs_set_res(input, ABS_X, STEAM_DECK_JOYSTICK_RESOLUTION);
7919ba9498bSVicki Pfau 		input_abs_set_res(input, ABS_Y, STEAM_DECK_JOYSTICK_RESOLUTION);
7929ba9498bSVicki Pfau 		input_abs_set_res(input, ABS_RX, STEAM_DECK_JOYSTICK_RESOLUTION);
7939ba9498bSVicki Pfau 		input_abs_set_res(input, ABS_RY, STEAM_DECK_JOYSTICK_RESOLUTION);
7949ba9498bSVicki Pfau 		input_abs_set_res(input, ABS_HAT1X, STEAM_PAD_RESOLUTION);
7959ba9498bSVicki Pfau 		input_abs_set_res(input, ABS_HAT1Y, STEAM_PAD_RESOLUTION);
7969ba9498bSVicki Pfau 		input_abs_set_res(input, ABS_HAT2Y, STEAM_DECK_TRIGGER_RESOLUTION);
7979ba9498bSVicki Pfau 		input_abs_set_res(input, ABS_HAT2X, STEAM_DECK_TRIGGER_RESOLUTION);
7989ba9498bSVicki Pfau 	} else {
7999ba9498bSVicki Pfau 		input_set_abs_params(input, ABS_HAT2Y, 0, 255, 0, 0);
8009ba9498bSVicki Pfau 		input_set_abs_params(input, ABS_HAT2X, 0, 255, 0, 0);
8019ba9498bSVicki Pfau 
8029ba9498bSVicki Pfau 		input_set_abs_params(input, ABS_RX, -32767, 32767,
8039ba9498bSVicki Pfau 				STEAM_PAD_FUZZ, 0);
8049ba9498bSVicki Pfau 		input_set_abs_params(input, ABS_RY, -32767, 32767,
8059ba9498bSVicki Pfau 				STEAM_PAD_FUZZ, 0);
8069ba9498bSVicki Pfau 
807c164d6abSRodrigo Rivas Costa 		input_abs_set_res(input, ABS_X, STEAM_JOYSTICK_RESOLUTION);
808c164d6abSRodrigo Rivas Costa 		input_abs_set_res(input, ABS_Y, STEAM_JOYSTICK_RESOLUTION);
809c164d6abSRodrigo Rivas Costa 		input_abs_set_res(input, ABS_RX, STEAM_PAD_RESOLUTION);
810c164d6abSRodrigo Rivas Costa 		input_abs_set_res(input, ABS_RY, STEAM_PAD_RESOLUTION);
811c164d6abSRodrigo Rivas Costa 		input_abs_set_res(input, ABS_HAT2Y, STEAM_TRIGGER_RESOLUTION);
812c164d6abSRodrigo Rivas Costa 		input_abs_set_res(input, ABS_HAT2X, STEAM_TRIGGER_RESOLUTION);
8139ba9498bSVicki Pfau 	}
8149ba9498bSVicki Pfau 	input_abs_set_res(input, ABS_HAT0X, STEAM_PAD_RESOLUTION);
8159ba9498bSVicki Pfau 	input_abs_set_res(input, ABS_HAT0Y, STEAM_PAD_RESOLUTION);
816c164d6abSRodrigo Rivas Costa 
8179cd61c81SVicki Pfau #ifdef CONFIG_STEAM_FF
8189cd61c81SVicki Pfau 	if (steam->quirks & STEAM_QUIRK_DECK) {
8199cd61c81SVicki Pfau 		input_set_capability(input, EV_FF, FF_RUMBLE);
8209cd61c81SVicki Pfau 		ret = input_ff_create_memless(input, NULL, steam_play_effect);
8219cd61c81SVicki Pfau 		if (ret)
8229cd61c81SVicki Pfau 			goto input_register_fail;
8239cd61c81SVicki Pfau 	}
8249cd61c81SVicki Pfau #endif
825c164d6abSRodrigo Rivas Costa 
826c164d6abSRodrigo Rivas Costa 	ret = input_register_device(input);
827c164d6abSRodrigo Rivas Costa 	if (ret)
828c164d6abSRodrigo Rivas Costa 		goto input_register_fail;
829c164d6abSRodrigo Rivas Costa 
830c164d6abSRodrigo Rivas Costa 	rcu_assign_pointer(steam->input, input);
831c164d6abSRodrigo Rivas Costa 	return 0;
832c164d6abSRodrigo Rivas Costa 
833c164d6abSRodrigo Rivas Costa input_register_fail:
834c164d6abSRodrigo Rivas Costa 	input_free_device(input);
835c164d6abSRodrigo Rivas Costa 	return ret;
836c164d6abSRodrigo Rivas Costa }
837c164d6abSRodrigo Rivas Costa 
steam_sensors_register(struct steam_device * steam)8384b996b61SMax Maisel static int steam_sensors_register(struct steam_device *steam)
8394b996b61SMax Maisel {
8404b996b61SMax Maisel 	struct hid_device *hdev = steam->hdev;
8414b996b61SMax Maisel 	struct input_dev *sensors;
8424b996b61SMax Maisel 	int ret;
8434b996b61SMax Maisel 
8444b996b61SMax Maisel 	if (!(steam->quirks & STEAM_QUIRK_DECK))
8454b996b61SMax Maisel 		return 0;
8464b996b61SMax Maisel 
8474b996b61SMax Maisel 	rcu_read_lock();
8484b996b61SMax Maisel 	sensors = rcu_dereference(steam->sensors);
8494b996b61SMax Maisel 	rcu_read_unlock();
8504b996b61SMax Maisel 	if (sensors) {
8514b996b61SMax Maisel 		dbg_hid("%s: already connected\n", __func__);
8524b996b61SMax Maisel 		return 0;
8534b996b61SMax Maisel 	}
8544b996b61SMax Maisel 
8554b996b61SMax Maisel 	sensors = input_allocate_device();
8564b996b61SMax Maisel 	if (!sensors)
8574b996b61SMax Maisel 		return -ENOMEM;
8584b996b61SMax Maisel 
8594b996b61SMax Maisel 	input_set_drvdata(sensors, steam);
8604b996b61SMax Maisel 	sensors->dev.parent = &hdev->dev;
8614b996b61SMax Maisel 
8624b996b61SMax Maisel 	sensors->name = "Steam Deck Motion Sensors";
8634b996b61SMax Maisel 	sensors->phys = hdev->phys;
8644b996b61SMax Maisel 	sensors->uniq = steam->serial_no;
8654b996b61SMax Maisel 	sensors->id.bustype = hdev->bus;
8664b996b61SMax Maisel 	sensors->id.vendor = hdev->vendor;
8674b996b61SMax Maisel 	sensors->id.product = hdev->product;
8684b996b61SMax Maisel 	sensors->id.version = hdev->version;
8694b996b61SMax Maisel 
8704b996b61SMax Maisel 	__set_bit(INPUT_PROP_ACCELEROMETER, sensors->propbit);
8714b996b61SMax Maisel 	__set_bit(EV_MSC, sensors->evbit);
8724b996b61SMax Maisel 	__set_bit(MSC_TIMESTAMP, sensors->mscbit);
8734b996b61SMax Maisel 
8744b996b61SMax Maisel 	input_set_abs_params(sensors, ABS_X, -STEAM_DECK_ACCEL_RANGE,
8754b996b61SMax Maisel 			STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
8764b996b61SMax Maisel 	input_set_abs_params(sensors, ABS_Y, -STEAM_DECK_ACCEL_RANGE,
8774b996b61SMax Maisel 			STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
8784b996b61SMax Maisel 	input_set_abs_params(sensors, ABS_Z, -STEAM_DECK_ACCEL_RANGE,
8794b996b61SMax Maisel 			STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0);
8804b996b61SMax Maisel 	input_abs_set_res(sensors, ABS_X, STEAM_DECK_ACCEL_RES_PER_G);
8814b996b61SMax Maisel 	input_abs_set_res(sensors, ABS_Y, STEAM_DECK_ACCEL_RES_PER_G);
8824b996b61SMax Maisel 	input_abs_set_res(sensors, ABS_Z, STEAM_DECK_ACCEL_RES_PER_G);
8834b996b61SMax Maisel 
8844b996b61SMax Maisel 	input_set_abs_params(sensors, ABS_RX, -STEAM_DECK_GYRO_RANGE,
8854b996b61SMax Maisel 			STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0);
8864b996b61SMax Maisel 	input_set_abs_params(sensors, ABS_RY, -STEAM_DECK_GYRO_RANGE,
8874b996b61SMax Maisel 			STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0);
8884b996b61SMax Maisel 	input_set_abs_params(sensors, ABS_RZ, -STEAM_DECK_GYRO_RANGE,
8894b996b61SMax Maisel 			STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0);
8904b996b61SMax Maisel 	input_abs_set_res(sensors, ABS_RX, STEAM_DECK_GYRO_RES_PER_DPS);
8914b996b61SMax Maisel 	input_abs_set_res(sensors, ABS_RY, STEAM_DECK_GYRO_RES_PER_DPS);
8924b996b61SMax Maisel 	input_abs_set_res(sensors, ABS_RZ, STEAM_DECK_GYRO_RES_PER_DPS);
8934b996b61SMax Maisel 
8944b996b61SMax Maisel 	ret = input_register_device(sensors);
8954b996b61SMax Maisel 	if (ret)
8964b996b61SMax Maisel 		goto sensors_register_fail;
8974b996b61SMax Maisel 
8984b996b61SMax Maisel 	rcu_assign_pointer(steam->sensors, sensors);
8994b996b61SMax Maisel 	return 0;
9004b996b61SMax Maisel 
9014b996b61SMax Maisel sensors_register_fail:
9024b996b61SMax Maisel 	input_free_device(sensors);
9034b996b61SMax Maisel 	return ret;
9044b996b61SMax Maisel }
9054b996b61SMax Maisel 
steam_input_unregister(struct steam_device * steam)906385a4886SRodrigo Rivas Costa static void steam_input_unregister(struct steam_device *steam)
907c164d6abSRodrigo Rivas Costa {
908c164d6abSRodrigo Rivas Costa 	struct input_dev *input;
909385a4886SRodrigo Rivas Costa 	rcu_read_lock();
910385a4886SRodrigo Rivas Costa 	input = rcu_dereference(steam->input);
911385a4886SRodrigo Rivas Costa 	rcu_read_unlock();
912385a4886SRodrigo Rivas Costa 	if (!input)
913385a4886SRodrigo Rivas Costa 		return;
914385a4886SRodrigo Rivas Costa 	RCU_INIT_POINTER(steam->input, NULL);
915385a4886SRodrigo Rivas Costa 	synchronize_rcu();
916385a4886SRodrigo Rivas Costa 	input_unregister_device(input);
917385a4886SRodrigo Rivas Costa }
918385a4886SRodrigo Rivas Costa 
steam_sensors_unregister(struct steam_device * steam)9194b996b61SMax Maisel static void steam_sensors_unregister(struct steam_device *steam)
9204b996b61SMax Maisel {
9214b996b61SMax Maisel 	struct input_dev *sensors;
9224b996b61SMax Maisel 
9234b996b61SMax Maisel 	if (!(steam->quirks & STEAM_QUIRK_DECK))
9244b996b61SMax Maisel 		return;
9254b996b61SMax Maisel 
9264b996b61SMax Maisel 	rcu_read_lock();
9274b996b61SMax Maisel 	sensors = rcu_dereference(steam->sensors);
9284b996b61SMax Maisel 	rcu_read_unlock();
9294b996b61SMax Maisel 
9304b996b61SMax Maisel 	if (!sensors)
9314b996b61SMax Maisel 		return;
9324b996b61SMax Maisel 	RCU_INIT_POINTER(steam->sensors, NULL);
9334b996b61SMax Maisel 	synchronize_rcu();
9344b996b61SMax Maisel 	input_unregister_device(sensors);
9354b996b61SMax Maisel }
9364b996b61SMax Maisel 
steam_battery_unregister(struct steam_device * steam)937385a4886SRodrigo Rivas Costa static void steam_battery_unregister(struct steam_device *steam)
938385a4886SRodrigo Rivas Costa {
939f8271979SRodrigo Rivas Costa 	struct power_supply *battery;
940c164d6abSRodrigo Rivas Costa 
941c164d6abSRodrigo Rivas Costa 	rcu_read_lock();
942f8271979SRodrigo Rivas Costa 	battery = rcu_dereference(steam->battery);
943c164d6abSRodrigo Rivas Costa 	rcu_read_unlock();
944c164d6abSRodrigo Rivas Costa 
945385a4886SRodrigo Rivas Costa 	if (!battery)
946385a4886SRodrigo Rivas Costa 		return;
947f8271979SRodrigo Rivas Costa 	RCU_INIT_POINTER(steam->battery, NULL);
948f8271979SRodrigo Rivas Costa 	synchronize_rcu();
949f8271979SRodrigo Rivas Costa 	power_supply_unregister(battery);
950f8271979SRodrigo Rivas Costa }
951385a4886SRodrigo Rivas Costa 
steam_register(struct steam_device * steam)952385a4886SRodrigo Rivas Costa static int steam_register(struct steam_device *steam)
953385a4886SRodrigo Rivas Costa {
954385a4886SRodrigo Rivas Costa 	int ret;
9556b538cc2SRodrigo Rivas Costa 	bool client_opened;
9569544708cSVicki Pfau 	unsigned long flags;
957385a4886SRodrigo Rivas Costa 
958385a4886SRodrigo Rivas Costa 	/*
959385a4886SRodrigo Rivas Costa 	 * This function can be called several times in a row with the
960385a4886SRodrigo Rivas Costa 	 * wireless adaptor, without steam_unregister() between them, because
961385a4886SRodrigo Rivas Costa 	 * another client send a get_connection_status command, for example.
962385a4886SRodrigo Rivas Costa 	 * The battery and serial number are set just once per device.
963385a4886SRodrigo Rivas Costa 	 */
964385a4886SRodrigo Rivas Costa 	if (!steam->serial_no[0]) {
965385a4886SRodrigo Rivas Costa 		/*
966385a4886SRodrigo Rivas Costa 		 * Unlikely, but getting the serial could fail, and it is not so
967385a4886SRodrigo Rivas Costa 		 * important, so make up a serial number and go on.
968385a4886SRodrigo Rivas Costa 		 */
969385a4886SRodrigo Rivas Costa 		if (steam_get_serial(steam) < 0)
970eeeec27dSWolfram Sang 			strscpy(steam->serial_no, "XXXXXXXXXX",
971385a4886SRodrigo Rivas Costa 					sizeof(steam->serial_no));
972385a4886SRodrigo Rivas Costa 
973385a4886SRodrigo Rivas Costa 		hid_info(steam->hdev, "Steam Controller '%s' connected",
974385a4886SRodrigo Rivas Costa 				steam->serial_no);
975385a4886SRodrigo Rivas Costa 
976385a4886SRodrigo Rivas Costa 		/* ignore battery errors, we can live without it */
977385a4886SRodrigo Rivas Costa 		if (steam->quirks & STEAM_QUIRK_WIRELESS)
978385a4886SRodrigo Rivas Costa 			steam_battery_register(steam);
979385a4886SRodrigo Rivas Costa 
980385a4886SRodrigo Rivas Costa 		mutex_lock(&steam_devices_lock);
9812d3f53a8SRodrigo Rivas Costa 		if (list_empty(&steam->list))
982385a4886SRodrigo Rivas Costa 			list_add(&steam->list, &steam_devices);
983385a4886SRodrigo Rivas Costa 		mutex_unlock(&steam_devices_lock);
984385a4886SRodrigo Rivas Costa 	}
985385a4886SRodrigo Rivas Costa 
9869544708cSVicki Pfau 	spin_lock_irqsave(&steam->lock, flags);
9876b538cc2SRodrigo Rivas Costa 	client_opened = steam->client_opened;
9889544708cSVicki Pfau 	spin_unlock_irqrestore(&steam->lock, flags);
9894b996b61SMax Maisel 
9909544708cSVicki Pfau 	if (!client_opened) {
991385a4886SRodrigo Rivas Costa 		steam_set_lizard_mode(steam, lizard_mode);
9926b538cc2SRodrigo Rivas Costa 		ret = steam_input_register(steam);
9934b996b61SMax Maisel 		if (ret != 0)
9944b996b61SMax Maisel 			goto steam_register_input_fail;
9954b996b61SMax Maisel 		ret = steam_sensors_register(steam);
9964b996b61SMax Maisel 		if (ret != 0)
9974b996b61SMax Maisel 			goto steam_register_sensors_fail;
9984b996b61SMax Maisel 	}
9994b996b61SMax Maisel 	return 0;
10006b538cc2SRodrigo Rivas Costa 
10014b996b61SMax Maisel steam_register_sensors_fail:
10024b996b61SMax Maisel 	steam_input_unregister(steam);
10034b996b61SMax Maisel steam_register_input_fail:
1004385a4886SRodrigo Rivas Costa 	return ret;
1005385a4886SRodrigo Rivas Costa }
1006385a4886SRodrigo Rivas Costa 
steam_unregister(struct steam_device * steam)1007385a4886SRodrigo Rivas Costa static void steam_unregister(struct steam_device *steam)
1008385a4886SRodrigo Rivas Costa {
1009385a4886SRodrigo Rivas Costa 	steam_battery_unregister(steam);
10104b996b61SMax Maisel 	steam_sensors_unregister(steam);
1011385a4886SRodrigo Rivas Costa 	steam_input_unregister(steam);
1012385a4886SRodrigo Rivas Costa 	if (steam->serial_no[0]) {
1013c164d6abSRodrigo Rivas Costa 		hid_info(steam->hdev, "Steam Controller '%s' disconnected",
1014c164d6abSRodrigo Rivas Costa 				steam->serial_no);
1015385a4886SRodrigo Rivas Costa 		mutex_lock(&steam_devices_lock);
10162d3f53a8SRodrigo Rivas Costa 		list_del_init(&steam->list);
1017385a4886SRodrigo Rivas Costa 		mutex_unlock(&steam_devices_lock);
1018385a4886SRodrigo Rivas Costa 		steam->serial_no[0] = 0;
1019c164d6abSRodrigo Rivas Costa 	}
1020c164d6abSRodrigo Rivas Costa }
1021c164d6abSRodrigo Rivas Costa 
steam_work_connect_cb(struct work_struct * work)1022c164d6abSRodrigo Rivas Costa static void steam_work_connect_cb(struct work_struct *work)
1023c164d6abSRodrigo Rivas Costa {
1024c164d6abSRodrigo Rivas Costa 	struct steam_device *steam = container_of(work, struct steam_device,
1025c164d6abSRodrigo Rivas Costa 							work_connect);
1026c164d6abSRodrigo Rivas Costa 	unsigned long flags;
1027c164d6abSRodrigo Rivas Costa 	bool connected;
1028c164d6abSRodrigo Rivas Costa 	int ret;
1029c164d6abSRodrigo Rivas Costa 
1030c164d6abSRodrigo Rivas Costa 	spin_lock_irqsave(&steam->lock, flags);
1031c164d6abSRodrigo Rivas Costa 	connected = steam->connected;
1032c164d6abSRodrigo Rivas Costa 	spin_unlock_irqrestore(&steam->lock, flags);
1033c164d6abSRodrigo Rivas Costa 
1034c164d6abSRodrigo Rivas Costa 	if (connected) {
1035c164d6abSRodrigo Rivas Costa 		ret = steam_register(steam);
1036c164d6abSRodrigo Rivas Costa 		if (ret) {
1037c164d6abSRodrigo Rivas Costa 			hid_err(steam->hdev,
1038c164d6abSRodrigo Rivas Costa 				"%s:steam_register failed with error %d\n",
1039c164d6abSRodrigo Rivas Costa 				__func__, ret);
1040c164d6abSRodrigo Rivas Costa 		}
1041c164d6abSRodrigo Rivas Costa 	} else {
1042c164d6abSRodrigo Rivas Costa 		steam_unregister(steam);
1043c164d6abSRodrigo Rivas Costa 	}
1044c164d6abSRodrigo Rivas Costa }
1045c164d6abSRodrigo Rivas Costa 
steam_mode_switch_cb(struct work_struct * work)1046c68606bdSVicki Pfau static void steam_mode_switch_cb(struct work_struct *work)
1047c68606bdSVicki Pfau {
1048c68606bdSVicki Pfau 	struct steam_device *steam = container_of(to_delayed_work(work),
1049c68606bdSVicki Pfau 							struct steam_device, mode_switch);
1050c68606bdSVicki Pfau 	unsigned long flags;
1051c68606bdSVicki Pfau 	bool client_opened;
1052c68606bdSVicki Pfau 	steam->gamepad_mode = !steam->gamepad_mode;
1053c68606bdSVicki Pfau 	if (!lizard_mode)
1054c68606bdSVicki Pfau 		return;
1055c68606bdSVicki Pfau 
1056c68606bdSVicki Pfau 	if (steam->gamepad_mode)
1057c68606bdSVicki Pfau 		steam_set_lizard_mode(steam, false);
1058c68606bdSVicki Pfau 	else {
1059c68606bdSVicki Pfau 		spin_lock_irqsave(&steam->lock, flags);
1060c68606bdSVicki Pfau 		client_opened = steam->client_opened;
1061c68606bdSVicki Pfau 		spin_unlock_irqrestore(&steam->lock, flags);
1062c68606bdSVicki Pfau 		if (!client_opened)
1063c68606bdSVicki Pfau 			steam_set_lizard_mode(steam, lizard_mode);
1064c68606bdSVicki Pfau 	}
1065c68606bdSVicki Pfau 
1066c68606bdSVicki Pfau 	steam_haptic_pulse(steam, STEAM_PAD_RIGHT, 0x190, 0, 1, 0);
1067c68606bdSVicki Pfau 	if (steam->gamepad_mode) {
1068c68606bdSVicki Pfau 		steam_haptic_pulse(steam, STEAM_PAD_LEFT, 0x14D, 0x14D, 0x2D, 0);
1069c68606bdSVicki Pfau 	} else {
1070c68606bdSVicki Pfau 		steam_haptic_pulse(steam, STEAM_PAD_LEFT, 0x1F4, 0x1F4, 0x1E, 0);
1071c68606bdSVicki Pfau 	}
1072c68606bdSVicki Pfau }
1073c68606bdSVicki Pfau 
steam_work_unregister_cb(struct work_struct * work)1074e1147961SVicki Pfau static void steam_work_unregister_cb(struct work_struct *work)
1075e1147961SVicki Pfau {
1076e1147961SVicki Pfau 	struct steam_device *steam = container_of(work, struct steam_device,
1077e1147961SVicki Pfau 							unregister_work);
1078e1147961SVicki Pfau 	unsigned long flags;
1079e1147961SVicki Pfau 	bool connected;
1080e1147961SVicki Pfau 	bool opened;
1081e1147961SVicki Pfau 
1082e1147961SVicki Pfau 	spin_lock_irqsave(&steam->lock, flags);
1083e1147961SVicki Pfau 	opened = steam->client_opened;
1084e1147961SVicki Pfau 	connected = steam->connected;
1085e1147961SVicki Pfau 	spin_unlock_irqrestore(&steam->lock, flags);
1086e1147961SVicki Pfau 
1087e1147961SVicki Pfau 	if (connected) {
1088e1147961SVicki Pfau 		if (opened) {
1089e1147961SVicki Pfau 			steam_sensors_unregister(steam);
1090e1147961SVicki Pfau 			steam_input_unregister(steam);
1091e1147961SVicki Pfau 		} else {
1092e1147961SVicki Pfau 			steam_set_lizard_mode(steam, lizard_mode);
1093e1147961SVicki Pfau 			steam_input_register(steam);
1094e1147961SVicki Pfau 			steam_sensors_register(steam);
1095e1147961SVicki Pfau 		}
1096e1147961SVicki Pfau 	}
1097e1147961SVicki Pfau }
1098e1147961SVicki Pfau 
steam_is_valve_interface(struct hid_device * hdev)1099c164d6abSRodrigo Rivas Costa static bool steam_is_valve_interface(struct hid_device *hdev)
1100c164d6abSRodrigo Rivas Costa {
1101c164d6abSRodrigo Rivas Costa 	struct hid_report_enum *rep_enum;
1102c164d6abSRodrigo Rivas Costa 
1103c164d6abSRodrigo Rivas Costa 	/*
1104c164d6abSRodrigo Rivas Costa 	 * The wired device creates 3 interfaces:
1105c164d6abSRodrigo Rivas Costa 	 *  0: emulated mouse.
1106c164d6abSRodrigo Rivas Costa 	 *  1: emulated keyboard.
1107c164d6abSRodrigo Rivas Costa 	 *  2: the real game pad.
1108c164d6abSRodrigo Rivas Costa 	 * The wireless device creates 5 interfaces:
1109c164d6abSRodrigo Rivas Costa 	 *  0: emulated keyboard.
1110c164d6abSRodrigo Rivas Costa 	 *  1-4: slots where up to 4 real game pads will be connected to.
1111c164d6abSRodrigo Rivas Costa 	 * We know which one is the real gamepad interface because they are the
1112c164d6abSRodrigo Rivas Costa 	 * only ones with a feature report.
1113c164d6abSRodrigo Rivas Costa 	 */
1114c164d6abSRodrigo Rivas Costa 	rep_enum = &hdev->report_enum[HID_FEATURE_REPORT];
1115c164d6abSRodrigo Rivas Costa 	return !list_empty(&rep_enum->report_list);
1116c164d6abSRodrigo Rivas Costa }
1117c164d6abSRodrigo Rivas Costa 
steam_client_ll_parse(struct hid_device * hdev)1118c164d6abSRodrigo Rivas Costa static int steam_client_ll_parse(struct hid_device *hdev)
1119c164d6abSRodrigo Rivas Costa {
11204bff980fSRodrigo Rivas Costa 	struct steam_device *steam = hdev->driver_data;
1121c164d6abSRodrigo Rivas Costa 
1122c164d6abSRodrigo Rivas Costa 	return hid_parse_report(hdev, steam->hdev->dev_rdesc,
1123c164d6abSRodrigo Rivas Costa 			steam->hdev->dev_rsize);
1124c164d6abSRodrigo Rivas Costa }
1125c164d6abSRodrigo Rivas Costa 
steam_client_ll_start(struct hid_device * hdev)1126c164d6abSRodrigo Rivas Costa static int steam_client_ll_start(struct hid_device *hdev)
1127c164d6abSRodrigo Rivas Costa {
1128c164d6abSRodrigo Rivas Costa 	return 0;
1129c164d6abSRodrigo Rivas Costa }
1130c164d6abSRodrigo Rivas Costa 
steam_client_ll_stop(struct hid_device * hdev)1131c164d6abSRodrigo Rivas Costa static void steam_client_ll_stop(struct hid_device *hdev)
1132c164d6abSRodrigo Rivas Costa {
1133c164d6abSRodrigo Rivas Costa }
1134c164d6abSRodrigo Rivas Costa 
steam_client_ll_open(struct hid_device * hdev)1135c164d6abSRodrigo Rivas Costa static int steam_client_ll_open(struct hid_device *hdev)
1136c164d6abSRodrigo Rivas Costa {
11374bff980fSRodrigo Rivas Costa 	struct steam_device *steam = hdev->driver_data;
11389544708cSVicki Pfau 	unsigned long flags;
1139c164d6abSRodrigo Rivas Costa 
11409544708cSVicki Pfau 	spin_lock_irqsave(&steam->lock, flags);
1141c164d6abSRodrigo Rivas Costa 	steam->client_opened = true;
11429544708cSVicki Pfau 	spin_unlock_irqrestore(&steam->lock, flags);
1143385a4886SRodrigo Rivas Costa 
1144e1147961SVicki Pfau 	schedule_work(&steam->unregister_work);
1145385a4886SRodrigo Rivas Costa 
1146cf28aee2SRodrigo Rivas Costa 	return 0;
1147c164d6abSRodrigo Rivas Costa }
1148c164d6abSRodrigo Rivas Costa 
steam_client_ll_close(struct hid_device * hdev)1149c164d6abSRodrigo Rivas Costa static void steam_client_ll_close(struct hid_device *hdev)
1150c164d6abSRodrigo Rivas Costa {
11514bff980fSRodrigo Rivas Costa 	struct steam_device *steam = hdev->driver_data;
1152c164d6abSRodrigo Rivas Costa 
11536b538cc2SRodrigo Rivas Costa 	unsigned long flags;
11546b538cc2SRodrigo Rivas Costa 	bool connected;
11556b538cc2SRodrigo Rivas Costa 
11566b538cc2SRodrigo Rivas Costa 	spin_lock_irqsave(&steam->lock, flags);
11579544708cSVicki Pfau 	steam->client_opened = false;
11589544708cSVicki Pfau 	connected = steam->connected && !steam->client_opened;
11596b538cc2SRodrigo Rivas Costa 	spin_unlock_irqrestore(&steam->lock, flags);
11606b538cc2SRodrigo Rivas Costa 
1161e1147961SVicki Pfau 	schedule_work(&steam->unregister_work);
11629544708cSVicki Pfau }
1163c164d6abSRodrigo Rivas Costa 
steam_client_ll_raw_request(struct hid_device * hdev,unsigned char reportnum,u8 * buf,size_t count,unsigned char report_type,int reqtype)1164c164d6abSRodrigo Rivas Costa static int steam_client_ll_raw_request(struct hid_device *hdev,
1165c164d6abSRodrigo Rivas Costa 				unsigned char reportnum, u8 *buf,
1166c164d6abSRodrigo Rivas Costa 				size_t count, unsigned char report_type,
1167c164d6abSRodrigo Rivas Costa 				int reqtype)
1168c164d6abSRodrigo Rivas Costa {
11694bff980fSRodrigo Rivas Costa 	struct steam_device *steam = hdev->driver_data;
1170c164d6abSRodrigo Rivas Costa 
1171c164d6abSRodrigo Rivas Costa 	return hid_hw_raw_request(steam->hdev, reportnum, buf, count,
1172c164d6abSRodrigo Rivas Costa 			report_type, reqtype);
1173c164d6abSRodrigo Rivas Costa }
1174c164d6abSRodrigo Rivas Costa 
1175ddb6792fSThomas Weißschuh static const struct hid_ll_driver steam_client_ll_driver = {
1176c164d6abSRodrigo Rivas Costa 	.parse = steam_client_ll_parse,
1177c164d6abSRodrigo Rivas Costa 	.start = steam_client_ll_start,
1178c164d6abSRodrigo Rivas Costa 	.stop = steam_client_ll_stop,
1179c164d6abSRodrigo Rivas Costa 	.open = steam_client_ll_open,
1180c164d6abSRodrigo Rivas Costa 	.close = steam_client_ll_close,
1181c164d6abSRodrigo Rivas Costa 	.raw_request = steam_client_ll_raw_request,
1182c164d6abSRodrigo Rivas Costa };
1183c164d6abSRodrigo Rivas Costa 
steam_create_client_hid(struct hid_device * hdev)1184c164d6abSRodrigo Rivas Costa static struct hid_device *steam_create_client_hid(struct hid_device *hdev)
1185c164d6abSRodrigo Rivas Costa {
1186c164d6abSRodrigo Rivas Costa 	struct hid_device *client_hdev;
1187c164d6abSRodrigo Rivas Costa 
1188c164d6abSRodrigo Rivas Costa 	client_hdev = hid_allocate_device();
1189c164d6abSRodrigo Rivas Costa 	if (IS_ERR(client_hdev))
1190c164d6abSRodrigo Rivas Costa 		return client_hdev;
1191c164d6abSRodrigo Rivas Costa 
1192c164d6abSRodrigo Rivas Costa 	client_hdev->ll_driver = &steam_client_ll_driver;
1193c164d6abSRodrigo Rivas Costa 	client_hdev->dev.parent = hdev->dev.parent;
1194c164d6abSRodrigo Rivas Costa 	client_hdev->bus = hdev->bus;
1195c164d6abSRodrigo Rivas Costa 	client_hdev->vendor = hdev->vendor;
1196c164d6abSRodrigo Rivas Costa 	client_hdev->product = hdev->product;
1197165e2cadSJiri Kosina 	client_hdev->version = hdev->version;
1198165e2cadSJiri Kosina 	client_hdev->type = hdev->type;
1199165e2cadSJiri Kosina 	client_hdev->country = hdev->country;
1200eeeec27dSWolfram Sang 	strscpy(client_hdev->name, hdev->name,
1201c164d6abSRodrigo Rivas Costa 			sizeof(client_hdev->name));
1202eeeec27dSWolfram Sang 	strscpy(client_hdev->phys, hdev->phys,
1203c164d6abSRodrigo Rivas Costa 			sizeof(client_hdev->phys));
1204c164d6abSRodrigo Rivas Costa 	/*
1205c164d6abSRodrigo Rivas Costa 	 * Since we use the same device info than the real interface to
1206c164d6abSRodrigo Rivas Costa 	 * trick userspace, we will be calling steam_probe recursively.
1207c164d6abSRodrigo Rivas Costa 	 * We need to recognize the client interface somehow.
1208c164d6abSRodrigo Rivas Costa 	 */
1209c164d6abSRodrigo Rivas Costa 	client_hdev->group = HID_GROUP_STEAM;
1210c164d6abSRodrigo Rivas Costa 	return client_hdev;
1211c164d6abSRodrigo Rivas Costa }
1212c164d6abSRodrigo Rivas Costa 
steam_probe(struct hid_device * hdev,const struct hid_device_id * id)1213c164d6abSRodrigo Rivas Costa static int steam_probe(struct hid_device *hdev,
1214c164d6abSRodrigo Rivas Costa 				const struct hid_device_id *id)
1215c164d6abSRodrigo Rivas Costa {
1216c164d6abSRodrigo Rivas Costa 	struct steam_device *steam;
1217c164d6abSRodrigo Rivas Costa 	int ret;
1218c164d6abSRodrigo Rivas Costa 
1219c164d6abSRodrigo Rivas Costa 	ret = hid_parse(hdev);
1220c164d6abSRodrigo Rivas Costa 	if (ret) {
1221c164d6abSRodrigo Rivas Costa 		hid_err(hdev,
1222c164d6abSRodrigo Rivas Costa 			"%s:parse of hid interface failed\n", __func__);
1223c164d6abSRodrigo Rivas Costa 		return ret;
1224c164d6abSRodrigo Rivas Costa 	}
1225c164d6abSRodrigo Rivas Costa 
1226c164d6abSRodrigo Rivas Costa 	/*
1227c164d6abSRodrigo Rivas Costa 	 * The virtual client_dev is only used for hidraw.
1228c164d6abSRodrigo Rivas Costa 	 * Also avoid the recursive probe.
1229c164d6abSRodrigo Rivas Costa 	 */
1230c164d6abSRodrigo Rivas Costa 	if (hdev->group == HID_GROUP_STEAM)
1231c164d6abSRodrigo Rivas Costa 		return hid_hw_start(hdev, HID_CONNECT_HIDRAW);
1232c164d6abSRodrigo Rivas Costa 	/*
1233c164d6abSRodrigo Rivas Costa 	 * The non-valve interfaces (mouse and keyboard emulation) are
1234c164d6abSRodrigo Rivas Costa 	 * connected without changes.
1235c164d6abSRodrigo Rivas Costa 	 */
1236c164d6abSRodrigo Rivas Costa 	if (!steam_is_valve_interface(hdev))
1237c164d6abSRodrigo Rivas Costa 		return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
1238c164d6abSRodrigo Rivas Costa 
1239c164d6abSRodrigo Rivas Costa 	steam = devm_kzalloc(&hdev->dev, sizeof(*steam), GFP_KERNEL);
12400c38fefeSDan Carpenter 	if (!steam)
12410c38fefeSDan Carpenter 		return -ENOMEM;
12420c38fefeSDan Carpenter 
1243c164d6abSRodrigo Rivas Costa 	steam->hdev = hdev;
1244c164d6abSRodrigo Rivas Costa 	hid_set_drvdata(hdev, steam);
1245c164d6abSRodrigo Rivas Costa 	spin_lock_init(&steam->lock);
12469544708cSVicki Pfau 	mutex_init(&steam->report_mutex);
1247c164d6abSRodrigo Rivas Costa 	steam->quirks = id->driver_data;
1248c164d6abSRodrigo Rivas Costa 	INIT_WORK(&steam->work_connect, steam_work_connect_cb);
1249c68606bdSVicki Pfau 	INIT_DELAYED_WORK(&steam->mode_switch, steam_mode_switch_cb);
12502d3f53a8SRodrigo Rivas Costa 	INIT_LIST_HEAD(&steam->list);
12519cd61c81SVicki Pfau 	INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
12524b996b61SMax Maisel 	steam->sensor_timestamp_us = 0;
1253e1147961SVicki Pfau 	INIT_WORK(&steam->unregister_work, steam_work_unregister_cb);
1254c164d6abSRodrigo Rivas Costa 
1255c164d6abSRodrigo Rivas Costa 	/*
1256c164d6abSRodrigo Rivas Costa 	 * With the real steam controller interface, do not connect hidraw.
1257c164d6abSRodrigo Rivas Costa 	 * Instead, create the client_hid and connect that.
1258c164d6abSRodrigo Rivas Costa 	 */
1259c164d6abSRodrigo Rivas Costa 	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDRAW);
1260c164d6abSRodrigo Rivas Costa 	if (ret)
12619f82ed6eSDan Carpenter 		goto err_cancel_work;
1262c164d6abSRodrigo Rivas Costa 
1263c164d6abSRodrigo Rivas Costa 	ret = hid_hw_open(hdev);
1264c164d6abSRodrigo Rivas Costa 	if (ret) {
1265c164d6abSRodrigo Rivas Costa 		hid_err(hdev,
1266cf28aee2SRodrigo Rivas Costa 			"%s:hid_hw_open\n",
1267c164d6abSRodrigo Rivas Costa 			__func__);
12689f82ed6eSDan Carpenter 		goto err_hw_stop;
1269c164d6abSRodrigo Rivas Costa 	}
1270cf28aee2SRodrigo Rivas Costa 
1271cf28aee2SRodrigo Rivas Costa 	if (steam->quirks & STEAM_QUIRK_WIRELESS) {
1272c164d6abSRodrigo Rivas Costa 		hid_info(hdev, "Steam wireless receiver connected");
127320eee6e5SRodrigo Rivas Costa 		/* If using a wireless adaptor ask for connection status */
127420eee6e5SRodrigo Rivas Costa 		steam->connected = false;
1275c164d6abSRodrigo Rivas Costa 		steam_request_conn_status(steam);
1276c164d6abSRodrigo Rivas Costa 	} else {
127720eee6e5SRodrigo Rivas Costa 		/* A wired connection is always present */
127820eee6e5SRodrigo Rivas Costa 		steam->connected = true;
1279c164d6abSRodrigo Rivas Costa 		ret = steam_register(steam);
1280c164d6abSRodrigo Rivas Costa 		if (ret) {
1281c164d6abSRodrigo Rivas Costa 			hid_err(hdev,
1282c164d6abSRodrigo Rivas Costa 				"%s:steam_register failed with error %d\n",
1283c164d6abSRodrigo Rivas Costa 				__func__, ret);
12849f82ed6eSDan Carpenter 			goto err_hw_close;
1285c164d6abSRodrigo Rivas Costa 		}
1286c164d6abSRodrigo Rivas Costa 	}
1287c164d6abSRodrigo Rivas Costa 
12889544708cSVicki Pfau 	steam->client_hdev = steam_create_client_hid(hdev);
12899544708cSVicki Pfau 	if (IS_ERR(steam->client_hdev)) {
12909544708cSVicki Pfau 		ret = PTR_ERR(steam->client_hdev);
12919f82ed6eSDan Carpenter 		goto err_stream_unregister;
12929544708cSVicki Pfau 	}
12939544708cSVicki Pfau 	steam->client_hdev->driver_data = steam;
12949544708cSVicki Pfau 
12959544708cSVicki Pfau 	ret = hid_add_device(steam->client_hdev);
12969544708cSVicki Pfau 	if (ret)
12979f82ed6eSDan Carpenter 		goto err_destroy;
12989544708cSVicki Pfau 
1299c164d6abSRodrigo Rivas Costa 	return 0;
1300c164d6abSRodrigo Rivas Costa 
13019f82ed6eSDan Carpenter err_destroy:
13029544708cSVicki Pfau 	hid_destroy_device(steam->client_hdev);
13039f82ed6eSDan Carpenter err_stream_unregister:
13049f82ed6eSDan Carpenter 	if (steam->connected)
13059f82ed6eSDan Carpenter 		steam_unregister(steam);
13069f82ed6eSDan Carpenter err_hw_close:
13079f82ed6eSDan Carpenter 	hid_hw_close(hdev);
13089f82ed6eSDan Carpenter err_hw_stop:
13099f82ed6eSDan Carpenter 	hid_hw_stop(hdev);
13109f82ed6eSDan Carpenter err_cancel_work:
1311c164d6abSRodrigo Rivas Costa 	cancel_work_sync(&steam->work_connect);
1312c68606bdSVicki Pfau 	cancel_delayed_work_sync(&steam->mode_switch);
13139cd61c81SVicki Pfau 	cancel_work_sync(&steam->rumble_work);
1314e1147961SVicki Pfau 	cancel_work_sync(&steam->unregister_work);
13159f82ed6eSDan Carpenter 
1316c164d6abSRodrigo Rivas Costa 	return ret;
1317c164d6abSRodrigo Rivas Costa }
1318c164d6abSRodrigo Rivas Costa 
steam_remove(struct hid_device * hdev)1319c164d6abSRodrigo Rivas Costa static void steam_remove(struct hid_device *hdev)
1320c164d6abSRodrigo Rivas Costa {
1321c164d6abSRodrigo Rivas Costa 	struct steam_device *steam = hid_get_drvdata(hdev);
1322c164d6abSRodrigo Rivas Costa 
1323c164d6abSRodrigo Rivas Costa 	if (!steam || hdev->group == HID_GROUP_STEAM) {
1324c164d6abSRodrigo Rivas Costa 		hid_hw_stop(hdev);
1325c164d6abSRodrigo Rivas Costa 		return;
1326c164d6abSRodrigo Rivas Costa 	}
1327c164d6abSRodrigo Rivas Costa 
1328*026714ecSVicki Pfau 	hid_destroy_device(steam->client_hdev);
1329c68606bdSVicki Pfau 	cancel_delayed_work_sync(&steam->mode_switch);
13309544708cSVicki Pfau 	cancel_work_sync(&steam->work_connect);
1331d6187df3SVicki Pfau 	cancel_work_sync(&steam->rumble_work);
1332e1147961SVicki Pfau 	cancel_work_sync(&steam->unregister_work);
13339ba9498bSVicki Pfau 	steam->client_hdev = NULL;
1334c164d6abSRodrigo Rivas Costa 	steam->client_opened = false;
1335c164d6abSRodrigo Rivas Costa 	if (steam->quirks & STEAM_QUIRK_WIRELESS) {
1336c164d6abSRodrigo Rivas Costa 		hid_info(hdev, "Steam wireless receiver disconnected");
1337c164d6abSRodrigo Rivas Costa 	}
1338cf28aee2SRodrigo Rivas Costa 	hid_hw_close(hdev);
1339c164d6abSRodrigo Rivas Costa 	hid_hw_stop(hdev);
1340c164d6abSRodrigo Rivas Costa 	steam_unregister(steam);
1341c164d6abSRodrigo Rivas Costa }
1342c164d6abSRodrigo Rivas Costa 
steam_do_connect_event(struct steam_device * steam,bool connected)1343c164d6abSRodrigo Rivas Costa static void steam_do_connect_event(struct steam_device *steam, bool connected)
1344c164d6abSRodrigo Rivas Costa {
1345c164d6abSRodrigo Rivas Costa 	unsigned long flags;
1346385a4886SRodrigo Rivas Costa 	bool changed;
1347c164d6abSRodrigo Rivas Costa 
1348c164d6abSRodrigo Rivas Costa 	spin_lock_irqsave(&steam->lock, flags);
1349385a4886SRodrigo Rivas Costa 	changed = steam->connected != connected;
1350c164d6abSRodrigo Rivas Costa 	steam->connected = connected;
1351c164d6abSRodrigo Rivas Costa 	spin_unlock_irqrestore(&steam->lock, flags);
1352c164d6abSRodrigo Rivas Costa 
1353385a4886SRodrigo Rivas Costa 	if (changed && schedule_work(&steam->work_connect) == 0)
1354c164d6abSRodrigo Rivas Costa 		dbg_hid("%s: connected=%d event already queued\n",
1355c164d6abSRodrigo Rivas Costa 				__func__, connected);
1356c164d6abSRodrigo Rivas Costa }
1357c164d6abSRodrigo Rivas Costa 
1358c164d6abSRodrigo Rivas Costa /*
1359c164d6abSRodrigo Rivas Costa  * Some input data in the protocol has the opposite sign.
1360c164d6abSRodrigo Rivas Costa  * Clamp the values to 32767..-32767 so that the range is
1361c164d6abSRodrigo Rivas Costa  * symmetrical and can be negated safely.
1362c164d6abSRodrigo Rivas Costa  */
steam_le16(u8 * data)1363c164d6abSRodrigo Rivas Costa static inline s16 steam_le16(u8 *data)
1364c164d6abSRodrigo Rivas Costa {
1365c164d6abSRodrigo Rivas Costa 	s16 x = (s16) le16_to_cpup((__le16 *)data);
1366c164d6abSRodrigo Rivas Costa 
1367c164d6abSRodrigo Rivas Costa 	return x == -32768 ? -32767 : x;
1368c164d6abSRodrigo Rivas Costa }
1369c164d6abSRodrigo Rivas Costa 
1370c164d6abSRodrigo Rivas Costa /*
1371c164d6abSRodrigo Rivas Costa  * The size for this message payload is 60.
1372c164d6abSRodrigo Rivas Costa  * The known values are:
1373c164d6abSRodrigo Rivas Costa  *  (* values are not sent through wireless)
1374c164d6abSRodrigo Rivas Costa  *  (* accelerator/gyro is disabled by default)
1375c164d6abSRodrigo Rivas Costa  *  Offset| Type  | Mapped to |Meaning
1376c164d6abSRodrigo Rivas Costa  * -------+-------+-----------+--------------------------
1377c164d6abSRodrigo Rivas Costa  *  4-7   | u32   | --        | sequence number
1378c164d6abSRodrigo Rivas Costa  *  8-10  | 24bit | see below | buttons
1379c164d6abSRodrigo Rivas Costa  *  11    | u8    | ABS_HAT2Y | left trigger
1380c164d6abSRodrigo Rivas Costa  *  12    | u8    | ABS_HAT2X | right trigger
1381c164d6abSRodrigo Rivas Costa  *  13-15 | --    | --        | always 0
1382c164d6abSRodrigo Rivas Costa  *  16-17 | s16   | ABS_X/ABS_HAT0X     | X value
1383c164d6abSRodrigo Rivas Costa  *  18-19 | s16   | ABS_Y/ABS_HAT0Y     | Y value
1384c164d6abSRodrigo Rivas Costa  *  20-21 | s16   | ABS_RX    | right-pad X value
1385c164d6abSRodrigo Rivas Costa  *  22-23 | s16   | ABS_RY    | right-pad Y value
1386c164d6abSRodrigo Rivas Costa  *  24-25 | s16   | --        | * left trigger
1387c164d6abSRodrigo Rivas Costa  *  26-27 | s16   | --        | * right trigger
1388c164d6abSRodrigo Rivas Costa  *  28-29 | s16   | --        | * accelerometer X value
1389c164d6abSRodrigo Rivas Costa  *  30-31 | s16   | --        | * accelerometer Y value
1390c164d6abSRodrigo Rivas Costa  *  32-33 | s16   | --        | * accelerometer Z value
1391c164d6abSRodrigo Rivas Costa  *  34-35 | s16   | --        | gyro X value
1392c164d6abSRodrigo Rivas Costa  *  36-36 | s16   | --        | gyro Y value
1393c164d6abSRodrigo Rivas Costa  *  38-39 | s16   | --        | gyro Z value
1394c164d6abSRodrigo Rivas Costa  *  40-41 | s16   | --        | quaternion W value
1395c164d6abSRodrigo Rivas Costa  *  42-43 | s16   | --        | quaternion X value
1396c164d6abSRodrigo Rivas Costa  *  44-45 | s16   | --        | quaternion Y value
1397c164d6abSRodrigo Rivas Costa  *  46-47 | s16   | --        | quaternion Z value
1398c164d6abSRodrigo Rivas Costa  *  48-49 | --    | --        | always 0
1399c164d6abSRodrigo Rivas Costa  *  50-51 | s16   | --        | * left trigger (uncalibrated)
1400c164d6abSRodrigo Rivas Costa  *  52-53 | s16   | --        | * right trigger (uncalibrated)
1401c164d6abSRodrigo Rivas Costa  *  54-55 | s16   | --        | * joystick X value (uncalibrated)
1402c164d6abSRodrigo Rivas Costa  *  56-57 | s16   | --        | * joystick Y value (uncalibrated)
1403c164d6abSRodrigo Rivas Costa  *  58-59 | s16   | --        | * left-pad X value
1404c164d6abSRodrigo Rivas Costa  *  60-61 | s16   | --        | * left-pad Y value
1405c164d6abSRodrigo Rivas Costa  *  62-63 | u16   | --        | * battery voltage
1406c164d6abSRodrigo Rivas Costa  *
1407c164d6abSRodrigo Rivas Costa  * The buttons are:
1408c164d6abSRodrigo Rivas Costa  *  Bit  | Mapped to  | Description
1409c164d6abSRodrigo Rivas Costa  * ------+------------+--------------------------------
1410c164d6abSRodrigo Rivas Costa  *  8.0  | BTN_TR2    | right trigger fully pressed
1411c164d6abSRodrigo Rivas Costa  *  8.1  | BTN_TL2    | left trigger fully pressed
1412c164d6abSRodrigo Rivas Costa  *  8.2  | BTN_TR     | right shoulder
1413c164d6abSRodrigo Rivas Costa  *  8.3  | BTN_TL     | left shoulder
1414c164d6abSRodrigo Rivas Costa  *  8.4  | BTN_Y      | button Y
1415c164d6abSRodrigo Rivas Costa  *  8.5  | BTN_B      | button B
1416c164d6abSRodrigo Rivas Costa  *  8.6  | BTN_X      | button X
1417c164d6abSRodrigo Rivas Costa  *  8.7  | BTN_A      | button A
14189ba9498bSVicki Pfau  *  9.0  | BTN_DPAD_UP    | left-pad up
14199ba9498bSVicki Pfau  *  9.1  | BTN_DPAD_RIGHT | left-pad right
14209ba9498bSVicki Pfau  *  9.2  | BTN_DPAD_LEFT  | left-pad left
14219ba9498bSVicki Pfau  *  9.3  | BTN_DPAD_DOWN  | left-pad down
1422c164d6abSRodrigo Rivas Costa  *  9.4  | BTN_SELECT | menu left
1423c164d6abSRodrigo Rivas Costa  *  9.5  | BTN_MODE   | steam logo
1424c164d6abSRodrigo Rivas Costa  *  9.6  | BTN_START  | menu right
1425c164d6abSRodrigo Rivas Costa  *  9.7  | BTN_GEAR_DOWN | left back lever
1426c164d6abSRodrigo Rivas Costa  * 10.0  | BTN_GEAR_UP   | right back lever
1427c164d6abSRodrigo Rivas Costa  * 10.1  | --         | left-pad clicked
1428c164d6abSRodrigo Rivas Costa  * 10.2  | BTN_THUMBR | right-pad clicked
1429c164d6abSRodrigo Rivas Costa  * 10.3  | BTN_THUMB  | left-pad touched (but see explanation below)
1430c164d6abSRodrigo Rivas Costa  * 10.4  | BTN_THUMB2 | right-pad touched
1431c164d6abSRodrigo Rivas Costa  * 10.5  | --         | unknown
1432c164d6abSRodrigo Rivas Costa  * 10.6  | BTN_THUMBL | joystick clicked
1433c164d6abSRodrigo Rivas Costa  * 10.7  | --         | lpad_and_joy
1434c164d6abSRodrigo Rivas Costa  */
1435c164d6abSRodrigo Rivas Costa 
steam_do_input_event(struct steam_device * steam,struct input_dev * input,u8 * data)1436c164d6abSRodrigo Rivas Costa static void steam_do_input_event(struct steam_device *steam,
1437c164d6abSRodrigo Rivas Costa 		struct input_dev *input, u8 *data)
1438c164d6abSRodrigo Rivas Costa {
1439c164d6abSRodrigo Rivas Costa 	/* 24 bits of buttons */
1440c164d6abSRodrigo Rivas Costa 	u8 b8, b9, b10;
1441c164d6abSRodrigo Rivas Costa 	s16 x, y;
1442c164d6abSRodrigo Rivas Costa 	bool lpad_touched, lpad_and_joy;
1443c164d6abSRodrigo Rivas Costa 
1444c164d6abSRodrigo Rivas Costa 	b8 = data[8];
1445c164d6abSRodrigo Rivas Costa 	b9 = data[9];
1446c164d6abSRodrigo Rivas Costa 	b10 = data[10];
1447c164d6abSRodrigo Rivas Costa 
1448c164d6abSRodrigo Rivas Costa 	input_report_abs(input, ABS_HAT2Y, data[11]);
1449c164d6abSRodrigo Rivas Costa 	input_report_abs(input, ABS_HAT2X, data[12]);
1450c164d6abSRodrigo Rivas Costa 
1451c164d6abSRodrigo Rivas Costa 	/*
1452c164d6abSRodrigo Rivas Costa 	 * These two bits tells how to interpret the values X and Y.
1453c164d6abSRodrigo Rivas Costa 	 * lpad_and_joy tells that the joystick and the lpad are used at the
1454c164d6abSRodrigo Rivas Costa 	 * same time.
1455c164d6abSRodrigo Rivas Costa 	 * lpad_touched tells whether X/Y are to be read as lpad coord or
1456c164d6abSRodrigo Rivas Costa 	 * joystick values.
1457c164d6abSRodrigo Rivas Costa 	 * (lpad_touched || lpad_and_joy) tells if the lpad is really touched.
1458c164d6abSRodrigo Rivas Costa 	 */
1459c164d6abSRodrigo Rivas Costa 	lpad_touched = b10 & BIT(3);
1460c164d6abSRodrigo Rivas Costa 	lpad_and_joy = b10 & BIT(7);
1461c164d6abSRodrigo Rivas Costa 	x = steam_le16(data + 16);
1462c164d6abSRodrigo Rivas Costa 	y = -steam_le16(data + 18);
1463c164d6abSRodrigo Rivas Costa 
1464c164d6abSRodrigo Rivas Costa 	input_report_abs(input, lpad_touched ? ABS_HAT0X : ABS_X, x);
1465c164d6abSRodrigo Rivas Costa 	input_report_abs(input, lpad_touched ? ABS_HAT0Y : ABS_Y, y);
1466c164d6abSRodrigo Rivas Costa 	/* Check if joystick is centered */
1467c164d6abSRodrigo Rivas Costa 	if (lpad_touched && !lpad_and_joy) {
1468c164d6abSRodrigo Rivas Costa 		input_report_abs(input, ABS_X, 0);
1469c164d6abSRodrigo Rivas Costa 		input_report_abs(input, ABS_Y, 0);
1470c164d6abSRodrigo Rivas Costa 	}
1471c164d6abSRodrigo Rivas Costa 	/* Check if lpad is untouched */
1472c164d6abSRodrigo Rivas Costa 	if (!(lpad_touched || lpad_and_joy)) {
1473c164d6abSRodrigo Rivas Costa 		input_report_abs(input, ABS_HAT0X, 0);
1474c164d6abSRodrigo Rivas Costa 		input_report_abs(input, ABS_HAT0Y, 0);
1475c164d6abSRodrigo Rivas Costa 	}
1476c164d6abSRodrigo Rivas Costa 
1477c164d6abSRodrigo Rivas Costa 	input_report_abs(input, ABS_RX, steam_le16(data + 20));
1478c164d6abSRodrigo Rivas Costa 	input_report_abs(input, ABS_RY, -steam_le16(data + 22));
1479c164d6abSRodrigo Rivas Costa 
1480c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_TR2, !!(b8 & BIT(0)));
1481c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_TL2, !!(b8 & BIT(1)));
1482c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_TR, !!(b8 & BIT(2)));
1483c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_TL, !!(b8 & BIT(3)));
1484c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_Y, !!(b8 & BIT(4)));
1485c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_B, !!(b8 & BIT(5)));
1486c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_X, !!(b8 & BIT(6)));
1487c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_A, !!(b8 & BIT(7)));
1488c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_SELECT, !!(b9 & BIT(4)));
1489c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_MODE, !!(b9 & BIT(5)));
1490c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_START, !!(b9 & BIT(6)));
1491c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_GEAR_DOWN, !!(b9 & BIT(7)));
1492c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_GEAR_UP, !!(b10 & BIT(0)));
1493c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_THUMBR, !!(b10 & BIT(2)));
1494c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_THUMBL, !!(b10 & BIT(6)));
1495c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_THUMB, lpad_touched || lpad_and_joy);
1496c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_THUMB2, !!(b10 & BIT(4)));
1497c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_DPAD_UP, !!(b9 & BIT(0)));
1498c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_DPAD_RIGHT, !!(b9 & BIT(1)));
1499c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_DPAD_LEFT, !!(b9 & BIT(2)));
1500c164d6abSRodrigo Rivas Costa 	input_event(input, EV_KEY, BTN_DPAD_DOWN, !!(b9 & BIT(3)));
1501c164d6abSRodrigo Rivas Costa 
1502c164d6abSRodrigo Rivas Costa 	input_sync(input);
1503c164d6abSRodrigo Rivas Costa }
1504c164d6abSRodrigo Rivas Costa 
1505f8271979SRodrigo Rivas Costa /*
15069ba9498bSVicki Pfau  * The size for this message payload is 56.
15079ba9498bSVicki Pfau  * The known values are:
15089ba9498bSVicki Pfau  *  Offset| Type  | Mapped to |Meaning
15099ba9498bSVicki Pfau  * -------+-------+-----------+--------------------------
15109ba9498bSVicki Pfau  *  4-7   | u32   | --        | sequence number
15119ba9498bSVicki Pfau  *  8-15  | u64   | see below | buttons
15129ba9498bSVicki Pfau  *  16-17 | s16   | ABS_HAT0X | left-pad X value
15139ba9498bSVicki Pfau  *  18-19 | s16   | ABS_HAT0Y | left-pad Y value
15149ba9498bSVicki Pfau  *  20-21 | s16   | ABS_HAT1X | right-pad X value
15159ba9498bSVicki Pfau  *  22-23 | s16   | ABS_HAT1Y | right-pad Y value
15164b996b61SMax Maisel  *  24-25 | s16   | IMU ABS_X | accelerometer X value
15174b996b61SMax Maisel  *  26-27 | s16   | IMU ABS_Z | accelerometer Y value
15184b996b61SMax Maisel  *  28-29 | s16   | IMU ABS_Y | accelerometer Z value
15194b996b61SMax Maisel  *  30-31 | s16   | IMU ABS_RX | gyro X value
15204b996b61SMax Maisel  *  32-33 | s16   | IMU ABS_RZ | gyro Y value
15214b996b61SMax Maisel  *  34-35 | s16   | IMU ABS_RY | gyro Z value
15229ba9498bSVicki Pfau  *  36-37 | s16   | --        | quaternion W value
15239ba9498bSVicki Pfau  *  38-39 | s16   | --        | quaternion X value
15249ba9498bSVicki Pfau  *  40-41 | s16   | --        | quaternion Y value
15259ba9498bSVicki Pfau  *  42-43 | s16   | --        | quaternion Z value
15269ba9498bSVicki Pfau  *  44-45 | u16   | ABS_HAT2Y | left trigger (uncalibrated)
15279ba9498bSVicki Pfau  *  46-47 | u16   | ABS_HAT2X | right trigger (uncalibrated)
15289ba9498bSVicki Pfau  *  48-49 | s16   | ABS_X     | left joystick X
15299ba9498bSVicki Pfau  *  50-51 | s16   | ABS_Y     | left joystick Y
15309ba9498bSVicki Pfau  *  52-53 | s16   | ABS_RX    | right joystick X
15319ba9498bSVicki Pfau  *  54-55 | s16   | ABS_RY    | right joystick Y
15329ba9498bSVicki Pfau  *  56-57 | u16   | --        | left pad pressure
15339ba9498bSVicki Pfau  *  58-59 | u16   | --        | right pad pressure
15349ba9498bSVicki Pfau  *
15359ba9498bSVicki Pfau  * The buttons are:
15369ba9498bSVicki Pfau  *  Bit  | Mapped to  | Description
15379ba9498bSVicki Pfau  * ------+------------+--------------------------------
15389ba9498bSVicki Pfau  *  8.0  | BTN_TR2    | right trigger fully pressed
15399ba9498bSVicki Pfau  *  8.1  | BTN_TL2    | left trigger fully pressed
15409ba9498bSVicki Pfau  *  8.2  | BTN_TR     | right shoulder
15419ba9498bSVicki Pfau  *  8.3  | BTN_TL     | left shoulder
15429ba9498bSVicki Pfau  *  8.4  | BTN_Y      | button Y
15439ba9498bSVicki Pfau  *  8.5  | BTN_B      | button B
15449ba9498bSVicki Pfau  *  8.6  | BTN_X      | button X
15459ba9498bSVicki Pfau  *  8.7  | BTN_A      | button A
15469ba9498bSVicki Pfau  *  9.0  | BTN_DPAD_UP    | left-pad up
15479ba9498bSVicki Pfau  *  9.1  | BTN_DPAD_RIGHT | left-pad right
15489ba9498bSVicki Pfau  *  9.2  | BTN_DPAD_LEFT  | left-pad left
15499ba9498bSVicki Pfau  *  9.3  | BTN_DPAD_DOWN  | left-pad down
15509ba9498bSVicki Pfau  *  9.4  | BTN_SELECT | menu left
15519ba9498bSVicki Pfau  *  9.5  | BTN_MODE   | steam logo
15529ba9498bSVicki Pfau  *  9.6  | BTN_START  | menu right
15539ba9498bSVicki Pfau  *  9.7  | BTN_TRIGGER_HAPPY3 | left bottom grip button
15549ba9498bSVicki Pfau  *  10.0 | BTN_TRIGGER_HAPPY4 | right bottom grip button
15559ba9498bSVicki Pfau  *  10.1 | BTN_THUMB  | left pad pressed
15569ba9498bSVicki Pfau  *  10.2 | BTN_THUMB2 | right pad pressed
15579ba9498bSVicki Pfau  *  10.3 | --         | left pad touched
15589ba9498bSVicki Pfau  *  10.4 | --         | right pad touched
15599ba9498bSVicki Pfau  *  10.5 | --         | unknown
15609ba9498bSVicki Pfau  *  10.6 | BTN_THUMBL | left joystick clicked
15619ba9498bSVicki Pfau  *  10.7 | --         | unknown
15629ba9498bSVicki Pfau  *  11.0 | --         | unknown
15639ba9498bSVicki Pfau  *  11.1 | --         | unknown
15649ba9498bSVicki Pfau  *  11.2 | BTN_THUMBR | right joystick clicked
15659ba9498bSVicki Pfau  *  11.3 | --         | unknown
15669ba9498bSVicki Pfau  *  11.4 | --         | unknown
15679ba9498bSVicki Pfau  *  11.5 | --         | unknown
15689ba9498bSVicki Pfau  *  11.6 | --         | unknown
15699ba9498bSVicki Pfau  *  11.7 | --         | unknown
15709ba9498bSVicki Pfau  *  12.0 | --         | unknown
15719ba9498bSVicki Pfau  *  12.1 | --         | unknown
15729ba9498bSVicki Pfau  *  12.2 | --         | unknown
15739ba9498bSVicki Pfau  *  12.3 | --         | unknown
15749ba9498bSVicki Pfau  *  12.4 | --         | unknown
15759ba9498bSVicki Pfau  *  12.5 | --         | unknown
15769ba9498bSVicki Pfau  *  12.6 | --         | unknown
15779ba9498bSVicki Pfau  *  12.7 | --         | unknown
15789ba9498bSVicki Pfau  *  13.0 | --         | unknown
15799ba9498bSVicki Pfau  *  13.1 | BTN_TRIGGER_HAPPY1 | left top grip button
15809ba9498bSVicki Pfau  *  13.2 | BTN_TRIGGER_HAPPY2 | right top grip button
15819ba9498bSVicki Pfau  *  13.3 | --         | unknown
15829ba9498bSVicki Pfau  *  13.4 | --         | unknown
15839ba9498bSVicki Pfau  *  13.5 | --         | unknown
15849ba9498bSVicki Pfau  *  13.6 | --         | left joystick touched
15859ba9498bSVicki Pfau  *  13.7 | --         | right joystick touched
15869ba9498bSVicki Pfau  *  14.0 | --         | unknown
15879ba9498bSVicki Pfau  *  14.1 | --         | unknown
15889ba9498bSVicki Pfau  *  14.2 | BTN_BASE   | quick access button
15899ba9498bSVicki Pfau  *  14.3 | --         | unknown
15909ba9498bSVicki Pfau  *  14.4 | --         | unknown
15919ba9498bSVicki Pfau  *  14.5 | --         | unknown
15929ba9498bSVicki Pfau  *  14.6 | --         | unknown
15939ba9498bSVicki Pfau  *  14.7 | --         | unknown
15949ba9498bSVicki Pfau  *  15.0 | --         | unknown
15959ba9498bSVicki Pfau  *  15.1 | --         | unknown
15969ba9498bSVicki Pfau  *  15.2 | --         | unknown
15979ba9498bSVicki Pfau  *  15.3 | --         | unknown
15989ba9498bSVicki Pfau  *  15.4 | --         | unknown
15999ba9498bSVicki Pfau  *  15.5 | --         | unknown
16009ba9498bSVicki Pfau  *  15.6 | --         | unknown
16019ba9498bSVicki Pfau  *  15.7 | --         | unknown
16029ba9498bSVicki Pfau  */
steam_do_deck_input_event(struct steam_device * steam,struct input_dev * input,u8 * data)16039ba9498bSVicki Pfau static void steam_do_deck_input_event(struct steam_device *steam,
16049ba9498bSVicki Pfau 		struct input_dev *input, u8 *data)
16059ba9498bSVicki Pfau {
16069ba9498bSVicki Pfau 	u8 b8, b9, b10, b11, b13, b14;
16079ba9498bSVicki Pfau 	bool lpad_touched, rpad_touched;
16089ba9498bSVicki Pfau 
16099ba9498bSVicki Pfau 	b8 = data[8];
16109ba9498bSVicki Pfau 	b9 = data[9];
16119ba9498bSVicki Pfau 	b10 = data[10];
16129ba9498bSVicki Pfau 	b11 = data[11];
16139ba9498bSVicki Pfau 	b13 = data[13];
16149ba9498bSVicki Pfau 	b14 = data[14];
16159ba9498bSVicki Pfau 
1616c68606bdSVicki Pfau 	if (!(b9 & BIT(6)) && steam->did_mode_switch) {
1617c68606bdSVicki Pfau 		steam->did_mode_switch = false;
16184c8f958aSVicki Pfau 		cancel_delayed_work(&steam->mode_switch);
1619c68606bdSVicki Pfau 	} else if (!steam->client_opened && (b9 & BIT(6)) && !steam->did_mode_switch) {
1620c68606bdSVicki Pfau 		steam->did_mode_switch = true;
1621c68606bdSVicki Pfau 		schedule_delayed_work(&steam->mode_switch, 45 * HZ / 100);
1622c68606bdSVicki Pfau 	}
1623c68606bdSVicki Pfau 
1624c68606bdSVicki Pfau 	if (!steam->gamepad_mode)
1625c68606bdSVicki Pfau 		return;
1626c68606bdSVicki Pfau 
16279ba9498bSVicki Pfau 	lpad_touched = b10 & BIT(3);
16289ba9498bSVicki Pfau 	rpad_touched = b10 & BIT(4);
16299ba9498bSVicki Pfau 
16309ba9498bSVicki Pfau 	if (lpad_touched) {
16319ba9498bSVicki Pfau 		input_report_abs(input, ABS_HAT0X, steam_le16(data + 16));
16329ba9498bSVicki Pfau 		input_report_abs(input, ABS_HAT0Y, steam_le16(data + 18));
16339ba9498bSVicki Pfau 	} else {
16349ba9498bSVicki Pfau 		input_report_abs(input, ABS_HAT0X, 0);
16359ba9498bSVicki Pfau 		input_report_abs(input, ABS_HAT0Y, 0);
16369ba9498bSVicki Pfau 	}
16379ba9498bSVicki Pfau 
16389ba9498bSVicki Pfau 	if (rpad_touched) {
16399ba9498bSVicki Pfau 		input_report_abs(input, ABS_HAT1X, steam_le16(data + 20));
16409ba9498bSVicki Pfau 		input_report_abs(input, ABS_HAT1Y, steam_le16(data + 22));
16419ba9498bSVicki Pfau 	} else {
16429ba9498bSVicki Pfau 		input_report_abs(input, ABS_HAT1X, 0);
16439ba9498bSVicki Pfau 		input_report_abs(input, ABS_HAT1Y, 0);
16449ba9498bSVicki Pfau 	}
16459ba9498bSVicki Pfau 
16469ba9498bSVicki Pfau 	input_report_abs(input, ABS_X, steam_le16(data + 48));
16479ba9498bSVicki Pfau 	input_report_abs(input, ABS_Y, -steam_le16(data + 50));
16489ba9498bSVicki Pfau 	input_report_abs(input, ABS_RX, steam_le16(data + 52));
16499ba9498bSVicki Pfau 	input_report_abs(input, ABS_RY, -steam_le16(data + 54));
16509ba9498bSVicki Pfau 
16519ba9498bSVicki Pfau 	input_report_abs(input, ABS_HAT2Y, steam_le16(data + 44));
16529ba9498bSVicki Pfau 	input_report_abs(input, ABS_HAT2X, steam_le16(data + 46));
16539ba9498bSVicki Pfau 
16549ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_TR2, !!(b8 & BIT(0)));
16559ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_TL2, !!(b8 & BIT(1)));
16569ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_TR, !!(b8 & BIT(2)));
16579ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_TL, !!(b8 & BIT(3)));
16589ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_Y, !!(b8 & BIT(4)));
16599ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_B, !!(b8 & BIT(5)));
16609ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_X, !!(b8 & BIT(6)));
16619ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_A, !!(b8 & BIT(7)));
16629ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_SELECT, !!(b9 & BIT(4)));
16639ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_MODE, !!(b9 & BIT(5)));
16649ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_START, !!(b9 & BIT(6)));
16659ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_TRIGGER_HAPPY3, !!(b9 & BIT(7)));
16669ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_TRIGGER_HAPPY4, !!(b10 & BIT(0)));
16679ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_THUMBL, !!(b10 & BIT(6)));
16689ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_THUMBR, !!(b11 & BIT(2)));
16699ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_DPAD_UP, !!(b9 & BIT(0)));
16709ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_DPAD_RIGHT, !!(b9 & BIT(1)));
16719ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_DPAD_LEFT, !!(b9 & BIT(2)));
16729ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_DPAD_DOWN, !!(b9 & BIT(3)));
16739ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_THUMB, !!(b10 & BIT(1)));
16749ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_THUMB2, !!(b10 & BIT(2)));
16759ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_TRIGGER_HAPPY1, !!(b13 & BIT(1)));
16769ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_TRIGGER_HAPPY2, !!(b13 & BIT(2)));
16779ba9498bSVicki Pfau 	input_event(input, EV_KEY, BTN_BASE, !!(b14 & BIT(2)));
16789ba9498bSVicki Pfau 
16799ba9498bSVicki Pfau 	input_sync(input);
16809ba9498bSVicki Pfau }
16819ba9498bSVicki Pfau 
steam_do_deck_sensors_event(struct steam_device * steam,struct input_dev * sensors,u8 * data)16824b996b61SMax Maisel static void steam_do_deck_sensors_event(struct steam_device *steam,
16834b996b61SMax Maisel 		struct input_dev *sensors, u8 *data)
16844b996b61SMax Maisel {
16854b996b61SMax Maisel 	/*
16864b996b61SMax Maisel 	 * The deck input report is received every 4 ms on average,
16874b996b61SMax Maisel 	 * with a jitter of +/- 4 ms even though the USB descriptor claims
16884b996b61SMax Maisel 	 * that it uses 1 kHz.
16894b996b61SMax Maisel 	 * Since the HID report does not include a sensor timestamp,
16904b996b61SMax Maisel 	 * use a fixed increment here.
16914b996b61SMax Maisel 	 */
16924b996b61SMax Maisel 	steam->sensor_timestamp_us += 4000;
16934b996b61SMax Maisel 
16944b996b61SMax Maisel 	if (!steam->gamepad_mode)
16954b996b61SMax Maisel 		return;
16964b996b61SMax Maisel 
16974b996b61SMax Maisel 	input_event(sensors, EV_MSC, MSC_TIMESTAMP, steam->sensor_timestamp_us);
16984b996b61SMax Maisel 	input_report_abs(sensors, ABS_X, steam_le16(data + 24));
16994b996b61SMax Maisel 	input_report_abs(sensors, ABS_Z, -steam_le16(data + 26));
17004b996b61SMax Maisel 	input_report_abs(sensors, ABS_Y, steam_le16(data + 28));
17014b996b61SMax Maisel 	input_report_abs(sensors, ABS_RX, steam_le16(data + 30));
17024b996b61SMax Maisel 	input_report_abs(sensors, ABS_RZ, -steam_le16(data + 32));
17034b996b61SMax Maisel 	input_report_abs(sensors, ABS_RY, steam_le16(data + 34));
17044b996b61SMax Maisel 
17054b996b61SMax Maisel 	input_sync(sensors);
17064b996b61SMax Maisel }
17074b996b61SMax Maisel 
17089ba9498bSVicki Pfau /*
1709f8271979SRodrigo Rivas Costa  * The size for this message payload is 11.
1710f8271979SRodrigo Rivas Costa  * The known values are:
1711f8271979SRodrigo Rivas Costa  *  Offset| Type  | Meaning
1712f8271979SRodrigo Rivas Costa  * -------+-------+---------------------------
1713f8271979SRodrigo Rivas Costa  *  4-7   | u32   | sequence number
1714f8271979SRodrigo Rivas Costa  *  8-11  | --    | always 0
1715f8271979SRodrigo Rivas Costa  *  12-13 | u16   | voltage (mV)
1716f8271979SRodrigo Rivas Costa  *  14    | u8    | battery percent
1717f8271979SRodrigo Rivas Costa  */
steam_do_battery_event(struct steam_device * steam,struct power_supply * battery,u8 * data)1718f8271979SRodrigo Rivas Costa static void steam_do_battery_event(struct steam_device *steam,
1719f8271979SRodrigo Rivas Costa 		struct power_supply *battery, u8 *data)
1720f8271979SRodrigo Rivas Costa {
1721f8271979SRodrigo Rivas Costa 	unsigned long flags;
1722f8271979SRodrigo Rivas Costa 
1723f8271979SRodrigo Rivas Costa 	s16 volts = steam_le16(data + 12);
1724f8271979SRodrigo Rivas Costa 	u8 batt = data[14];
1725f8271979SRodrigo Rivas Costa 
1726f8271979SRodrigo Rivas Costa 	/* Creating the battery may have failed */
1727f8271979SRodrigo Rivas Costa 	rcu_read_lock();
1728f8271979SRodrigo Rivas Costa 	battery = rcu_dereference(steam->battery);
1729f8271979SRodrigo Rivas Costa 	if (likely(battery)) {
1730f8271979SRodrigo Rivas Costa 		spin_lock_irqsave(&steam->lock, flags);
1731f8271979SRodrigo Rivas Costa 		steam->voltage = volts;
1732f8271979SRodrigo Rivas Costa 		steam->battery_charge = batt;
1733f8271979SRodrigo Rivas Costa 		spin_unlock_irqrestore(&steam->lock, flags);
1734f8271979SRodrigo Rivas Costa 		power_supply_changed(battery);
1735f8271979SRodrigo Rivas Costa 	}
1736f8271979SRodrigo Rivas Costa 	rcu_read_unlock();
1737f8271979SRodrigo Rivas Costa }
1738f8271979SRodrigo Rivas Costa 
steam_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)1739c164d6abSRodrigo Rivas Costa static int steam_raw_event(struct hid_device *hdev,
1740c164d6abSRodrigo Rivas Costa 			struct hid_report *report, u8 *data,
1741c164d6abSRodrigo Rivas Costa 			int size)
1742c164d6abSRodrigo Rivas Costa {
1743c164d6abSRodrigo Rivas Costa 	struct steam_device *steam = hid_get_drvdata(hdev);
1744c164d6abSRodrigo Rivas Costa 	struct input_dev *input;
17454b996b61SMax Maisel 	struct input_dev *sensors;
1746f8271979SRodrigo Rivas Costa 	struct power_supply *battery;
1747c164d6abSRodrigo Rivas Costa 
1748c164d6abSRodrigo Rivas Costa 	if (!steam)
1749c164d6abSRodrigo Rivas Costa 		return 0;
1750c164d6abSRodrigo Rivas Costa 
1751c164d6abSRodrigo Rivas Costa 	if (steam->client_opened)
1752c164d6abSRodrigo Rivas Costa 		hid_input_report(steam->client_hdev, HID_FEATURE_REPORT,
1753c164d6abSRodrigo Rivas Costa 				data, size, 0);
1754c164d6abSRodrigo Rivas Costa 	/*
1755c164d6abSRodrigo Rivas Costa 	 * All messages are size=64, all values little-endian.
1756c164d6abSRodrigo Rivas Costa 	 * The format is:
1757c164d6abSRodrigo Rivas Costa 	 *  Offset| Meaning
1758c164d6abSRodrigo Rivas Costa 	 * -------+--------------------------------------------
1759c164d6abSRodrigo Rivas Costa 	 *  0-1   | always 0x01, 0x00, maybe protocol version?
1760c164d6abSRodrigo Rivas Costa 	 *  2     | type of message
1761c164d6abSRodrigo Rivas Costa 	 *  3     | length of the real payload (not checked)
1762c164d6abSRodrigo Rivas Costa 	 *  4-n   | payload data, depends on the type
1763c164d6abSRodrigo Rivas Costa 	 *
1764c164d6abSRodrigo Rivas Costa 	 * There are these known types of message:
1765c164d6abSRodrigo Rivas Costa 	 *  0x01: input data (60 bytes)
1766c164d6abSRodrigo Rivas Costa 	 *  0x03: wireless connect/disconnect (1 byte)
1767c164d6abSRodrigo Rivas Costa 	 *  0x04: battery status (11 bytes)
17689ba9498bSVicki Pfau 	 *  0x09: Steam Deck input data (56 bytes)
1769c164d6abSRodrigo Rivas Costa 	 */
1770c164d6abSRodrigo Rivas Costa 
1771c164d6abSRodrigo Rivas Costa 	if (size != 64 || data[0] != 1 || data[1] != 0)
1772c164d6abSRodrigo Rivas Costa 		return 0;
1773c164d6abSRodrigo Rivas Costa 
1774c164d6abSRodrigo Rivas Costa 	switch (data[2]) {
1775622ee123SVicki Pfau 	case ID_CONTROLLER_STATE:
1776c164d6abSRodrigo Rivas Costa 		if (steam->client_opened)
1777c164d6abSRodrigo Rivas Costa 			return 0;
1778c164d6abSRodrigo Rivas Costa 		rcu_read_lock();
1779c164d6abSRodrigo Rivas Costa 		input = rcu_dereference(steam->input);
1780385a4886SRodrigo Rivas Costa 		if (likely(input))
1781c164d6abSRodrigo Rivas Costa 			steam_do_input_event(steam, input, data);
1782c164d6abSRodrigo Rivas Costa 		rcu_read_unlock();
1783c164d6abSRodrigo Rivas Costa 		break;
1784622ee123SVicki Pfau 	case ID_CONTROLLER_DECK_STATE:
17859ba9498bSVicki Pfau 		if (steam->client_opened)
17869ba9498bSVicki Pfau 			return 0;
17879ba9498bSVicki Pfau 		rcu_read_lock();
17889ba9498bSVicki Pfau 		input = rcu_dereference(steam->input);
17899ba9498bSVicki Pfau 		if (likely(input))
17909ba9498bSVicki Pfau 			steam_do_deck_input_event(steam, input, data);
17914b996b61SMax Maisel 		sensors = rcu_dereference(steam->sensors);
17924b996b61SMax Maisel 		if (likely(sensors))
17934b996b61SMax Maisel 			steam_do_deck_sensors_event(steam, sensors, data);
17949ba9498bSVicki Pfau 		rcu_read_unlock();
17959ba9498bSVicki Pfau 		break;
1796622ee123SVicki Pfau 	case ID_CONTROLLER_WIRELESS:
1797c164d6abSRodrigo Rivas Costa 		/*
1798c164d6abSRodrigo Rivas Costa 		 * The payload of this event is a single byte:
1799c164d6abSRodrigo Rivas Costa 		 *  0x01: disconnected.
1800c164d6abSRodrigo Rivas Costa 		 *  0x02: connected.
1801c164d6abSRodrigo Rivas Costa 		 */
1802c164d6abSRodrigo Rivas Costa 		switch (data[4]) {
1803c164d6abSRodrigo Rivas Costa 		case 0x01:
1804c164d6abSRodrigo Rivas Costa 			steam_do_connect_event(steam, false);
1805c164d6abSRodrigo Rivas Costa 			break;
1806c164d6abSRodrigo Rivas Costa 		case 0x02:
1807c164d6abSRodrigo Rivas Costa 			steam_do_connect_event(steam, true);
1808c164d6abSRodrigo Rivas Costa 			break;
1809c164d6abSRodrigo Rivas Costa 		}
1810c164d6abSRodrigo Rivas Costa 		break;
1811622ee123SVicki Pfau 	case ID_CONTROLLER_STATUS:
1812f8271979SRodrigo Rivas Costa 		if (steam->quirks & STEAM_QUIRK_WIRELESS) {
1813f8271979SRodrigo Rivas Costa 			rcu_read_lock();
1814f8271979SRodrigo Rivas Costa 			battery = rcu_dereference(steam->battery);
1815f8271979SRodrigo Rivas Costa 			if (likely(battery)) {
1816f8271979SRodrigo Rivas Costa 				steam_do_battery_event(steam, battery, data);
1817f8271979SRodrigo Rivas Costa 			} else {
1818f8271979SRodrigo Rivas Costa 				dbg_hid(
1819f8271979SRodrigo Rivas Costa 					"%s: battery data without connect event\n",
1820f8271979SRodrigo Rivas Costa 					__func__);
1821f8271979SRodrigo Rivas Costa 				steam_do_connect_event(steam, true);
1822f8271979SRodrigo Rivas Costa 			}
1823f8271979SRodrigo Rivas Costa 			rcu_read_unlock();
1824f8271979SRodrigo Rivas Costa 		}
1825c164d6abSRodrigo Rivas Costa 		break;
1826c164d6abSRodrigo Rivas Costa 	}
1827c164d6abSRodrigo Rivas Costa 	return 0;
1828c164d6abSRodrigo Rivas Costa }
1829c164d6abSRodrigo Rivas Costa 
steam_param_set_lizard_mode(const char * val,const struct kernel_param * kp)1830c164d6abSRodrigo Rivas Costa static int steam_param_set_lizard_mode(const char *val,
1831c164d6abSRodrigo Rivas Costa 					const struct kernel_param *kp)
1832c164d6abSRodrigo Rivas Costa {
1833c164d6abSRodrigo Rivas Costa 	struct steam_device *steam;
1834c164d6abSRodrigo Rivas Costa 	int ret;
1835c164d6abSRodrigo Rivas Costa 
1836c164d6abSRodrigo Rivas Costa 	ret = param_set_bool(val, kp);
1837c164d6abSRodrigo Rivas Costa 	if (ret)
1838c164d6abSRodrigo Rivas Costa 		return ret;
1839c164d6abSRodrigo Rivas Costa 
1840c164d6abSRodrigo Rivas Costa 	mutex_lock(&steam_devices_lock);
1841c164d6abSRodrigo Rivas Costa 	list_for_each_entry(steam, &steam_devices, list) {
1842385a4886SRodrigo Rivas Costa 		if (!steam->client_opened)
1843385a4886SRodrigo Rivas Costa 			steam_set_lizard_mode(steam, lizard_mode);
1844c164d6abSRodrigo Rivas Costa 	}
1845c164d6abSRodrigo Rivas Costa 	mutex_unlock(&steam_devices_lock);
1846c164d6abSRodrigo Rivas Costa 	return 0;
1847c164d6abSRodrigo Rivas Costa }
1848c164d6abSRodrigo Rivas Costa 
1849c164d6abSRodrigo Rivas Costa static const struct kernel_param_ops steam_lizard_mode_ops = {
1850c164d6abSRodrigo Rivas Costa 	.set	= steam_param_set_lizard_mode,
1851c164d6abSRodrigo Rivas Costa 	.get	= param_get_bool,
1852c164d6abSRodrigo Rivas Costa };
1853c164d6abSRodrigo Rivas Costa 
1854c164d6abSRodrigo Rivas Costa module_param_cb(lizard_mode, &steam_lizard_mode_ops, &lizard_mode, 0644);
1855c164d6abSRodrigo Rivas Costa MODULE_PARM_DESC(lizard_mode,
1856c164d6abSRodrigo Rivas Costa 	"Enable mouse and keyboard emulation (lizard mode) when the gamepad is not in use");
1857c164d6abSRodrigo Rivas Costa 
1858c164d6abSRodrigo Rivas Costa static const struct hid_device_id steam_controllers[] = {
1859c164d6abSRodrigo Rivas Costa 	{ /* Wired Steam Controller */
1860c164d6abSRodrigo Rivas Costa 	  HID_USB_DEVICE(USB_VENDOR_ID_VALVE,
1861c164d6abSRodrigo Rivas Costa 		USB_DEVICE_ID_STEAM_CONTROLLER)
1862c164d6abSRodrigo Rivas Costa 	},
1863c164d6abSRodrigo Rivas Costa 	{ /* Wireless Steam Controller */
1864c164d6abSRodrigo Rivas Costa 	  HID_USB_DEVICE(USB_VENDOR_ID_VALVE,
1865c164d6abSRodrigo Rivas Costa 		USB_DEVICE_ID_STEAM_CONTROLLER_WIRELESS),
1866c164d6abSRodrigo Rivas Costa 	  .driver_data = STEAM_QUIRK_WIRELESS
1867c164d6abSRodrigo Rivas Costa 	},
18689ba9498bSVicki Pfau 	{ /* Steam Deck */
18699ba9498bSVicki Pfau 	  HID_USB_DEVICE(USB_VENDOR_ID_VALVE,
18709ba9498bSVicki Pfau 		USB_DEVICE_ID_STEAM_DECK),
18719ba9498bSVicki Pfau 	  .driver_data = STEAM_QUIRK_DECK
18729ba9498bSVicki Pfau 	},
1873c164d6abSRodrigo Rivas Costa 	{}
1874c164d6abSRodrigo Rivas Costa };
1875c164d6abSRodrigo Rivas Costa 
1876c164d6abSRodrigo Rivas Costa MODULE_DEVICE_TABLE(hid, steam_controllers);
1877c164d6abSRodrigo Rivas Costa 
1878c164d6abSRodrigo Rivas Costa static struct hid_driver steam_controller_driver = {
1879c164d6abSRodrigo Rivas Costa 	.name = "hid-steam",
1880c164d6abSRodrigo Rivas Costa 	.id_table = steam_controllers,
1881c164d6abSRodrigo Rivas Costa 	.probe = steam_probe,
1882c164d6abSRodrigo Rivas Costa 	.remove = steam_remove,
1883c164d6abSRodrigo Rivas Costa 	.raw_event = steam_raw_event,
1884c164d6abSRodrigo Rivas Costa };
1885c164d6abSRodrigo Rivas Costa 
1886c164d6abSRodrigo Rivas Costa module_hid_driver(steam_controller_driver);
1887