162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * HID driver for Valve Steam Controller
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2018 Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>
662306a36Sopenharmony_ci * Copyright (c) 2022 Valve Software
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Supports both the wired and wireless interfaces.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * This controller has a builtin emulation of mouse and keyboard: the right pad
1162306a36Sopenharmony_ci * can be used as a mouse, the shoulder buttons are mouse buttons, A and B
1262306a36Sopenharmony_ci * buttons are ENTER and ESCAPE, and so on. This is implemented as additional
1362306a36Sopenharmony_ci * HID interfaces.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * This is known as the "lizard mode", because apparently lizards like to use
1662306a36Sopenharmony_ci * the computer from the coach, without a proper mouse and keyboard.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * This driver will disable the lizard mode when the input device is opened
1962306a36Sopenharmony_ci * and re-enable it when the input device is closed, so as not to break user
2062306a36Sopenharmony_ci * mode behaviour. The lizard_mode parameter can be used to change that.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * There are a few user space applications (notably Steam Client) that use
2362306a36Sopenharmony_ci * the hidraw interface directly to create input devices (XTest, uinput...).
2462306a36Sopenharmony_ci * In order to avoid breaking them this driver creates a layered hidraw device,
2562306a36Sopenharmony_ci * so it can detect when the client is running and then:
2662306a36Sopenharmony_ci *  - it will not send any command to the controller.
2762306a36Sopenharmony_ci *  - this input device will be removed, to avoid double input of the same
2862306a36Sopenharmony_ci *    user action.
2962306a36Sopenharmony_ci * When the client is closed, this input device will be created again.
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci * For additional functions, such as changing the right-pad margin or switching
3262306a36Sopenharmony_ci * the led, you can use the user-space tool at:
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci *   https://github.com/rodrigorc/steamctrl
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include <linux/device.h>
3862306a36Sopenharmony_ci#include <linux/input.h>
3962306a36Sopenharmony_ci#include <linux/hid.h>
4062306a36Sopenharmony_ci#include <linux/module.h>
4162306a36Sopenharmony_ci#include <linux/workqueue.h>
4262306a36Sopenharmony_ci#include <linux/mutex.h>
4362306a36Sopenharmony_ci#include <linux/rcupdate.h>
4462306a36Sopenharmony_ci#include <linux/delay.h>
4562306a36Sopenharmony_ci#include <linux/power_supply.h>
4662306a36Sopenharmony_ci#include "hid-ids.h"
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
4962306a36Sopenharmony_ciMODULE_AUTHOR("Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>");
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic bool lizard_mode = true;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic DEFINE_MUTEX(steam_devices_lock);
5462306a36Sopenharmony_cistatic LIST_HEAD(steam_devices);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define STEAM_QUIRK_WIRELESS		BIT(0)
5762306a36Sopenharmony_ci#define STEAM_QUIRK_DECK		BIT(1)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* Touch pads are 40 mm in diameter and 65535 units */
6062306a36Sopenharmony_ci#define STEAM_PAD_RESOLUTION 1638
6162306a36Sopenharmony_ci/* Trigger runs are about 5 mm and 256 units */
6262306a36Sopenharmony_ci#define STEAM_TRIGGER_RESOLUTION 51
6362306a36Sopenharmony_ci/* Joystick runs are about 5 mm and 256 units */
6462306a36Sopenharmony_ci#define STEAM_JOYSTICK_RESOLUTION 51
6562306a36Sopenharmony_ci/* Trigger runs are about 6 mm and 32768 units */
6662306a36Sopenharmony_ci#define STEAM_DECK_TRIGGER_RESOLUTION 5461
6762306a36Sopenharmony_ci/* Joystick runs are about 5 mm and 32768 units */
6862306a36Sopenharmony_ci#define STEAM_DECK_JOYSTICK_RESOLUTION 6553
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define STEAM_PAD_FUZZ 256
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/*
7362306a36Sopenharmony_ci * Commands that can be sent in a feature report.
7462306a36Sopenharmony_ci * Thanks to Valve for some valuable hints.
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_ci#define STEAM_CMD_SET_MAPPINGS		0x80
7762306a36Sopenharmony_ci#define STEAM_CMD_CLEAR_MAPPINGS	0x81
7862306a36Sopenharmony_ci#define STEAM_CMD_GET_MAPPINGS		0x82
7962306a36Sopenharmony_ci#define STEAM_CMD_GET_ATTRIB		0x83
8062306a36Sopenharmony_ci#define STEAM_CMD_GET_ATTRIB_LABEL	0x84
8162306a36Sopenharmony_ci#define STEAM_CMD_DEFAULT_MAPPINGS	0x85
8262306a36Sopenharmony_ci#define STEAM_CMD_FACTORY_RESET		0x86
8362306a36Sopenharmony_ci#define STEAM_CMD_WRITE_REGISTER	0x87
8462306a36Sopenharmony_ci#define STEAM_CMD_CLEAR_REGISTER	0x88
8562306a36Sopenharmony_ci#define STEAM_CMD_READ_REGISTER		0x89
8662306a36Sopenharmony_ci#define STEAM_CMD_GET_REGISTER_LABEL	0x8a
8762306a36Sopenharmony_ci#define STEAM_CMD_GET_REGISTER_MAX	0x8b
8862306a36Sopenharmony_ci#define STEAM_CMD_GET_REGISTER_DEFAULT	0x8c
8962306a36Sopenharmony_ci#define STEAM_CMD_SET_MODE		0x8d
9062306a36Sopenharmony_ci#define STEAM_CMD_DEFAULT_MOUSE		0x8e
9162306a36Sopenharmony_ci#define STEAM_CMD_FORCEFEEDBAK		0x8f
9262306a36Sopenharmony_ci#define STEAM_CMD_REQUEST_COMM_STATUS	0xb4
9362306a36Sopenharmony_ci#define STEAM_CMD_GET_SERIAL		0xae
9462306a36Sopenharmony_ci#define STEAM_CMD_HAPTIC_RUMBLE		0xeb
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/* Some useful register ids */
9762306a36Sopenharmony_ci#define STEAM_REG_LPAD_MODE		0x07
9862306a36Sopenharmony_ci#define STEAM_REG_RPAD_MODE		0x08
9962306a36Sopenharmony_ci#define STEAM_REG_RPAD_MARGIN		0x18
10062306a36Sopenharmony_ci#define STEAM_REG_LED			0x2d
10162306a36Sopenharmony_ci#define STEAM_REG_GYRO_MODE		0x30
10262306a36Sopenharmony_ci#define STEAM_REG_LPAD_CLICK_PRESSURE	0x34
10362306a36Sopenharmony_ci#define STEAM_REG_RPAD_CLICK_PRESSURE	0x35
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* Raw event identifiers */
10662306a36Sopenharmony_ci#define STEAM_EV_INPUT_DATA		0x01
10762306a36Sopenharmony_ci#define STEAM_EV_CONNECT		0x03
10862306a36Sopenharmony_ci#define STEAM_EV_BATTERY		0x04
10962306a36Sopenharmony_ci#define STEAM_EV_DECK_INPUT_DATA	0x09
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/* Values for GYRO_MODE (bitmask) */
11262306a36Sopenharmony_ci#define STEAM_GYRO_MODE_OFF		0x0000
11362306a36Sopenharmony_ci#define STEAM_GYRO_MODE_STEERING	0x0001
11462306a36Sopenharmony_ci#define STEAM_GYRO_MODE_TILT		0x0002
11562306a36Sopenharmony_ci#define STEAM_GYRO_MODE_SEND_ORIENTATION	0x0004
11662306a36Sopenharmony_ci#define STEAM_GYRO_MODE_SEND_RAW_ACCEL		0x0008
11762306a36Sopenharmony_ci#define STEAM_GYRO_MODE_SEND_RAW_GYRO		0x0010
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/* Other random constants */
12062306a36Sopenharmony_ci#define STEAM_SERIAL_LEN 10
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistruct steam_device {
12362306a36Sopenharmony_ci	struct list_head list;
12462306a36Sopenharmony_ci	spinlock_t lock;
12562306a36Sopenharmony_ci	struct hid_device *hdev, *client_hdev;
12662306a36Sopenharmony_ci	struct mutex mutex;
12762306a36Sopenharmony_ci	bool client_opened;
12862306a36Sopenharmony_ci	struct input_dev __rcu *input;
12962306a36Sopenharmony_ci	unsigned long quirks;
13062306a36Sopenharmony_ci	struct work_struct work_connect;
13162306a36Sopenharmony_ci	bool connected;
13262306a36Sopenharmony_ci	char serial_no[STEAM_SERIAL_LEN + 1];
13362306a36Sopenharmony_ci	struct power_supply_desc battery_desc;
13462306a36Sopenharmony_ci	struct power_supply __rcu *battery;
13562306a36Sopenharmony_ci	u8 battery_charge;
13662306a36Sopenharmony_ci	u16 voltage;
13762306a36Sopenharmony_ci	struct delayed_work heartbeat;
13862306a36Sopenharmony_ci	struct work_struct rumble_work;
13962306a36Sopenharmony_ci	u16 rumble_left;
14062306a36Sopenharmony_ci	u16 rumble_right;
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int steam_recv_report(struct steam_device *steam,
14462306a36Sopenharmony_ci		u8 *data, int size)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct hid_report *r;
14762306a36Sopenharmony_ci	u8 *buf;
14862306a36Sopenharmony_ci	int ret;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	r = steam->hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0];
15162306a36Sopenharmony_ci	if (!r) {
15262306a36Sopenharmony_ci		hid_err(steam->hdev, "No HID_FEATURE_REPORT submitted -  nothing to read\n");
15362306a36Sopenharmony_ci		return -EINVAL;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (hid_report_len(r) < 64)
15762306a36Sopenharmony_ci		return -EINVAL;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	buf = hid_alloc_report_buf(r, GFP_KERNEL);
16062306a36Sopenharmony_ci	if (!buf)
16162306a36Sopenharmony_ci		return -ENOMEM;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/*
16462306a36Sopenharmony_ci	 * The report ID is always 0, so strip the first byte from the output.
16562306a36Sopenharmony_ci	 * hid_report_len() is not counting the report ID, so +1 to the length
16662306a36Sopenharmony_ci	 * or else we get a EOVERFLOW. We are safe from a buffer overflow
16762306a36Sopenharmony_ci	 * because hid_alloc_report_buf() allocates +7 bytes.
16862306a36Sopenharmony_ci	 */
16962306a36Sopenharmony_ci	ret = hid_hw_raw_request(steam->hdev, 0x00,
17062306a36Sopenharmony_ci			buf, hid_report_len(r) + 1,
17162306a36Sopenharmony_ci			HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
17262306a36Sopenharmony_ci	if (ret > 0)
17362306a36Sopenharmony_ci		memcpy(data, buf + 1, min(size, ret - 1));
17462306a36Sopenharmony_ci	kfree(buf);
17562306a36Sopenharmony_ci	return ret;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic int steam_send_report(struct steam_device *steam,
17962306a36Sopenharmony_ci		u8 *cmd, int size)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct hid_report *r;
18262306a36Sopenharmony_ci	u8 *buf;
18362306a36Sopenharmony_ci	unsigned int retries = 50;
18462306a36Sopenharmony_ci	int ret;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	r = steam->hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0];
18762306a36Sopenharmony_ci	if (!r) {
18862306a36Sopenharmony_ci		hid_err(steam->hdev, "No HID_FEATURE_REPORT submitted -  nothing to read\n");
18962306a36Sopenharmony_ci		return -EINVAL;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (hid_report_len(r) < 64)
19362306a36Sopenharmony_ci		return -EINVAL;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	buf = hid_alloc_report_buf(r, GFP_KERNEL);
19662306a36Sopenharmony_ci	if (!buf)
19762306a36Sopenharmony_ci		return -ENOMEM;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* The report ID is always 0 */
20062306a36Sopenharmony_ci	memcpy(buf + 1, cmd, size);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/*
20362306a36Sopenharmony_ci	 * Sometimes the wireless controller fails with EPIPE
20462306a36Sopenharmony_ci	 * when sending a feature report.
20562306a36Sopenharmony_ci	 * Doing a HID_REQ_GET_REPORT and waiting for a while
20662306a36Sopenharmony_ci	 * seems to fix that.
20762306a36Sopenharmony_ci	 */
20862306a36Sopenharmony_ci	do {
20962306a36Sopenharmony_ci		ret = hid_hw_raw_request(steam->hdev, 0,
21062306a36Sopenharmony_ci				buf, max(size, 64) + 1,
21162306a36Sopenharmony_ci				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
21262306a36Sopenharmony_ci		if (ret != -EPIPE)
21362306a36Sopenharmony_ci			break;
21462306a36Sopenharmony_ci		msleep(20);
21562306a36Sopenharmony_ci	} while (--retries);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	kfree(buf);
21862306a36Sopenharmony_ci	if (ret < 0)
21962306a36Sopenharmony_ci		hid_err(steam->hdev, "%s: error %d (%*ph)\n", __func__,
22062306a36Sopenharmony_ci				ret, size, cmd);
22162306a36Sopenharmony_ci	return ret;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic inline int steam_send_report_byte(struct steam_device *steam, u8 cmd)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	return steam_send_report(steam, &cmd, 1);
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int steam_write_registers(struct steam_device *steam,
23062306a36Sopenharmony_ci		/* u8 reg, u16 val */...)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	/* Send: 0x87 len (reg valLo valHi)* */
23362306a36Sopenharmony_ci	u8 reg;
23462306a36Sopenharmony_ci	u16 val;
23562306a36Sopenharmony_ci	u8 cmd[64] = {STEAM_CMD_WRITE_REGISTER, 0x00};
23662306a36Sopenharmony_ci	int ret;
23762306a36Sopenharmony_ci	va_list args;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	va_start(args, steam);
24062306a36Sopenharmony_ci	for (;;) {
24162306a36Sopenharmony_ci		reg = va_arg(args, int);
24262306a36Sopenharmony_ci		if (reg == 0)
24362306a36Sopenharmony_ci			break;
24462306a36Sopenharmony_ci		val = va_arg(args, int);
24562306a36Sopenharmony_ci		cmd[cmd[1] + 2] = reg;
24662306a36Sopenharmony_ci		cmd[cmd[1] + 3] = val & 0xff;
24762306a36Sopenharmony_ci		cmd[cmd[1] + 4] = val >> 8;
24862306a36Sopenharmony_ci		cmd[1] += 3;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci	va_end(args);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	ret = steam_send_report(steam, cmd, 2 + cmd[1]);
25362306a36Sopenharmony_ci	if (ret < 0)
25462306a36Sopenharmony_ci		return ret;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/*
25762306a36Sopenharmony_ci	 * Sometimes a lingering report for this command can
25862306a36Sopenharmony_ci	 * get read back instead of the last set report if
25962306a36Sopenharmony_ci	 * this isn't explicitly queried
26062306a36Sopenharmony_ci	 */
26162306a36Sopenharmony_ci	return steam_recv_report(steam, cmd, 2 + cmd[1]);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic int steam_get_serial(struct steam_device *steam)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	/*
26762306a36Sopenharmony_ci	 * Send: 0xae 0x15 0x01
26862306a36Sopenharmony_ci	 * Recv: 0xae 0x15 0x01 serialnumber (10 chars)
26962306a36Sopenharmony_ci	 */
27062306a36Sopenharmony_ci	int ret;
27162306a36Sopenharmony_ci	u8 cmd[] = {STEAM_CMD_GET_SERIAL, 0x15, 0x01};
27262306a36Sopenharmony_ci	u8 reply[3 + STEAM_SERIAL_LEN + 1];
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	ret = steam_send_report(steam, cmd, sizeof(cmd));
27562306a36Sopenharmony_ci	if (ret < 0)
27662306a36Sopenharmony_ci		return ret;
27762306a36Sopenharmony_ci	ret = steam_recv_report(steam, reply, sizeof(reply));
27862306a36Sopenharmony_ci	if (ret < 0)
27962306a36Sopenharmony_ci		return ret;
28062306a36Sopenharmony_ci	if (reply[0] != 0xae || reply[1] != 0x15 || reply[2] != 0x01)
28162306a36Sopenharmony_ci		return -EIO;
28262306a36Sopenharmony_ci	reply[3 + STEAM_SERIAL_LEN] = 0;
28362306a36Sopenharmony_ci	strscpy(steam->serial_no, reply + 3, sizeof(steam->serial_no));
28462306a36Sopenharmony_ci	return 0;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci/*
28862306a36Sopenharmony_ci * This command requests the wireless adaptor to post an event
28962306a36Sopenharmony_ci * with the connection status. Useful if this driver is loaded when
29062306a36Sopenharmony_ci * the controller is already connected.
29162306a36Sopenharmony_ci */
29262306a36Sopenharmony_cistatic inline int steam_request_conn_status(struct steam_device *steam)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	return steam_send_report_byte(steam, STEAM_CMD_REQUEST_COMM_STATUS);
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic inline int steam_haptic_rumble(struct steam_device *steam,
29862306a36Sopenharmony_ci				u16 intensity, u16 left_speed, u16 right_speed,
29962306a36Sopenharmony_ci				u8 left_gain, u8 right_gain)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	u8 report[11] = {STEAM_CMD_HAPTIC_RUMBLE, 9};
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	report[3] = intensity & 0xFF;
30462306a36Sopenharmony_ci	report[4] = intensity >> 8;
30562306a36Sopenharmony_ci	report[5] = left_speed & 0xFF;
30662306a36Sopenharmony_ci	report[6] = left_speed >> 8;
30762306a36Sopenharmony_ci	report[7] = right_speed & 0xFF;
30862306a36Sopenharmony_ci	report[8] = right_speed >> 8;
30962306a36Sopenharmony_ci	report[9] = left_gain;
31062306a36Sopenharmony_ci	report[10] = right_gain;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return steam_send_report(steam, report, sizeof(report));
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic void steam_haptic_rumble_cb(struct work_struct *work)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct steam_device *steam = container_of(work, struct steam_device,
31862306a36Sopenharmony_ci							rumble_work);
31962306a36Sopenharmony_ci	steam_haptic_rumble(steam, 0, steam->rumble_left,
32062306a36Sopenharmony_ci		steam->rumble_right, 2, 0);
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci#ifdef CONFIG_STEAM_FF
32462306a36Sopenharmony_cistatic int steam_play_effect(struct input_dev *dev, void *data,
32562306a36Sopenharmony_ci				struct ff_effect *effect)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct steam_device *steam = input_get_drvdata(dev);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	steam->rumble_left = effect->u.rumble.strong_magnitude;
33062306a36Sopenharmony_ci	steam->rumble_right = effect->u.rumble.weak_magnitude;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	return schedule_work(&steam->rumble_work);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci#endif
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic void steam_set_lizard_mode(struct steam_device *steam, bool enable)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	if (enable) {
33962306a36Sopenharmony_ci		/* enable esc, enter, cursors */
34062306a36Sopenharmony_ci		steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MAPPINGS);
34162306a36Sopenharmony_ci		/* enable mouse */
34262306a36Sopenharmony_ci		steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MOUSE);
34362306a36Sopenharmony_ci		steam_write_registers(steam,
34462306a36Sopenharmony_ci			STEAM_REG_RPAD_MARGIN, 0x01, /* enable margin */
34562306a36Sopenharmony_ci			0);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		cancel_delayed_work_sync(&steam->heartbeat);
34862306a36Sopenharmony_ci	} else {
34962306a36Sopenharmony_ci		/* disable esc, enter, cursor */
35062306a36Sopenharmony_ci		steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci		if (steam->quirks & STEAM_QUIRK_DECK) {
35362306a36Sopenharmony_ci			steam_write_registers(steam,
35462306a36Sopenharmony_ci				STEAM_REG_RPAD_MARGIN, 0x00, /* disable margin */
35562306a36Sopenharmony_ci				STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */
35662306a36Sopenharmony_ci				STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */
35762306a36Sopenharmony_ci				STEAM_REG_LPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */
35862306a36Sopenharmony_ci				STEAM_REG_RPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */
35962306a36Sopenharmony_ci				0);
36062306a36Sopenharmony_ci			/*
36162306a36Sopenharmony_ci			 * The Steam Deck has a watchdog that automatically enables
36262306a36Sopenharmony_ci			 * lizard mode if it doesn't see any traffic for too long
36362306a36Sopenharmony_ci			 */
36462306a36Sopenharmony_ci			if (!work_busy(&steam->heartbeat.work))
36562306a36Sopenharmony_ci				schedule_delayed_work(&steam->heartbeat, 5 * HZ);
36662306a36Sopenharmony_ci		} else {
36762306a36Sopenharmony_ci			steam_write_registers(steam,
36862306a36Sopenharmony_ci				STEAM_REG_RPAD_MARGIN, 0x00, /* disable margin */
36962306a36Sopenharmony_ci				STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */
37062306a36Sopenharmony_ci				STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */
37162306a36Sopenharmony_ci				0);
37262306a36Sopenharmony_ci		}
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic int steam_input_open(struct input_dev *dev)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	struct steam_device *steam = input_get_drvdata(dev);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	mutex_lock(&steam->mutex);
38162306a36Sopenharmony_ci	if (!steam->client_opened && lizard_mode)
38262306a36Sopenharmony_ci		steam_set_lizard_mode(steam, false);
38362306a36Sopenharmony_ci	mutex_unlock(&steam->mutex);
38462306a36Sopenharmony_ci	return 0;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic void steam_input_close(struct input_dev *dev)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	struct steam_device *steam = input_get_drvdata(dev);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	mutex_lock(&steam->mutex);
39262306a36Sopenharmony_ci	if (!steam->client_opened && lizard_mode)
39362306a36Sopenharmony_ci		steam_set_lizard_mode(steam, true);
39462306a36Sopenharmony_ci	mutex_unlock(&steam->mutex);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic enum power_supply_property steam_battery_props[] = {
39862306a36Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
39962306a36Sopenharmony_ci	POWER_SUPPLY_PROP_SCOPE,
40062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
40162306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY,
40262306a36Sopenharmony_ci};
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int steam_battery_get_property(struct power_supply *psy,
40562306a36Sopenharmony_ci				enum power_supply_property psp,
40662306a36Sopenharmony_ci				union power_supply_propval *val)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct steam_device *steam = power_supply_get_drvdata(psy);
40962306a36Sopenharmony_ci	unsigned long flags;
41062306a36Sopenharmony_ci	s16 volts;
41162306a36Sopenharmony_ci	u8 batt;
41262306a36Sopenharmony_ci	int ret = 0;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	spin_lock_irqsave(&steam->lock, flags);
41562306a36Sopenharmony_ci	volts = steam->voltage;
41662306a36Sopenharmony_ci	batt = steam->battery_charge;
41762306a36Sopenharmony_ci	spin_unlock_irqrestore(&steam->lock, flags);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	switch (psp) {
42062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
42162306a36Sopenharmony_ci		val->intval = 1;
42262306a36Sopenharmony_ci		break;
42362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_SCOPE:
42462306a36Sopenharmony_ci		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
42562306a36Sopenharmony_ci		break;
42662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
42762306a36Sopenharmony_ci		val->intval = volts * 1000; /* mV -> uV */
42862306a36Sopenharmony_ci		break;
42962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY:
43062306a36Sopenharmony_ci		val->intval = batt;
43162306a36Sopenharmony_ci		break;
43262306a36Sopenharmony_ci	default:
43362306a36Sopenharmony_ci		ret = -EINVAL;
43462306a36Sopenharmony_ci		break;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci	return ret;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int steam_battery_register(struct steam_device *steam)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct power_supply *battery;
44262306a36Sopenharmony_ci	struct power_supply_config battery_cfg = { .drv_data = steam, };
44362306a36Sopenharmony_ci	unsigned long flags;
44462306a36Sopenharmony_ci	int ret;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	steam->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
44762306a36Sopenharmony_ci	steam->battery_desc.properties = steam_battery_props;
44862306a36Sopenharmony_ci	steam->battery_desc.num_properties = ARRAY_SIZE(steam_battery_props);
44962306a36Sopenharmony_ci	steam->battery_desc.get_property = steam_battery_get_property;
45062306a36Sopenharmony_ci	steam->battery_desc.name = devm_kasprintf(&steam->hdev->dev,
45162306a36Sopenharmony_ci			GFP_KERNEL, "steam-controller-%s-battery",
45262306a36Sopenharmony_ci			steam->serial_no);
45362306a36Sopenharmony_ci	if (!steam->battery_desc.name)
45462306a36Sopenharmony_ci		return -ENOMEM;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	/* avoid the warning of 0% battery while waiting for the first info */
45762306a36Sopenharmony_ci	spin_lock_irqsave(&steam->lock, flags);
45862306a36Sopenharmony_ci	steam->voltage = 3000;
45962306a36Sopenharmony_ci	steam->battery_charge = 100;
46062306a36Sopenharmony_ci	spin_unlock_irqrestore(&steam->lock, flags);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	battery = power_supply_register(&steam->hdev->dev,
46362306a36Sopenharmony_ci			&steam->battery_desc, &battery_cfg);
46462306a36Sopenharmony_ci	if (IS_ERR(battery)) {
46562306a36Sopenharmony_ci		ret = PTR_ERR(battery);
46662306a36Sopenharmony_ci		hid_err(steam->hdev,
46762306a36Sopenharmony_ci				"%s:power_supply_register failed with error %d\n",
46862306a36Sopenharmony_ci				__func__, ret);
46962306a36Sopenharmony_ci		return ret;
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci	rcu_assign_pointer(steam->battery, battery);
47262306a36Sopenharmony_ci	power_supply_powers(battery, &steam->hdev->dev);
47362306a36Sopenharmony_ci	return 0;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int steam_input_register(struct steam_device *steam)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct hid_device *hdev = steam->hdev;
47962306a36Sopenharmony_ci	struct input_dev *input;
48062306a36Sopenharmony_ci	int ret;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	rcu_read_lock();
48362306a36Sopenharmony_ci	input = rcu_dereference(steam->input);
48462306a36Sopenharmony_ci	rcu_read_unlock();
48562306a36Sopenharmony_ci	if (input) {
48662306a36Sopenharmony_ci		dbg_hid("%s: already connected\n", __func__);
48762306a36Sopenharmony_ci		return 0;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	input = input_allocate_device();
49162306a36Sopenharmony_ci	if (!input)
49262306a36Sopenharmony_ci		return -ENOMEM;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	input_set_drvdata(input, steam);
49562306a36Sopenharmony_ci	input->dev.parent = &hdev->dev;
49662306a36Sopenharmony_ci	input->open = steam_input_open;
49762306a36Sopenharmony_ci	input->close = steam_input_close;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	input->name = (steam->quirks & STEAM_QUIRK_WIRELESS) ? "Wireless Steam Controller" :
50062306a36Sopenharmony_ci		(steam->quirks & STEAM_QUIRK_DECK) ? "Steam Deck" :
50162306a36Sopenharmony_ci		"Steam Controller";
50262306a36Sopenharmony_ci	input->phys = hdev->phys;
50362306a36Sopenharmony_ci	input->uniq = steam->serial_no;
50462306a36Sopenharmony_ci	input->id.bustype = hdev->bus;
50562306a36Sopenharmony_ci	input->id.vendor = hdev->vendor;
50662306a36Sopenharmony_ci	input->id.product = hdev->product;
50762306a36Sopenharmony_ci	input->id.version = hdev->version;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_TR2);
51062306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_TL2);
51162306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_TR);
51262306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_TL);
51362306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_Y);
51462306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_B);
51562306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_X);
51662306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_A);
51762306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_DPAD_UP);
51862306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_DPAD_RIGHT);
51962306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_DPAD_LEFT);
52062306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_DPAD_DOWN);
52162306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_SELECT);
52262306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_MODE);
52362306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_START);
52462306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_THUMBR);
52562306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_THUMBL);
52662306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_THUMB);
52762306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, BTN_THUMB2);
52862306a36Sopenharmony_ci	if (steam->quirks & STEAM_QUIRK_DECK) {
52962306a36Sopenharmony_ci		input_set_capability(input, EV_KEY, BTN_BASE);
53062306a36Sopenharmony_ci		input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY1);
53162306a36Sopenharmony_ci		input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY2);
53262306a36Sopenharmony_ci		input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY3);
53362306a36Sopenharmony_ci		input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY4);
53462306a36Sopenharmony_ci	} else {
53562306a36Sopenharmony_ci		input_set_capability(input, EV_KEY, BTN_GEAR_DOWN);
53662306a36Sopenharmony_ci		input_set_capability(input, EV_KEY, BTN_GEAR_UP);
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	input_set_abs_params(input, ABS_X, -32767, 32767, 0, 0);
54062306a36Sopenharmony_ci	input_set_abs_params(input, ABS_Y, -32767, 32767, 0, 0);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	input_set_abs_params(input, ABS_HAT0X, -32767, 32767,
54362306a36Sopenharmony_ci			STEAM_PAD_FUZZ, 0);
54462306a36Sopenharmony_ci	input_set_abs_params(input, ABS_HAT0Y, -32767, 32767,
54562306a36Sopenharmony_ci			STEAM_PAD_FUZZ, 0);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (steam->quirks & STEAM_QUIRK_DECK) {
54862306a36Sopenharmony_ci		input_set_abs_params(input, ABS_HAT2Y, 0, 32767, 0, 0);
54962306a36Sopenharmony_ci		input_set_abs_params(input, ABS_HAT2X, 0, 32767, 0, 0);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		input_set_abs_params(input, ABS_RX, -32767, 32767, 0, 0);
55262306a36Sopenharmony_ci		input_set_abs_params(input, ABS_RY, -32767, 32767, 0, 0);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		input_set_abs_params(input, ABS_HAT1X, -32767, 32767,
55562306a36Sopenharmony_ci				STEAM_PAD_FUZZ, 0);
55662306a36Sopenharmony_ci		input_set_abs_params(input, ABS_HAT1Y, -32767, 32767,
55762306a36Sopenharmony_ci				STEAM_PAD_FUZZ, 0);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		input_abs_set_res(input, ABS_X, STEAM_DECK_JOYSTICK_RESOLUTION);
56062306a36Sopenharmony_ci		input_abs_set_res(input, ABS_Y, STEAM_DECK_JOYSTICK_RESOLUTION);
56162306a36Sopenharmony_ci		input_abs_set_res(input, ABS_RX, STEAM_DECK_JOYSTICK_RESOLUTION);
56262306a36Sopenharmony_ci		input_abs_set_res(input, ABS_RY, STEAM_DECK_JOYSTICK_RESOLUTION);
56362306a36Sopenharmony_ci		input_abs_set_res(input, ABS_HAT1X, STEAM_PAD_RESOLUTION);
56462306a36Sopenharmony_ci		input_abs_set_res(input, ABS_HAT1Y, STEAM_PAD_RESOLUTION);
56562306a36Sopenharmony_ci		input_abs_set_res(input, ABS_HAT2Y, STEAM_DECK_TRIGGER_RESOLUTION);
56662306a36Sopenharmony_ci		input_abs_set_res(input, ABS_HAT2X, STEAM_DECK_TRIGGER_RESOLUTION);
56762306a36Sopenharmony_ci	} else {
56862306a36Sopenharmony_ci		input_set_abs_params(input, ABS_HAT2Y, 0, 255, 0, 0);
56962306a36Sopenharmony_ci		input_set_abs_params(input, ABS_HAT2X, 0, 255, 0, 0);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci		input_set_abs_params(input, ABS_RX, -32767, 32767,
57262306a36Sopenharmony_ci				STEAM_PAD_FUZZ, 0);
57362306a36Sopenharmony_ci		input_set_abs_params(input, ABS_RY, -32767, 32767,
57462306a36Sopenharmony_ci				STEAM_PAD_FUZZ, 0);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		input_abs_set_res(input, ABS_X, STEAM_JOYSTICK_RESOLUTION);
57762306a36Sopenharmony_ci		input_abs_set_res(input, ABS_Y, STEAM_JOYSTICK_RESOLUTION);
57862306a36Sopenharmony_ci		input_abs_set_res(input, ABS_RX, STEAM_PAD_RESOLUTION);
57962306a36Sopenharmony_ci		input_abs_set_res(input, ABS_RY, STEAM_PAD_RESOLUTION);
58062306a36Sopenharmony_ci		input_abs_set_res(input, ABS_HAT2Y, STEAM_TRIGGER_RESOLUTION);
58162306a36Sopenharmony_ci		input_abs_set_res(input, ABS_HAT2X, STEAM_TRIGGER_RESOLUTION);
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci	input_abs_set_res(input, ABS_HAT0X, STEAM_PAD_RESOLUTION);
58462306a36Sopenharmony_ci	input_abs_set_res(input, ABS_HAT0Y, STEAM_PAD_RESOLUTION);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci#ifdef CONFIG_STEAM_FF
58762306a36Sopenharmony_ci	if (steam->quirks & STEAM_QUIRK_DECK) {
58862306a36Sopenharmony_ci		input_set_capability(input, EV_FF, FF_RUMBLE);
58962306a36Sopenharmony_ci		ret = input_ff_create_memless(input, NULL, steam_play_effect);
59062306a36Sopenharmony_ci		if (ret)
59162306a36Sopenharmony_ci			goto input_register_fail;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci#endif
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	ret = input_register_device(input);
59662306a36Sopenharmony_ci	if (ret)
59762306a36Sopenharmony_ci		goto input_register_fail;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	rcu_assign_pointer(steam->input, input);
60062306a36Sopenharmony_ci	return 0;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ciinput_register_fail:
60362306a36Sopenharmony_ci	input_free_device(input);
60462306a36Sopenharmony_ci	return ret;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic void steam_input_unregister(struct steam_device *steam)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct input_dev *input;
61062306a36Sopenharmony_ci	rcu_read_lock();
61162306a36Sopenharmony_ci	input = rcu_dereference(steam->input);
61262306a36Sopenharmony_ci	rcu_read_unlock();
61362306a36Sopenharmony_ci	if (!input)
61462306a36Sopenharmony_ci		return;
61562306a36Sopenharmony_ci	RCU_INIT_POINTER(steam->input, NULL);
61662306a36Sopenharmony_ci	synchronize_rcu();
61762306a36Sopenharmony_ci	input_unregister_device(input);
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic void steam_battery_unregister(struct steam_device *steam)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct power_supply *battery;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	rcu_read_lock();
62562306a36Sopenharmony_ci	battery = rcu_dereference(steam->battery);
62662306a36Sopenharmony_ci	rcu_read_unlock();
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (!battery)
62962306a36Sopenharmony_ci		return;
63062306a36Sopenharmony_ci	RCU_INIT_POINTER(steam->battery, NULL);
63162306a36Sopenharmony_ci	synchronize_rcu();
63262306a36Sopenharmony_ci	power_supply_unregister(battery);
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic int steam_register(struct steam_device *steam)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	int ret;
63862306a36Sopenharmony_ci	bool client_opened;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	/*
64162306a36Sopenharmony_ci	 * This function can be called several times in a row with the
64262306a36Sopenharmony_ci	 * wireless adaptor, without steam_unregister() between them, because
64362306a36Sopenharmony_ci	 * another client send a get_connection_status command, for example.
64462306a36Sopenharmony_ci	 * The battery and serial number are set just once per device.
64562306a36Sopenharmony_ci	 */
64662306a36Sopenharmony_ci	if (!steam->serial_no[0]) {
64762306a36Sopenharmony_ci		/*
64862306a36Sopenharmony_ci		 * Unlikely, but getting the serial could fail, and it is not so
64962306a36Sopenharmony_ci		 * important, so make up a serial number and go on.
65062306a36Sopenharmony_ci		 */
65162306a36Sopenharmony_ci		mutex_lock(&steam->mutex);
65262306a36Sopenharmony_ci		if (steam_get_serial(steam) < 0)
65362306a36Sopenharmony_ci			strscpy(steam->serial_no, "XXXXXXXXXX",
65462306a36Sopenharmony_ci					sizeof(steam->serial_no));
65562306a36Sopenharmony_ci		mutex_unlock(&steam->mutex);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci		hid_info(steam->hdev, "Steam Controller '%s' connected",
65862306a36Sopenharmony_ci				steam->serial_no);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		/* ignore battery errors, we can live without it */
66162306a36Sopenharmony_ci		if (steam->quirks & STEAM_QUIRK_WIRELESS)
66262306a36Sopenharmony_ci			steam_battery_register(steam);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		mutex_lock(&steam_devices_lock);
66562306a36Sopenharmony_ci		if (list_empty(&steam->list))
66662306a36Sopenharmony_ci			list_add(&steam->list, &steam_devices);
66762306a36Sopenharmony_ci		mutex_unlock(&steam_devices_lock);
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	mutex_lock(&steam->mutex);
67162306a36Sopenharmony_ci	client_opened = steam->client_opened;
67262306a36Sopenharmony_ci	if (!client_opened)
67362306a36Sopenharmony_ci		steam_set_lizard_mode(steam, lizard_mode);
67462306a36Sopenharmony_ci	mutex_unlock(&steam->mutex);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	if (!client_opened)
67762306a36Sopenharmony_ci		ret = steam_input_register(steam);
67862306a36Sopenharmony_ci	else
67962306a36Sopenharmony_ci		ret = 0;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	return ret;
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic void steam_unregister(struct steam_device *steam)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	steam_battery_unregister(steam);
68762306a36Sopenharmony_ci	steam_input_unregister(steam);
68862306a36Sopenharmony_ci	if (steam->serial_no[0]) {
68962306a36Sopenharmony_ci		hid_info(steam->hdev, "Steam Controller '%s' disconnected",
69062306a36Sopenharmony_ci				steam->serial_no);
69162306a36Sopenharmony_ci		mutex_lock(&steam_devices_lock);
69262306a36Sopenharmony_ci		list_del_init(&steam->list);
69362306a36Sopenharmony_ci		mutex_unlock(&steam_devices_lock);
69462306a36Sopenharmony_ci		steam->serial_no[0] = 0;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic void steam_work_connect_cb(struct work_struct *work)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	struct steam_device *steam = container_of(work, struct steam_device,
70162306a36Sopenharmony_ci							work_connect);
70262306a36Sopenharmony_ci	unsigned long flags;
70362306a36Sopenharmony_ci	bool connected;
70462306a36Sopenharmony_ci	int ret;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	spin_lock_irqsave(&steam->lock, flags);
70762306a36Sopenharmony_ci	connected = steam->connected;
70862306a36Sopenharmony_ci	spin_unlock_irqrestore(&steam->lock, flags);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	if (connected) {
71162306a36Sopenharmony_ci		ret = steam_register(steam);
71262306a36Sopenharmony_ci		if (ret) {
71362306a36Sopenharmony_ci			hid_err(steam->hdev,
71462306a36Sopenharmony_ci				"%s:steam_register failed with error %d\n",
71562306a36Sopenharmony_ci				__func__, ret);
71662306a36Sopenharmony_ci		}
71762306a36Sopenharmony_ci	} else {
71862306a36Sopenharmony_ci		steam_unregister(steam);
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistatic bool steam_is_valve_interface(struct hid_device *hdev)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct hid_report_enum *rep_enum;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/*
72762306a36Sopenharmony_ci	 * The wired device creates 3 interfaces:
72862306a36Sopenharmony_ci	 *  0: emulated mouse.
72962306a36Sopenharmony_ci	 *  1: emulated keyboard.
73062306a36Sopenharmony_ci	 *  2: the real game pad.
73162306a36Sopenharmony_ci	 * The wireless device creates 5 interfaces:
73262306a36Sopenharmony_ci	 *  0: emulated keyboard.
73362306a36Sopenharmony_ci	 *  1-4: slots where up to 4 real game pads will be connected to.
73462306a36Sopenharmony_ci	 * We know which one is the real gamepad interface because they are the
73562306a36Sopenharmony_ci	 * only ones with a feature report.
73662306a36Sopenharmony_ci	 */
73762306a36Sopenharmony_ci	rep_enum = &hdev->report_enum[HID_FEATURE_REPORT];
73862306a36Sopenharmony_ci	return !list_empty(&rep_enum->report_list);
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_cistatic void steam_lizard_mode_heartbeat(struct work_struct *work)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	struct steam_device *steam = container_of(work, struct steam_device,
74462306a36Sopenharmony_ci							heartbeat.work);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	mutex_lock(&steam->mutex);
74762306a36Sopenharmony_ci	if (!steam->client_opened && steam->client_hdev) {
74862306a36Sopenharmony_ci		steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS);
74962306a36Sopenharmony_ci		steam_write_registers(steam,
75062306a36Sopenharmony_ci			STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */
75162306a36Sopenharmony_ci			0);
75262306a36Sopenharmony_ci		schedule_delayed_work(&steam->heartbeat, 5 * HZ);
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci	mutex_unlock(&steam->mutex);
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic int steam_client_ll_parse(struct hid_device *hdev)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	struct steam_device *steam = hdev->driver_data;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	return hid_parse_report(hdev, steam->hdev->dev_rdesc,
76262306a36Sopenharmony_ci			steam->hdev->dev_rsize);
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic int steam_client_ll_start(struct hid_device *hdev)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	return 0;
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic void steam_client_ll_stop(struct hid_device *hdev)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_cistatic int steam_client_ll_open(struct hid_device *hdev)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	struct steam_device *steam = hdev->driver_data;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	mutex_lock(&steam->mutex);
77962306a36Sopenharmony_ci	steam->client_opened = true;
78062306a36Sopenharmony_ci	mutex_unlock(&steam->mutex);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	steam_input_unregister(steam);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	return 0;
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_cistatic void steam_client_ll_close(struct hid_device *hdev)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	struct steam_device *steam = hdev->driver_data;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	unsigned long flags;
79262306a36Sopenharmony_ci	bool connected;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	spin_lock_irqsave(&steam->lock, flags);
79562306a36Sopenharmony_ci	connected = steam->connected;
79662306a36Sopenharmony_ci	spin_unlock_irqrestore(&steam->lock, flags);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	mutex_lock(&steam->mutex);
79962306a36Sopenharmony_ci	steam->client_opened = false;
80062306a36Sopenharmony_ci	if (connected)
80162306a36Sopenharmony_ci		steam_set_lizard_mode(steam, lizard_mode);
80262306a36Sopenharmony_ci	mutex_unlock(&steam->mutex);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if (connected)
80562306a36Sopenharmony_ci		steam_input_register(steam);
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic int steam_client_ll_raw_request(struct hid_device *hdev,
80962306a36Sopenharmony_ci				unsigned char reportnum, u8 *buf,
81062306a36Sopenharmony_ci				size_t count, unsigned char report_type,
81162306a36Sopenharmony_ci				int reqtype)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	struct steam_device *steam = hdev->driver_data;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	return hid_hw_raw_request(steam->hdev, reportnum, buf, count,
81662306a36Sopenharmony_ci			report_type, reqtype);
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic const struct hid_ll_driver steam_client_ll_driver = {
82062306a36Sopenharmony_ci	.parse = steam_client_ll_parse,
82162306a36Sopenharmony_ci	.start = steam_client_ll_start,
82262306a36Sopenharmony_ci	.stop = steam_client_ll_stop,
82362306a36Sopenharmony_ci	.open = steam_client_ll_open,
82462306a36Sopenharmony_ci	.close = steam_client_ll_close,
82562306a36Sopenharmony_ci	.raw_request = steam_client_ll_raw_request,
82662306a36Sopenharmony_ci};
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic struct hid_device *steam_create_client_hid(struct hid_device *hdev)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct hid_device *client_hdev;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	client_hdev = hid_allocate_device();
83362306a36Sopenharmony_ci	if (IS_ERR(client_hdev))
83462306a36Sopenharmony_ci		return client_hdev;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	client_hdev->ll_driver = &steam_client_ll_driver;
83762306a36Sopenharmony_ci	client_hdev->dev.parent = hdev->dev.parent;
83862306a36Sopenharmony_ci	client_hdev->bus = hdev->bus;
83962306a36Sopenharmony_ci	client_hdev->vendor = hdev->vendor;
84062306a36Sopenharmony_ci	client_hdev->product = hdev->product;
84162306a36Sopenharmony_ci	client_hdev->version = hdev->version;
84262306a36Sopenharmony_ci	client_hdev->type = hdev->type;
84362306a36Sopenharmony_ci	client_hdev->country = hdev->country;
84462306a36Sopenharmony_ci	strscpy(client_hdev->name, hdev->name,
84562306a36Sopenharmony_ci			sizeof(client_hdev->name));
84662306a36Sopenharmony_ci	strscpy(client_hdev->phys, hdev->phys,
84762306a36Sopenharmony_ci			sizeof(client_hdev->phys));
84862306a36Sopenharmony_ci	/*
84962306a36Sopenharmony_ci	 * Since we use the same device info than the real interface to
85062306a36Sopenharmony_ci	 * trick userspace, we will be calling steam_probe recursively.
85162306a36Sopenharmony_ci	 * We need to recognize the client interface somehow.
85262306a36Sopenharmony_ci	 */
85362306a36Sopenharmony_ci	client_hdev->group = HID_GROUP_STEAM;
85462306a36Sopenharmony_ci	return client_hdev;
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic int steam_probe(struct hid_device *hdev,
85862306a36Sopenharmony_ci				const struct hid_device_id *id)
85962306a36Sopenharmony_ci{
86062306a36Sopenharmony_ci	struct steam_device *steam;
86162306a36Sopenharmony_ci	int ret;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	ret = hid_parse(hdev);
86462306a36Sopenharmony_ci	if (ret) {
86562306a36Sopenharmony_ci		hid_err(hdev,
86662306a36Sopenharmony_ci			"%s:parse of hid interface failed\n", __func__);
86762306a36Sopenharmony_ci		return ret;
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	/*
87162306a36Sopenharmony_ci	 * The virtual client_dev is only used for hidraw.
87262306a36Sopenharmony_ci	 * Also avoid the recursive probe.
87362306a36Sopenharmony_ci	 */
87462306a36Sopenharmony_ci	if (hdev->group == HID_GROUP_STEAM)
87562306a36Sopenharmony_ci		return hid_hw_start(hdev, HID_CONNECT_HIDRAW);
87662306a36Sopenharmony_ci	/*
87762306a36Sopenharmony_ci	 * The non-valve interfaces (mouse and keyboard emulation) are
87862306a36Sopenharmony_ci	 * connected without changes.
87962306a36Sopenharmony_ci	 */
88062306a36Sopenharmony_ci	if (!steam_is_valve_interface(hdev))
88162306a36Sopenharmony_ci		return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	steam = devm_kzalloc(&hdev->dev, sizeof(*steam), GFP_KERNEL);
88462306a36Sopenharmony_ci	if (!steam) {
88562306a36Sopenharmony_ci		ret = -ENOMEM;
88662306a36Sopenharmony_ci		goto steam_alloc_fail;
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci	steam->hdev = hdev;
88962306a36Sopenharmony_ci	hid_set_drvdata(hdev, steam);
89062306a36Sopenharmony_ci	spin_lock_init(&steam->lock);
89162306a36Sopenharmony_ci	mutex_init(&steam->mutex);
89262306a36Sopenharmony_ci	steam->quirks = id->driver_data;
89362306a36Sopenharmony_ci	INIT_WORK(&steam->work_connect, steam_work_connect_cb);
89462306a36Sopenharmony_ci	INIT_LIST_HEAD(&steam->list);
89562306a36Sopenharmony_ci	INIT_DEFERRABLE_WORK(&steam->heartbeat, steam_lizard_mode_heartbeat);
89662306a36Sopenharmony_ci	INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	steam->client_hdev = steam_create_client_hid(hdev);
89962306a36Sopenharmony_ci	if (IS_ERR(steam->client_hdev)) {
90062306a36Sopenharmony_ci		ret = PTR_ERR(steam->client_hdev);
90162306a36Sopenharmony_ci		goto client_hdev_fail;
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci	steam->client_hdev->driver_data = steam;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	/*
90662306a36Sopenharmony_ci	 * With the real steam controller interface, do not connect hidraw.
90762306a36Sopenharmony_ci	 * Instead, create the client_hid and connect that.
90862306a36Sopenharmony_ci	 */
90962306a36Sopenharmony_ci	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDRAW);
91062306a36Sopenharmony_ci	if (ret)
91162306a36Sopenharmony_ci		goto hid_hw_start_fail;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	ret = hid_add_device(steam->client_hdev);
91462306a36Sopenharmony_ci	if (ret)
91562306a36Sopenharmony_ci		goto client_hdev_add_fail;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	ret = hid_hw_open(hdev);
91862306a36Sopenharmony_ci	if (ret) {
91962306a36Sopenharmony_ci		hid_err(hdev,
92062306a36Sopenharmony_ci			"%s:hid_hw_open\n",
92162306a36Sopenharmony_ci			__func__);
92262306a36Sopenharmony_ci		goto hid_hw_open_fail;
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	if (steam->quirks & STEAM_QUIRK_WIRELESS) {
92662306a36Sopenharmony_ci		hid_info(hdev, "Steam wireless receiver connected");
92762306a36Sopenharmony_ci		/* If using a wireless adaptor ask for connection status */
92862306a36Sopenharmony_ci		steam->connected = false;
92962306a36Sopenharmony_ci		steam_request_conn_status(steam);
93062306a36Sopenharmony_ci	} else {
93162306a36Sopenharmony_ci		/* A wired connection is always present */
93262306a36Sopenharmony_ci		steam->connected = true;
93362306a36Sopenharmony_ci		ret = steam_register(steam);
93462306a36Sopenharmony_ci		if (ret) {
93562306a36Sopenharmony_ci			hid_err(hdev,
93662306a36Sopenharmony_ci				"%s:steam_register failed with error %d\n",
93762306a36Sopenharmony_ci				__func__, ret);
93862306a36Sopenharmony_ci			goto input_register_fail;
93962306a36Sopenharmony_ci		}
94062306a36Sopenharmony_ci	}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	return 0;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ciinput_register_fail:
94562306a36Sopenharmony_cihid_hw_open_fail:
94662306a36Sopenharmony_ciclient_hdev_add_fail:
94762306a36Sopenharmony_ci	hid_hw_stop(hdev);
94862306a36Sopenharmony_cihid_hw_start_fail:
94962306a36Sopenharmony_ci	hid_destroy_device(steam->client_hdev);
95062306a36Sopenharmony_ciclient_hdev_fail:
95162306a36Sopenharmony_ci	cancel_work_sync(&steam->work_connect);
95262306a36Sopenharmony_ci	cancel_delayed_work_sync(&steam->heartbeat);
95362306a36Sopenharmony_ci	cancel_work_sync(&steam->rumble_work);
95462306a36Sopenharmony_cisteam_alloc_fail:
95562306a36Sopenharmony_ci	hid_err(hdev, "%s: failed with error %d\n",
95662306a36Sopenharmony_ci			__func__, ret);
95762306a36Sopenharmony_ci	return ret;
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic void steam_remove(struct hid_device *hdev)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	struct steam_device *steam = hid_get_drvdata(hdev);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	if (!steam || hdev->group == HID_GROUP_STEAM) {
96562306a36Sopenharmony_ci		hid_hw_stop(hdev);
96662306a36Sopenharmony_ci		return;
96762306a36Sopenharmony_ci	}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	hid_destroy_device(steam->client_hdev);
97062306a36Sopenharmony_ci	mutex_lock(&steam->mutex);
97162306a36Sopenharmony_ci	steam->client_hdev = NULL;
97262306a36Sopenharmony_ci	steam->client_opened = false;
97362306a36Sopenharmony_ci	cancel_delayed_work_sync(&steam->heartbeat);
97462306a36Sopenharmony_ci	mutex_unlock(&steam->mutex);
97562306a36Sopenharmony_ci	cancel_work_sync(&steam->work_connect);
97662306a36Sopenharmony_ci	if (steam->quirks & STEAM_QUIRK_WIRELESS) {
97762306a36Sopenharmony_ci		hid_info(hdev, "Steam wireless receiver disconnected");
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci	hid_hw_close(hdev);
98062306a36Sopenharmony_ci	hid_hw_stop(hdev);
98162306a36Sopenharmony_ci	steam_unregister(steam);
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic void steam_do_connect_event(struct steam_device *steam, bool connected)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	unsigned long flags;
98762306a36Sopenharmony_ci	bool changed;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	spin_lock_irqsave(&steam->lock, flags);
99062306a36Sopenharmony_ci	changed = steam->connected != connected;
99162306a36Sopenharmony_ci	steam->connected = connected;
99262306a36Sopenharmony_ci	spin_unlock_irqrestore(&steam->lock, flags);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (changed && schedule_work(&steam->work_connect) == 0)
99562306a36Sopenharmony_ci		dbg_hid("%s: connected=%d event already queued\n",
99662306a36Sopenharmony_ci				__func__, connected);
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci/*
100062306a36Sopenharmony_ci * Some input data in the protocol has the opposite sign.
100162306a36Sopenharmony_ci * Clamp the values to 32767..-32767 so that the range is
100262306a36Sopenharmony_ci * symmetrical and can be negated safely.
100362306a36Sopenharmony_ci */
100462306a36Sopenharmony_cistatic inline s16 steam_le16(u8 *data)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	s16 x = (s16) le16_to_cpup((__le16 *)data);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	return x == -32768 ? -32767 : x;
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci/*
101262306a36Sopenharmony_ci * The size for this message payload is 60.
101362306a36Sopenharmony_ci * The known values are:
101462306a36Sopenharmony_ci *  (* values are not sent through wireless)
101562306a36Sopenharmony_ci *  (* accelerator/gyro is disabled by default)
101662306a36Sopenharmony_ci *  Offset| Type  | Mapped to |Meaning
101762306a36Sopenharmony_ci * -------+-------+-----------+--------------------------
101862306a36Sopenharmony_ci *  4-7   | u32   | --        | sequence number
101962306a36Sopenharmony_ci *  8-10  | 24bit | see below | buttons
102062306a36Sopenharmony_ci *  11    | u8    | ABS_HAT2Y | left trigger
102162306a36Sopenharmony_ci *  12    | u8    | ABS_HAT2X | right trigger
102262306a36Sopenharmony_ci *  13-15 | --    | --        | always 0
102362306a36Sopenharmony_ci *  16-17 | s16   | ABS_X/ABS_HAT0X     | X value
102462306a36Sopenharmony_ci *  18-19 | s16   | ABS_Y/ABS_HAT0Y     | Y value
102562306a36Sopenharmony_ci *  20-21 | s16   | ABS_RX    | right-pad X value
102662306a36Sopenharmony_ci *  22-23 | s16   | ABS_RY    | right-pad Y value
102762306a36Sopenharmony_ci *  24-25 | s16   | --        | * left trigger
102862306a36Sopenharmony_ci *  26-27 | s16   | --        | * right trigger
102962306a36Sopenharmony_ci *  28-29 | s16   | --        | * accelerometer X value
103062306a36Sopenharmony_ci *  30-31 | s16   | --        | * accelerometer Y value
103162306a36Sopenharmony_ci *  32-33 | s16   | --        | * accelerometer Z value
103262306a36Sopenharmony_ci *  34-35 | s16   | --        | gyro X value
103362306a36Sopenharmony_ci *  36-36 | s16   | --        | gyro Y value
103462306a36Sopenharmony_ci *  38-39 | s16   | --        | gyro Z value
103562306a36Sopenharmony_ci *  40-41 | s16   | --        | quaternion W value
103662306a36Sopenharmony_ci *  42-43 | s16   | --        | quaternion X value
103762306a36Sopenharmony_ci *  44-45 | s16   | --        | quaternion Y value
103862306a36Sopenharmony_ci *  46-47 | s16   | --        | quaternion Z value
103962306a36Sopenharmony_ci *  48-49 | --    | --        | always 0
104062306a36Sopenharmony_ci *  50-51 | s16   | --        | * left trigger (uncalibrated)
104162306a36Sopenharmony_ci *  52-53 | s16   | --        | * right trigger (uncalibrated)
104262306a36Sopenharmony_ci *  54-55 | s16   | --        | * joystick X value (uncalibrated)
104362306a36Sopenharmony_ci *  56-57 | s16   | --        | * joystick Y value (uncalibrated)
104462306a36Sopenharmony_ci *  58-59 | s16   | --        | * left-pad X value
104562306a36Sopenharmony_ci *  60-61 | s16   | --        | * left-pad Y value
104662306a36Sopenharmony_ci *  62-63 | u16   | --        | * battery voltage
104762306a36Sopenharmony_ci *
104862306a36Sopenharmony_ci * The buttons are:
104962306a36Sopenharmony_ci *  Bit  | Mapped to  | Description
105062306a36Sopenharmony_ci * ------+------------+--------------------------------
105162306a36Sopenharmony_ci *  8.0  | BTN_TR2    | right trigger fully pressed
105262306a36Sopenharmony_ci *  8.1  | BTN_TL2    | left trigger fully pressed
105362306a36Sopenharmony_ci *  8.2  | BTN_TR     | right shoulder
105462306a36Sopenharmony_ci *  8.3  | BTN_TL     | left shoulder
105562306a36Sopenharmony_ci *  8.4  | BTN_Y      | button Y
105662306a36Sopenharmony_ci *  8.5  | BTN_B      | button B
105762306a36Sopenharmony_ci *  8.6  | BTN_X      | button X
105862306a36Sopenharmony_ci *  8.7  | BTN_A      | button A
105962306a36Sopenharmony_ci *  9.0  | BTN_DPAD_UP    | left-pad up
106062306a36Sopenharmony_ci *  9.1  | BTN_DPAD_RIGHT | left-pad right
106162306a36Sopenharmony_ci *  9.2  | BTN_DPAD_LEFT  | left-pad left
106262306a36Sopenharmony_ci *  9.3  | BTN_DPAD_DOWN  | left-pad down
106362306a36Sopenharmony_ci *  9.4  | BTN_SELECT | menu left
106462306a36Sopenharmony_ci *  9.5  | BTN_MODE   | steam logo
106562306a36Sopenharmony_ci *  9.6  | BTN_START  | menu right
106662306a36Sopenharmony_ci *  9.7  | BTN_GEAR_DOWN | left back lever
106762306a36Sopenharmony_ci * 10.0  | BTN_GEAR_UP   | right back lever
106862306a36Sopenharmony_ci * 10.1  | --         | left-pad clicked
106962306a36Sopenharmony_ci * 10.2  | BTN_THUMBR | right-pad clicked
107062306a36Sopenharmony_ci * 10.3  | BTN_THUMB  | left-pad touched (but see explanation below)
107162306a36Sopenharmony_ci * 10.4  | BTN_THUMB2 | right-pad touched
107262306a36Sopenharmony_ci * 10.5  | --         | unknown
107362306a36Sopenharmony_ci * 10.6  | BTN_THUMBL | joystick clicked
107462306a36Sopenharmony_ci * 10.7  | --         | lpad_and_joy
107562306a36Sopenharmony_ci */
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_cistatic void steam_do_input_event(struct steam_device *steam,
107862306a36Sopenharmony_ci		struct input_dev *input, u8 *data)
107962306a36Sopenharmony_ci{
108062306a36Sopenharmony_ci	/* 24 bits of buttons */
108162306a36Sopenharmony_ci	u8 b8, b9, b10;
108262306a36Sopenharmony_ci	s16 x, y;
108362306a36Sopenharmony_ci	bool lpad_touched, lpad_and_joy;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	b8 = data[8];
108662306a36Sopenharmony_ci	b9 = data[9];
108762306a36Sopenharmony_ci	b10 = data[10];
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	input_report_abs(input, ABS_HAT2Y, data[11]);
109062306a36Sopenharmony_ci	input_report_abs(input, ABS_HAT2X, data[12]);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	/*
109362306a36Sopenharmony_ci	 * These two bits tells how to interpret the values X and Y.
109462306a36Sopenharmony_ci	 * lpad_and_joy tells that the joystick and the lpad are used at the
109562306a36Sopenharmony_ci	 * same time.
109662306a36Sopenharmony_ci	 * lpad_touched tells whether X/Y are to be read as lpad coord or
109762306a36Sopenharmony_ci	 * joystick values.
109862306a36Sopenharmony_ci	 * (lpad_touched || lpad_and_joy) tells if the lpad is really touched.
109962306a36Sopenharmony_ci	 */
110062306a36Sopenharmony_ci	lpad_touched = b10 & BIT(3);
110162306a36Sopenharmony_ci	lpad_and_joy = b10 & BIT(7);
110262306a36Sopenharmony_ci	x = steam_le16(data + 16);
110362306a36Sopenharmony_ci	y = -steam_le16(data + 18);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	input_report_abs(input, lpad_touched ? ABS_HAT0X : ABS_X, x);
110662306a36Sopenharmony_ci	input_report_abs(input, lpad_touched ? ABS_HAT0Y : ABS_Y, y);
110762306a36Sopenharmony_ci	/* Check if joystick is centered */
110862306a36Sopenharmony_ci	if (lpad_touched && !lpad_and_joy) {
110962306a36Sopenharmony_ci		input_report_abs(input, ABS_X, 0);
111062306a36Sopenharmony_ci		input_report_abs(input, ABS_Y, 0);
111162306a36Sopenharmony_ci	}
111262306a36Sopenharmony_ci	/* Check if lpad is untouched */
111362306a36Sopenharmony_ci	if (!(lpad_touched || lpad_and_joy)) {
111462306a36Sopenharmony_ci		input_report_abs(input, ABS_HAT0X, 0);
111562306a36Sopenharmony_ci		input_report_abs(input, ABS_HAT0Y, 0);
111662306a36Sopenharmony_ci	}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	input_report_abs(input, ABS_RX, steam_le16(data + 20));
111962306a36Sopenharmony_ci	input_report_abs(input, ABS_RY, -steam_le16(data + 22));
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_TR2, !!(b8 & BIT(0)));
112262306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_TL2, !!(b8 & BIT(1)));
112362306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_TR, !!(b8 & BIT(2)));
112462306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_TL, !!(b8 & BIT(3)));
112562306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_Y, !!(b8 & BIT(4)));
112662306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_B, !!(b8 & BIT(5)));
112762306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_X, !!(b8 & BIT(6)));
112862306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_A, !!(b8 & BIT(7)));
112962306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_SELECT, !!(b9 & BIT(4)));
113062306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_MODE, !!(b9 & BIT(5)));
113162306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_START, !!(b9 & BIT(6)));
113262306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_GEAR_DOWN, !!(b9 & BIT(7)));
113362306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_GEAR_UP, !!(b10 & BIT(0)));
113462306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_THUMBR, !!(b10 & BIT(2)));
113562306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_THUMBL, !!(b10 & BIT(6)));
113662306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_THUMB, lpad_touched || lpad_and_joy);
113762306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_THUMB2, !!(b10 & BIT(4)));
113862306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_DPAD_UP, !!(b9 & BIT(0)));
113962306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_DPAD_RIGHT, !!(b9 & BIT(1)));
114062306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_DPAD_LEFT, !!(b9 & BIT(2)));
114162306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_DPAD_DOWN, !!(b9 & BIT(3)));
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	input_sync(input);
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci/*
114762306a36Sopenharmony_ci * The size for this message payload is 56.
114862306a36Sopenharmony_ci * The known values are:
114962306a36Sopenharmony_ci *  Offset| Type  | Mapped to |Meaning
115062306a36Sopenharmony_ci * -------+-------+-----------+--------------------------
115162306a36Sopenharmony_ci *  4-7   | u32   | --        | sequence number
115262306a36Sopenharmony_ci *  8-15  | u64   | see below | buttons
115362306a36Sopenharmony_ci *  16-17 | s16   | ABS_HAT0X | left-pad X value
115462306a36Sopenharmony_ci *  18-19 | s16   | ABS_HAT0Y | left-pad Y value
115562306a36Sopenharmony_ci *  20-21 | s16   | ABS_HAT1X | right-pad X value
115662306a36Sopenharmony_ci *  22-23 | s16   | ABS_HAT1Y | right-pad Y value
115762306a36Sopenharmony_ci *  24-25 | s16   | --        | accelerometer X value
115862306a36Sopenharmony_ci *  26-27 | s16   | --        | accelerometer Y value
115962306a36Sopenharmony_ci *  28-29 | s16   | --        | accelerometer Z value
116062306a36Sopenharmony_ci *  30-31 | s16   | --        | gyro X value
116162306a36Sopenharmony_ci *  32-33 | s16   | --        | gyro Y value
116262306a36Sopenharmony_ci *  34-35 | s16   | --        | gyro Z value
116362306a36Sopenharmony_ci *  36-37 | s16   | --        | quaternion W value
116462306a36Sopenharmony_ci *  38-39 | s16   | --        | quaternion X value
116562306a36Sopenharmony_ci *  40-41 | s16   | --        | quaternion Y value
116662306a36Sopenharmony_ci *  42-43 | s16   | --        | quaternion Z value
116762306a36Sopenharmony_ci *  44-45 | u16   | ABS_HAT2Y | left trigger (uncalibrated)
116862306a36Sopenharmony_ci *  46-47 | u16   | ABS_HAT2X | right trigger (uncalibrated)
116962306a36Sopenharmony_ci *  48-49 | s16   | ABS_X     | left joystick X
117062306a36Sopenharmony_ci *  50-51 | s16   | ABS_Y     | left joystick Y
117162306a36Sopenharmony_ci *  52-53 | s16   | ABS_RX    | right joystick X
117262306a36Sopenharmony_ci *  54-55 | s16   | ABS_RY    | right joystick Y
117362306a36Sopenharmony_ci *  56-57 | u16   | --        | left pad pressure
117462306a36Sopenharmony_ci *  58-59 | u16   | --        | right pad pressure
117562306a36Sopenharmony_ci *
117662306a36Sopenharmony_ci * The buttons are:
117762306a36Sopenharmony_ci *  Bit  | Mapped to  | Description
117862306a36Sopenharmony_ci * ------+------------+--------------------------------
117962306a36Sopenharmony_ci *  8.0  | BTN_TR2    | right trigger fully pressed
118062306a36Sopenharmony_ci *  8.1  | BTN_TL2    | left trigger fully pressed
118162306a36Sopenharmony_ci *  8.2  | BTN_TR     | right shoulder
118262306a36Sopenharmony_ci *  8.3  | BTN_TL     | left shoulder
118362306a36Sopenharmony_ci *  8.4  | BTN_Y      | button Y
118462306a36Sopenharmony_ci *  8.5  | BTN_B      | button B
118562306a36Sopenharmony_ci *  8.6  | BTN_X      | button X
118662306a36Sopenharmony_ci *  8.7  | BTN_A      | button A
118762306a36Sopenharmony_ci *  9.0  | BTN_DPAD_UP    | left-pad up
118862306a36Sopenharmony_ci *  9.1  | BTN_DPAD_RIGHT | left-pad right
118962306a36Sopenharmony_ci *  9.2  | BTN_DPAD_LEFT  | left-pad left
119062306a36Sopenharmony_ci *  9.3  | BTN_DPAD_DOWN  | left-pad down
119162306a36Sopenharmony_ci *  9.4  | BTN_SELECT | menu left
119262306a36Sopenharmony_ci *  9.5  | BTN_MODE   | steam logo
119362306a36Sopenharmony_ci *  9.6  | BTN_START  | menu right
119462306a36Sopenharmony_ci *  9.7  | BTN_TRIGGER_HAPPY3 | left bottom grip button
119562306a36Sopenharmony_ci *  10.0 | BTN_TRIGGER_HAPPY4 | right bottom grip button
119662306a36Sopenharmony_ci *  10.1 | BTN_THUMB  | left pad pressed
119762306a36Sopenharmony_ci *  10.2 | BTN_THUMB2 | right pad pressed
119862306a36Sopenharmony_ci *  10.3 | --         | left pad touched
119962306a36Sopenharmony_ci *  10.4 | --         | right pad touched
120062306a36Sopenharmony_ci *  10.5 | --         | unknown
120162306a36Sopenharmony_ci *  10.6 | BTN_THUMBL | left joystick clicked
120262306a36Sopenharmony_ci *  10.7 | --         | unknown
120362306a36Sopenharmony_ci *  11.0 | --         | unknown
120462306a36Sopenharmony_ci *  11.1 | --         | unknown
120562306a36Sopenharmony_ci *  11.2 | BTN_THUMBR | right joystick clicked
120662306a36Sopenharmony_ci *  11.3 | --         | unknown
120762306a36Sopenharmony_ci *  11.4 | --         | unknown
120862306a36Sopenharmony_ci *  11.5 | --         | unknown
120962306a36Sopenharmony_ci *  11.6 | --         | unknown
121062306a36Sopenharmony_ci *  11.7 | --         | unknown
121162306a36Sopenharmony_ci *  12.0 | --         | unknown
121262306a36Sopenharmony_ci *  12.1 | --         | unknown
121362306a36Sopenharmony_ci *  12.2 | --         | unknown
121462306a36Sopenharmony_ci *  12.3 | --         | unknown
121562306a36Sopenharmony_ci *  12.4 | --         | unknown
121662306a36Sopenharmony_ci *  12.5 | --         | unknown
121762306a36Sopenharmony_ci *  12.6 | --         | unknown
121862306a36Sopenharmony_ci *  12.7 | --         | unknown
121962306a36Sopenharmony_ci *  13.0 | --         | unknown
122062306a36Sopenharmony_ci *  13.1 | BTN_TRIGGER_HAPPY1 | left top grip button
122162306a36Sopenharmony_ci *  13.2 | BTN_TRIGGER_HAPPY2 | right top grip button
122262306a36Sopenharmony_ci *  13.3 | --         | unknown
122362306a36Sopenharmony_ci *  13.4 | --         | unknown
122462306a36Sopenharmony_ci *  13.5 | --         | unknown
122562306a36Sopenharmony_ci *  13.6 | --         | left joystick touched
122662306a36Sopenharmony_ci *  13.7 | --         | right joystick touched
122762306a36Sopenharmony_ci *  14.0 | --         | unknown
122862306a36Sopenharmony_ci *  14.1 | --         | unknown
122962306a36Sopenharmony_ci *  14.2 | BTN_BASE   | quick access button
123062306a36Sopenharmony_ci *  14.3 | --         | unknown
123162306a36Sopenharmony_ci *  14.4 | --         | unknown
123262306a36Sopenharmony_ci *  14.5 | --         | unknown
123362306a36Sopenharmony_ci *  14.6 | --         | unknown
123462306a36Sopenharmony_ci *  14.7 | --         | unknown
123562306a36Sopenharmony_ci *  15.0 | --         | unknown
123662306a36Sopenharmony_ci *  15.1 | --         | unknown
123762306a36Sopenharmony_ci *  15.2 | --         | unknown
123862306a36Sopenharmony_ci *  15.3 | --         | unknown
123962306a36Sopenharmony_ci *  15.4 | --         | unknown
124062306a36Sopenharmony_ci *  15.5 | --         | unknown
124162306a36Sopenharmony_ci *  15.6 | --         | unknown
124262306a36Sopenharmony_ci *  15.7 | --         | unknown
124362306a36Sopenharmony_ci */
124462306a36Sopenharmony_cistatic void steam_do_deck_input_event(struct steam_device *steam,
124562306a36Sopenharmony_ci		struct input_dev *input, u8 *data)
124662306a36Sopenharmony_ci{
124762306a36Sopenharmony_ci	u8 b8, b9, b10, b11, b13, b14;
124862306a36Sopenharmony_ci	bool lpad_touched, rpad_touched;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	b8 = data[8];
125162306a36Sopenharmony_ci	b9 = data[9];
125262306a36Sopenharmony_ci	b10 = data[10];
125362306a36Sopenharmony_ci	b11 = data[11];
125462306a36Sopenharmony_ci	b13 = data[13];
125562306a36Sopenharmony_ci	b14 = data[14];
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	lpad_touched = b10 & BIT(3);
125862306a36Sopenharmony_ci	rpad_touched = b10 & BIT(4);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	if (lpad_touched) {
126162306a36Sopenharmony_ci		input_report_abs(input, ABS_HAT0X, steam_le16(data + 16));
126262306a36Sopenharmony_ci		input_report_abs(input, ABS_HAT0Y, steam_le16(data + 18));
126362306a36Sopenharmony_ci	} else {
126462306a36Sopenharmony_ci		input_report_abs(input, ABS_HAT0X, 0);
126562306a36Sopenharmony_ci		input_report_abs(input, ABS_HAT0Y, 0);
126662306a36Sopenharmony_ci	}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	if (rpad_touched) {
126962306a36Sopenharmony_ci		input_report_abs(input, ABS_HAT1X, steam_le16(data + 20));
127062306a36Sopenharmony_ci		input_report_abs(input, ABS_HAT1Y, steam_le16(data + 22));
127162306a36Sopenharmony_ci	} else {
127262306a36Sopenharmony_ci		input_report_abs(input, ABS_HAT1X, 0);
127362306a36Sopenharmony_ci		input_report_abs(input, ABS_HAT1Y, 0);
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	input_report_abs(input, ABS_X, steam_le16(data + 48));
127762306a36Sopenharmony_ci	input_report_abs(input, ABS_Y, -steam_le16(data + 50));
127862306a36Sopenharmony_ci	input_report_abs(input, ABS_RX, steam_le16(data + 52));
127962306a36Sopenharmony_ci	input_report_abs(input, ABS_RY, -steam_le16(data + 54));
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	input_report_abs(input, ABS_HAT2Y, steam_le16(data + 44));
128262306a36Sopenharmony_ci	input_report_abs(input, ABS_HAT2X, steam_le16(data + 46));
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_TR2, !!(b8 & BIT(0)));
128562306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_TL2, !!(b8 & BIT(1)));
128662306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_TR, !!(b8 & BIT(2)));
128762306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_TL, !!(b8 & BIT(3)));
128862306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_Y, !!(b8 & BIT(4)));
128962306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_B, !!(b8 & BIT(5)));
129062306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_X, !!(b8 & BIT(6)));
129162306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_A, !!(b8 & BIT(7)));
129262306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_SELECT, !!(b9 & BIT(4)));
129362306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_MODE, !!(b9 & BIT(5)));
129462306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_START, !!(b9 & BIT(6)));
129562306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_TRIGGER_HAPPY3, !!(b9 & BIT(7)));
129662306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_TRIGGER_HAPPY4, !!(b10 & BIT(0)));
129762306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_THUMBL, !!(b10 & BIT(6)));
129862306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_THUMBR, !!(b11 & BIT(2)));
129962306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_DPAD_UP, !!(b9 & BIT(0)));
130062306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_DPAD_RIGHT, !!(b9 & BIT(1)));
130162306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_DPAD_LEFT, !!(b9 & BIT(2)));
130262306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_DPAD_DOWN, !!(b9 & BIT(3)));
130362306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_THUMB, !!(b10 & BIT(1)));
130462306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_THUMB2, !!(b10 & BIT(2)));
130562306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_TRIGGER_HAPPY1, !!(b13 & BIT(1)));
130662306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_TRIGGER_HAPPY2, !!(b13 & BIT(2)));
130762306a36Sopenharmony_ci	input_event(input, EV_KEY, BTN_BASE, !!(b14 & BIT(2)));
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	input_sync(input);
131062306a36Sopenharmony_ci}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci/*
131362306a36Sopenharmony_ci * The size for this message payload is 11.
131462306a36Sopenharmony_ci * The known values are:
131562306a36Sopenharmony_ci *  Offset| Type  | Meaning
131662306a36Sopenharmony_ci * -------+-------+---------------------------
131762306a36Sopenharmony_ci *  4-7   | u32   | sequence number
131862306a36Sopenharmony_ci *  8-11  | --    | always 0
131962306a36Sopenharmony_ci *  12-13 | u16   | voltage (mV)
132062306a36Sopenharmony_ci *  14    | u8    | battery percent
132162306a36Sopenharmony_ci */
132262306a36Sopenharmony_cistatic void steam_do_battery_event(struct steam_device *steam,
132362306a36Sopenharmony_ci		struct power_supply *battery, u8 *data)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	unsigned long flags;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	s16 volts = steam_le16(data + 12);
132862306a36Sopenharmony_ci	u8 batt = data[14];
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	/* Creating the battery may have failed */
133162306a36Sopenharmony_ci	rcu_read_lock();
133262306a36Sopenharmony_ci	battery = rcu_dereference(steam->battery);
133362306a36Sopenharmony_ci	if (likely(battery)) {
133462306a36Sopenharmony_ci		spin_lock_irqsave(&steam->lock, flags);
133562306a36Sopenharmony_ci		steam->voltage = volts;
133662306a36Sopenharmony_ci		steam->battery_charge = batt;
133762306a36Sopenharmony_ci		spin_unlock_irqrestore(&steam->lock, flags);
133862306a36Sopenharmony_ci		power_supply_changed(battery);
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci	rcu_read_unlock();
134162306a36Sopenharmony_ci}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_cistatic int steam_raw_event(struct hid_device *hdev,
134462306a36Sopenharmony_ci			struct hid_report *report, u8 *data,
134562306a36Sopenharmony_ci			int size)
134662306a36Sopenharmony_ci{
134762306a36Sopenharmony_ci	struct steam_device *steam = hid_get_drvdata(hdev);
134862306a36Sopenharmony_ci	struct input_dev *input;
134962306a36Sopenharmony_ci	struct power_supply *battery;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	if (!steam)
135262306a36Sopenharmony_ci		return 0;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	if (steam->client_opened)
135562306a36Sopenharmony_ci		hid_input_report(steam->client_hdev, HID_FEATURE_REPORT,
135662306a36Sopenharmony_ci				data, size, 0);
135762306a36Sopenharmony_ci	/*
135862306a36Sopenharmony_ci	 * All messages are size=64, all values little-endian.
135962306a36Sopenharmony_ci	 * The format is:
136062306a36Sopenharmony_ci	 *  Offset| Meaning
136162306a36Sopenharmony_ci	 * -------+--------------------------------------------
136262306a36Sopenharmony_ci	 *  0-1   | always 0x01, 0x00, maybe protocol version?
136362306a36Sopenharmony_ci	 *  2     | type of message
136462306a36Sopenharmony_ci	 *  3     | length of the real payload (not checked)
136562306a36Sopenharmony_ci	 *  4-n   | payload data, depends on the type
136662306a36Sopenharmony_ci	 *
136762306a36Sopenharmony_ci	 * There are these known types of message:
136862306a36Sopenharmony_ci	 *  0x01: input data (60 bytes)
136962306a36Sopenharmony_ci	 *  0x03: wireless connect/disconnect (1 byte)
137062306a36Sopenharmony_ci	 *  0x04: battery status (11 bytes)
137162306a36Sopenharmony_ci	 *  0x09: Steam Deck input data (56 bytes)
137262306a36Sopenharmony_ci	 */
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	if (size != 64 || data[0] != 1 || data[1] != 0)
137562306a36Sopenharmony_ci		return 0;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	switch (data[2]) {
137862306a36Sopenharmony_ci	case STEAM_EV_INPUT_DATA:
137962306a36Sopenharmony_ci		if (steam->client_opened)
138062306a36Sopenharmony_ci			return 0;
138162306a36Sopenharmony_ci		rcu_read_lock();
138262306a36Sopenharmony_ci		input = rcu_dereference(steam->input);
138362306a36Sopenharmony_ci		if (likely(input))
138462306a36Sopenharmony_ci			steam_do_input_event(steam, input, data);
138562306a36Sopenharmony_ci		rcu_read_unlock();
138662306a36Sopenharmony_ci		break;
138762306a36Sopenharmony_ci	case STEAM_EV_DECK_INPUT_DATA:
138862306a36Sopenharmony_ci		if (steam->client_opened)
138962306a36Sopenharmony_ci			return 0;
139062306a36Sopenharmony_ci		rcu_read_lock();
139162306a36Sopenharmony_ci		input = rcu_dereference(steam->input);
139262306a36Sopenharmony_ci		if (likely(input))
139362306a36Sopenharmony_ci			steam_do_deck_input_event(steam, input, data);
139462306a36Sopenharmony_ci		rcu_read_unlock();
139562306a36Sopenharmony_ci		break;
139662306a36Sopenharmony_ci	case STEAM_EV_CONNECT:
139762306a36Sopenharmony_ci		/*
139862306a36Sopenharmony_ci		 * The payload of this event is a single byte:
139962306a36Sopenharmony_ci		 *  0x01: disconnected.
140062306a36Sopenharmony_ci		 *  0x02: connected.
140162306a36Sopenharmony_ci		 */
140262306a36Sopenharmony_ci		switch (data[4]) {
140362306a36Sopenharmony_ci		case 0x01:
140462306a36Sopenharmony_ci			steam_do_connect_event(steam, false);
140562306a36Sopenharmony_ci			break;
140662306a36Sopenharmony_ci		case 0x02:
140762306a36Sopenharmony_ci			steam_do_connect_event(steam, true);
140862306a36Sopenharmony_ci			break;
140962306a36Sopenharmony_ci		}
141062306a36Sopenharmony_ci		break;
141162306a36Sopenharmony_ci	case STEAM_EV_BATTERY:
141262306a36Sopenharmony_ci		if (steam->quirks & STEAM_QUIRK_WIRELESS) {
141362306a36Sopenharmony_ci			rcu_read_lock();
141462306a36Sopenharmony_ci			battery = rcu_dereference(steam->battery);
141562306a36Sopenharmony_ci			if (likely(battery)) {
141662306a36Sopenharmony_ci				steam_do_battery_event(steam, battery, data);
141762306a36Sopenharmony_ci			} else {
141862306a36Sopenharmony_ci				dbg_hid(
141962306a36Sopenharmony_ci					"%s: battery data without connect event\n",
142062306a36Sopenharmony_ci					__func__);
142162306a36Sopenharmony_ci				steam_do_connect_event(steam, true);
142262306a36Sopenharmony_ci			}
142362306a36Sopenharmony_ci			rcu_read_unlock();
142462306a36Sopenharmony_ci		}
142562306a36Sopenharmony_ci		break;
142662306a36Sopenharmony_ci	}
142762306a36Sopenharmony_ci	return 0;
142862306a36Sopenharmony_ci}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_cistatic int steam_param_set_lizard_mode(const char *val,
143162306a36Sopenharmony_ci					const struct kernel_param *kp)
143262306a36Sopenharmony_ci{
143362306a36Sopenharmony_ci	struct steam_device *steam;
143462306a36Sopenharmony_ci	int ret;
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	ret = param_set_bool(val, kp);
143762306a36Sopenharmony_ci	if (ret)
143862306a36Sopenharmony_ci		return ret;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	mutex_lock(&steam_devices_lock);
144162306a36Sopenharmony_ci	list_for_each_entry(steam, &steam_devices, list) {
144262306a36Sopenharmony_ci		mutex_lock(&steam->mutex);
144362306a36Sopenharmony_ci		if (!steam->client_opened)
144462306a36Sopenharmony_ci			steam_set_lizard_mode(steam, lizard_mode);
144562306a36Sopenharmony_ci		mutex_unlock(&steam->mutex);
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci	mutex_unlock(&steam_devices_lock);
144862306a36Sopenharmony_ci	return 0;
144962306a36Sopenharmony_ci}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_cistatic const struct kernel_param_ops steam_lizard_mode_ops = {
145262306a36Sopenharmony_ci	.set	= steam_param_set_lizard_mode,
145362306a36Sopenharmony_ci	.get	= param_get_bool,
145462306a36Sopenharmony_ci};
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_cimodule_param_cb(lizard_mode, &steam_lizard_mode_ops, &lizard_mode, 0644);
145762306a36Sopenharmony_ciMODULE_PARM_DESC(lizard_mode,
145862306a36Sopenharmony_ci	"Enable mouse and keyboard emulation (lizard mode) when the gamepad is not in use");
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_cistatic const struct hid_device_id steam_controllers[] = {
146162306a36Sopenharmony_ci	{ /* Wired Steam Controller */
146262306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_VALVE,
146362306a36Sopenharmony_ci		USB_DEVICE_ID_STEAM_CONTROLLER)
146462306a36Sopenharmony_ci	},
146562306a36Sopenharmony_ci	{ /* Wireless Steam Controller */
146662306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_VALVE,
146762306a36Sopenharmony_ci		USB_DEVICE_ID_STEAM_CONTROLLER_WIRELESS),
146862306a36Sopenharmony_ci	  .driver_data = STEAM_QUIRK_WIRELESS
146962306a36Sopenharmony_ci	},
147062306a36Sopenharmony_ci	{ /* Steam Deck */
147162306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_VALVE,
147262306a36Sopenharmony_ci		USB_DEVICE_ID_STEAM_DECK),
147362306a36Sopenharmony_ci	  .driver_data = STEAM_QUIRK_DECK
147462306a36Sopenharmony_ci	},
147562306a36Sopenharmony_ci	{}
147662306a36Sopenharmony_ci};
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, steam_controllers);
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_cistatic struct hid_driver steam_controller_driver = {
148162306a36Sopenharmony_ci	.name = "hid-steam",
148262306a36Sopenharmony_ci	.id_table = steam_controllers,
148362306a36Sopenharmony_ci	.probe = steam_probe,
148462306a36Sopenharmony_ci	.remove = steam_remove,
148562306a36Sopenharmony_ci	.raw_event = steam_raw_event,
148662306a36Sopenharmony_ci};
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_cimodule_hid_driver(steam_controller_driver);
1489