// SPDX-License-Identifier: GPL-2.0+ /* * HID driver for Valve Steam Controller * * Copyright (c) 2018 Rodrigo Rivas Costa <rodrigorivascosta@gmail.com> * Copyright (c) 2022 Valve Software * * Supports both the wired and wireless interfaces. * * This controller has a builtin emulation of mouse and keyboard: the right pad * can be used as a mouse, the shoulder buttons are mouse buttons, A and B * buttons are ENTER and ESCAPE, and so on. This is implemented as additional * HID interfaces. * * This is known as the "lizard mode", because apparently lizards like to use * the computer from the coach, without a proper mouse and keyboard. * * This driver will disable the lizard mode when the input device is opened * and re-enable it when the input device is closed, so as not to break user * mode behaviour. The lizard_mode parameter can be used to change that. * * There are a few user space applications (notably Steam Client) that use * the hidraw interface directly to create input devices (XTest, uinput...). * In order to avoid breaking them this driver creates a layered hidraw device, * so it can detect when the client is running and then: * - it will not send any command to the controller. * - this input device will be removed, to avoid double input of the same * user action. * When the client is closed, this input device will be created again. * * For additional functions, such as changing the right-pad margin or switching * the led, you can use the user-space tool at: * * https://github.com/rodrigorc/steamctrl
*/
/* Touch pads are 40 mm in diameter and 65535 units */ #define STEAM_PAD_RESOLUTION 1638 /* Trigger runs are about 5 mm and 256 units */ #define STEAM_TRIGGER_RESOLUTION 51 /* Joystick runs are about 5 mm and 256 units */ #define STEAM_JOYSTICK_RESOLUTION 51 /* Trigger runs are about 6 mm and 32768 units */ #define STEAM_DECK_TRIGGER_RESOLUTION 5461 /* Joystick runs are about 5 mm and 32768 units */ #define STEAM_DECK_JOYSTICK_RESOLUTION 6553 /* Accelerometer has 16 bit resolution and a range of +/- 2g */ #define STEAM_DECK_ACCEL_RES_PER_G 16384 #define STEAM_DECK_ACCEL_RANGE 32768 #define STEAM_DECK_ACCEL_FUZZ 32 /* Gyroscope has 16 bit resolution and a range of +/- 2000 dps */ #define STEAM_DECK_GYRO_RES_PER_DPS 16 #define STEAM_DECK_GYRO_RANGE 32768 #define STEAM_DECK_GYRO_FUZZ 1
#define STEAM_PAD_FUZZ 256
/* * Commands that can be sent in a feature report. * Thanks to Valve and SDL for the names.
*/ enum {
ID_SET_DIGITAL_MAPPINGS = 0x80,
ID_CLEAR_DIGITAL_MAPPINGS = 0x81,
ID_GET_DIGITAL_MAPPINGS = 0x82,
ID_GET_ATTRIBUTES_VALUES = 0x83,
ID_GET_ATTRIBUTE_LABEL = 0x84,
ID_SET_DEFAULT_DIGITAL_MAPPINGS = 0x85,
ID_FACTORY_RESET = 0x86,
ID_SET_SETTINGS_VALUES = 0x87,
ID_CLEAR_SETTINGS_VALUES = 0x88,
ID_GET_SETTINGS_VALUES = 0x89,
ID_GET_SETTING_LABEL = 0x8A,
ID_GET_SETTINGS_MAXS = 0x8B,
ID_GET_SETTINGS_DEFAULTS = 0x8C,
ID_SET_CONTROLLER_MODE = 0x8D,
ID_LOAD_DEFAULT_SETTINGS = 0x8E,
ID_TRIGGER_HAPTIC_PULSE = 0x8F,
ID_TURN_OFF_CONTROLLER = 0x9F,
staticint steam_recv_report(struct steam_device *steam,
u8 *data, int size)
{ struct hid_report *r;
u8 *buf; int ret;
r = steam->hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0]; if (!r) {
hid_err(steam->hdev, "No HID_FEATURE_REPORT submitted - nothing to read\n"); return -EINVAL;
}
if (hid_report_len(r) < 64) return -EINVAL;
buf = hid_alloc_report_buf(r, GFP_KERNEL); if (!buf) return -ENOMEM;
/* * The report ID is always 0, so strip the first byte from the output. * hid_report_len() is not counting the report ID, so +1 to the length * or else we get a EOVERFLOW. We are safe from a buffer overflow * because hid_alloc_report_buf() allocates +7 bytes.
*/
ret = hid_hw_raw_request(steam->hdev, 0x00,
buf, hid_report_len(r) + 1,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT); if (ret > 0)
memcpy(data, buf + 1, min(size, ret - 1));
kfree(buf); return ret;
}
staticint steam_send_report(struct steam_device *steam,
u8 *cmd, int size)
{ struct hid_report *r;
u8 *buf; unsignedint retries = 50; int ret;
r = steam->hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0]; if (!r) {
hid_err(steam->hdev, "No HID_FEATURE_REPORT submitted - nothing to read\n"); return -EINVAL;
}
if (hid_report_len(r) < 64) return -EINVAL;
buf = hid_alloc_report_buf(r, GFP_KERNEL); if (!buf) return -ENOMEM;
/* The report ID is always 0 */
memcpy(buf + 1, cmd, size);
/* * Sometimes the wireless controller fails with EPIPE * when sending a feature report. * Doing a HID_REQ_GET_REPORT and waiting for a while * seems to fix that.
*/ do {
ret = hid_hw_raw_request(steam->hdev, 0,
buf, max(size, 64) + 1,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT); if (ret != -EPIPE) break;
msleep(20);
} while (--retries);
va_start(args, steam); for (;;) {
reg = va_arg(args, int); if (reg == 0) break;
val = va_arg(args, int);
cmd[cmd[1] + 2] = reg;
cmd[cmd[1] + 3] = val & 0xff;
cmd[cmd[1] + 4] = val >> 8;
cmd[1] += 3;
}
va_end(args);
ret = steam_send_report(steam, cmd, 2 + cmd[1]); if (ret < 0) return ret;
/* * Sometimes a lingering report for this command can * get read back instead of the last set report if * this isn't explicitly queried
*/ return steam_recv_report(steam, cmd, 2 + cmd[1]);
}
/* * This command requests the wireless adaptor to post an event * with the connection status. Useful if this driver is loaded when * the controller is already connected.
*/ staticinlineint steam_request_conn_status(struct steam_device *steam)
{ int ret;
mutex_lock(&steam->report_mutex);
ret = steam_send_report_byte(steam, ID_DONGLE_GET_WIRELESS_STATE);
mutex_unlock(&steam->report_mutex); return ret;
}
/* * Send a haptic pulse to the trackpads * Duration and interval are measured in microseconds, count is the number * of pulses to send for duration time with interval microseconds between them * and gain is measured in decibels, ranging from -24 to +6
*/ staticinlineint steam_haptic_pulse(struct steam_device *steam, u8 pad,
u16 duration, u16 interval, u16 count, u8 gain)
{ int ret;
u8 report[10] = {ID_TRIGGER_HAPTIC_PULSE, 8};
/* Left and right are swapped on this report for legacy reasons */ if (pad < STEAM_PAD_BOTH)
pad ^= 1;
/* * Disabling lizard mode automatically is only done on the Steam * Controller. On the Steam Deck, this is toggled manually by holding * the options button instead, handled by steam_mode_switch_cb.
*/ if (!(steam->quirks & STEAM_QUIRK_DECK)) {
spin_lock_irqsave(&steam->lock, flags);
set_lizard_mode = !steam->client_opened && lizard_mode;
spin_unlock_irqrestore(&steam->lock, flags); if (set_lizard_mode)
steam_set_lizard_mode(steam, false);
}
/* avoid the warning of 0% battery while waiting for the first info */
spin_lock_irqsave(&steam->lock, flags);
steam->voltage = 3000;
steam->battery_charge = 100;
spin_unlock_irqrestore(&steam->lock, flags);
battery = power_supply_register(&steam->hdev->dev,
&steam->battery_desc, &battery_cfg); if (IS_ERR(battery)) {
ret = PTR_ERR(battery);
hid_err(steam->hdev, "%s:power_supply_register failed with error %d\n",
__func__, ret); return ret;
}
rcu_assign_pointer(steam->battery, battery);
power_supply_powers(battery, &steam->hdev->dev); return 0;
}
/* * This function can be called several times in a row with the * wireless adaptor, without steam_unregister() between them, because * another client send a get_connection_status command, for example. * The battery and serial number are set just once per device.
*/ if (!steam->serial_no[0]) { /* * Unlikely, but getting the serial could fail, and it is not so * important, so make up a serial number and go on.
*/ if (steam_get_serial(steam) < 0)
strscpy(steam->serial_no, "XXXXXXXXXX", sizeof(steam->serial_no));
/* * The wired device creates 3 interfaces: * 0: emulated mouse. * 1: emulated keyboard. * 2: the real game pad. * The wireless device creates 5 interfaces: * 0: emulated keyboard. * 1-4: slots where up to 4 real game pads will be connected to. * We know which one is the real gamepad interface because they are the * only ones with a feature report.
*/
rep_enum = &hdev->report_enum[HID_FEATURE_REPORT]; return !list_empty(&rep_enum->report_list);
}
client_hdev = hid_allocate_device(); if (IS_ERR(client_hdev)) return client_hdev;
client_hdev->ll_driver = &steam_client_ll_driver;
client_hdev->dev.parent = hdev->dev.parent;
client_hdev->bus = hdev->bus;
client_hdev->vendor = hdev->vendor;
client_hdev->product = hdev->product;
client_hdev->version = hdev->version;
client_hdev->type = hdev->type;
client_hdev->country = hdev->country;
strscpy(client_hdev->name, hdev->name, sizeof(client_hdev->name));
strscpy(client_hdev->phys, hdev->phys, sizeof(client_hdev->phys)); /* * Since we use the same device info than the real interface to * trick userspace, we will be calling steam_probe recursively. * We need to recognize the client interface somehow.
*/
client_hdev->group = HID_GROUP_STEAM; return client_hdev;
}
ret = hid_parse(hdev); if (ret) {
hid_err(hdev, "%s:parse of hid interface failed\n", __func__); return ret;
}
/* * The virtual client_dev is only used for hidraw. * Also avoid the recursive probe.
*/ if (hdev->group == HID_GROUP_STEAM) return hid_hw_start(hdev, HID_CONNECT_HIDRAW); /* * The non-valve interfaces (mouse and keyboard emulation) are * connected without changes.
*/ if (!steam_is_valve_interface(hdev)) return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
steam = devm_kzalloc(&hdev->dev, sizeof(*steam), GFP_KERNEL); if (!steam) return -ENOMEM;
/* * With the real steam controller interface, do not connect hidraw. * Instead, create the client_hid and connect that.
*/
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDRAW); if (ret) goto err_cancel_work;
ret = hid_hw_open(hdev); if (ret) {
hid_err(hdev, "%s:hid_hw_open\n",
__func__); goto err_hw_stop;
}
if (steam->quirks & STEAM_QUIRK_WIRELESS) {
hid_info(hdev, "Steam wireless receiver connected"); /* If using a wireless adaptor ask for connection status */
steam->connected = false;
steam_request_conn_status(steam);
} else { /* A wired connection is always present */
steam->connected = true;
ret = steam_register(steam); if (ret) {
hid_err(hdev, "%s:steam_register failed with error %d\n",
__func__, ret); goto err_hw_close;
}
}
steam->client_hdev = steam_create_client_hid(hdev); if (IS_ERR(steam->client_hdev)) {
ret = PTR_ERR(steam->client_hdev); goto err_steam_unregister;
}
steam->client_hdev->driver_data = steam;
ret = hid_add_device(steam->client_hdev); if (ret) goto err_destroy;
/* * Some input data in the protocol has the opposite sign. * Clamp the values to 32767..-32767 so that the range is * symmetrical and can be negated safely.
*/ staticinline s16 steam_le16(u8 *data)
{
s16 x = (s16) le16_to_cpup((__le16 *)data);
return x == -32768 ? -32767 : x;
}
/* * The size for this message payload is 60. * The known values are: * (* values are not sent through wireless) * (* accelerator/gyro is disabled by default) * Offset| Type | Mapped to |Meaning * -------+-------+-----------+-------------------------- * 4-7 | u32 | -- | sequence number * 8-10 | 24bit | see below | buttons * 11 | u8 | ABS_HAT2Y | left trigger * 12 | u8 | ABS_HAT2X | right trigger * 13-15 | -- | -- | always 0 * 16-17 | s16 | ABS_X/ABS_HAT0X | X value * 18-19 | s16 | ABS_Y/ABS_HAT0Y | Y value * 20-21 | s16 | ABS_RX | right-pad X value * 22-23 | s16 | ABS_RY | right-pad Y value * 24-25 | s16 | -- | * left trigger * 26-27 | s16 | -- | * right trigger * 28-29 | s16 | -- | * accelerometer X value * 30-31 | s16 | -- | * accelerometer Y value * 32-33 | s16 | -- | * accelerometer Z value * 34-35 | s16 | -- | gyro X value * 36-36 | s16 | -- | gyro Y value * 38-39 | s16 | -- | gyro Z value * 40-41 | s16 | -- | quaternion W value * 42-43 | s16 | -- | quaternion X value * 44-45 | s16 | -- | quaternion Y value * 46-47 | s16 | -- | quaternion Z value * 48-49 | -- | -- | always 0 * 50-51 | s16 | -- | * left trigger (uncalibrated) * 52-53 | s16 | -- | * right trigger (uncalibrated) * 54-55 | s16 | -- | * joystick X value (uncalibrated) * 56-57 | s16 | -- | * joystick Y value (uncalibrated) * 58-59 | s16 | -- | * left-pad X value * 60-61 | s16 | -- | * left-pad Y value * 62-63 | u16 | -- | * battery voltage * * The buttons are: * Bit | Mapped to | Description * ------+------------+-------------------------------- * 8.0 | BTN_TR2 | right trigger fully pressed * 8.1 | BTN_TL2 | left trigger fully pressed * 8.2 | BTN_TR | right shoulder * 8.3 | BTN_TL | left shoulder * 8.4 | BTN_Y | button Y * 8.5 | BTN_B | button B * 8.6 | BTN_X | button X * 8.7 | BTN_A | button A * 9.0 | BTN_DPAD_UP | left-pad up * 9.1 | BTN_DPAD_RIGHT | left-pad right * 9.2 | BTN_DPAD_LEFT | left-pad left * 9.3 | BTN_DPAD_DOWN | left-pad down * 9.4 | BTN_SELECT | menu left * 9.5 | BTN_MODE | steam logo * 9.6 | BTN_START | menu right * 9.7 | BTN_GRIPL | left back lever * 10.0 | BTN_GRIPR | right back lever * 10.1 | -- | left-pad clicked * 10.2 | BTN_THUMBR | right-pad clicked * 10.3 | BTN_THUMB | left-pad touched (but see explanation below) * 10.4 | BTN_THUMB2 | right-pad touched * 10.5 | -- | unknown * 10.6 | BTN_THUMBL | joystick clicked * 10.7 | -- | lpad_and_joy
*/
/* * These two bits tells how to interpret the values X and Y. * lpad_and_joy tells that the joystick and the lpad are used at the * same time. * lpad_touched tells whether X/Y are to be read as lpad coord or * joystick values. * (lpad_touched || lpad_and_joy) tells if the lpad is really touched.
*/
lpad_touched = b10 & BIT(3);
lpad_and_joy = b10 & BIT(7);
x = steam_le16(data + 16);
y = -steam_le16(data + 18);
input_report_abs(input, lpad_touched ? ABS_HAT0X : ABS_X, x);
input_report_abs(input, lpad_touched ? ABS_HAT0Y : ABS_Y, y); /* Check if joystick is centered */ if (lpad_touched && !lpad_and_joy) {
input_report_abs(input, ABS_X, 0);
input_report_abs(input, ABS_Y, 0);
} /* Check if lpad is untouched */ if (!(lpad_touched || lpad_and_joy)) {
input_report_abs(input, ABS_HAT0X, 0);
input_report_abs(input, ABS_HAT0Y, 0);
}
staticvoid steam_do_deck_sensors_event(struct steam_device *steam, struct input_dev *sensors, u8 *data)
{ /* * The deck input report is received every 4 ms on average, * with a jitter of +/- 4 ms even though the USB descriptor claims * that it uses 1 kHz. * Since the HID report does not include a sensor timestamp, * use a fixed increment here.
*/
steam->sensor_timestamp_us += 4000;
if (steam->client_opened)
hid_input_report(steam->client_hdev, HID_FEATURE_REPORT,
data, size, 0); /* * All messages are size=64, all values little-endian. * The format is: * Offset| Meaning * -------+-------------------------------------------- * 0-1 | always 0x01, 0x00, maybe protocol version? * 2 | type of message * 3 | length of the real payload (not checked) * 4-n | payload data, depends on the type * * There are these known types of message: * 0x01: input data (60 bytes) * 0x03: wireless connect/disconnect (1 byte) * 0x04: battery status (11 bytes) * 0x09: Steam Deck input data (56 bytes)
*/
module_param_cb(lizard_mode, &steam_lizard_mode_ops, &lizard_mode, 0644);
MODULE_PARM_DESC(lizard_mode, "Enable mouse and keyboard emulation (lizard mode) when the gamepad is not in use");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.