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