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