diff options
Diffstat (limited to 'drivers/hid/hid-steam.c')
| -rw-r--r-- | drivers/hid/hid-steam.c | 788 |
1 files changed, 590 insertions, 198 deletions
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c index b110818fc945..197126d6e081 100644 --- a/drivers/hid/hid-steam.c +++ b/drivers/hid/hid-steam.c @@ -45,6 +45,7 @@ #include <linux/power_supply.h> #include "hid-ids.h" +MODULE_DESCRIPTION("HID driver for Valve Steam Controller"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>"); @@ -66,66 +67,237 @@ static LIST_HEAD(steam_devices); #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 for some valuable hints. + * Thanks to Valve and SDL for the names. */ -#define STEAM_CMD_SET_MAPPINGS 0x80 -#define STEAM_CMD_CLEAR_MAPPINGS 0x81 -#define STEAM_CMD_GET_MAPPINGS 0x82 -#define STEAM_CMD_GET_ATTRIB 0x83 -#define STEAM_CMD_GET_ATTRIB_LABEL 0x84 -#define STEAM_CMD_DEFAULT_MAPPINGS 0x85 -#define STEAM_CMD_FACTORY_RESET 0x86 -#define STEAM_CMD_WRITE_REGISTER 0x87 -#define STEAM_CMD_CLEAR_REGISTER 0x88 -#define STEAM_CMD_READ_REGISTER 0x89 -#define STEAM_CMD_GET_REGISTER_LABEL 0x8a -#define STEAM_CMD_GET_REGISTER_MAX 0x8b -#define STEAM_CMD_GET_REGISTER_DEFAULT 0x8c -#define STEAM_CMD_SET_MODE 0x8d -#define STEAM_CMD_DEFAULT_MOUSE 0x8e -#define STEAM_CMD_FORCEFEEDBAK 0x8f -#define STEAM_CMD_REQUEST_COMM_STATUS 0xb4 -#define STEAM_CMD_GET_SERIAL 0xae -#define STEAM_CMD_HAPTIC_RUMBLE 0xeb - -/* Some useful register ids */ -#define STEAM_REG_LPAD_MODE 0x07 -#define STEAM_REG_RPAD_MODE 0x08 -#define STEAM_REG_RPAD_MARGIN 0x18 -#define STEAM_REG_LED 0x2d -#define STEAM_REG_GYRO_MODE 0x30 -#define STEAM_REG_LPAD_CLICK_PRESSURE 0x34 -#define STEAM_REG_RPAD_CLICK_PRESSURE 0x35 - -/* Raw event identifiers */ -#define STEAM_EV_INPUT_DATA 0x01 -#define STEAM_EV_CONNECT 0x03 -#define STEAM_EV_BATTERY 0x04 -#define STEAM_EV_DECK_INPUT_DATA 0x09 +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, + + ID_GET_DEVICE_INFO = 0xA1, + + ID_CALIBRATE_TRACKPADS = 0xA7, + ID_RESERVED_0 = 0xA8, + ID_SET_SERIAL_NUMBER = 0xA9, + ID_GET_TRACKPAD_CALIBRATION = 0xAA, + ID_GET_TRACKPAD_FACTORY_CALIBRATION = 0xAB, + ID_GET_TRACKPAD_RAW_DATA = 0xAC, + ID_ENABLE_PAIRING = 0xAD, + ID_GET_STRING_ATTRIBUTE = 0xAE, + ID_RADIO_ERASE_RECORDS = 0xAF, + ID_RADIO_WRITE_RECORD = 0xB0, + ID_SET_DONGLE_SETTING = 0xB1, + ID_DONGLE_DISCONNECT_DEVICE = 0xB2, + ID_DONGLE_COMMIT_DEVICE = 0xB3, + ID_DONGLE_GET_WIRELESS_STATE = 0xB4, + ID_CALIBRATE_GYRO = 0xB5, + ID_PLAY_AUDIO = 0xB6, + ID_AUDIO_UPDATE_START = 0xB7, + ID_AUDIO_UPDATE_DATA = 0xB8, + ID_AUDIO_UPDATE_COMPLETE = 0xB9, + ID_GET_CHIPID = 0xBA, + + ID_CALIBRATE_JOYSTICK = 0xBF, + ID_CALIBRATE_ANALOG_TRIGGERS = 0xC0, + ID_SET_AUDIO_MAPPING = 0xC1, + ID_CHECK_GYRO_FW_LOAD = 0xC2, + ID_CALIBRATE_ANALOG = 0xC3, + ID_DONGLE_GET_CONNECTED_SLOTS = 0xC4, + + ID_RESET_IMU = 0xCE, + + ID_TRIGGER_HAPTIC_CMD = 0xEA, + ID_TRIGGER_RUMBLE_CMD = 0xEB, +}; + +/* Settings IDs */ +enum { + /* 0 */ + SETTING_MOUSE_SENSITIVITY, + SETTING_MOUSE_ACCELERATION, + SETTING_TRACKBALL_ROTATION_ANGLE, + SETTING_HAPTIC_INTENSITY_UNUSED, + SETTING_LEFT_GAMEPAD_STICK_ENABLED, + SETTING_RIGHT_GAMEPAD_STICK_ENABLED, + SETTING_USB_DEBUG_MODE, + SETTING_LEFT_TRACKPAD_MODE, + SETTING_RIGHT_TRACKPAD_MODE, + SETTING_MOUSE_POINTER_ENABLED, + + /* 10 */ + SETTING_DPAD_DEADZONE, + SETTING_MINIMUM_MOMENTUM_VEL, + SETTING_MOMENTUM_DECAY_AMMOUNT, + SETTING_TRACKPAD_RELATIVE_MODE_TICKS_PER_PIXEL, + SETTING_HAPTIC_INCREMENT, + SETTING_DPAD_ANGLE_SIN, + SETTING_DPAD_ANGLE_COS, + SETTING_MOMENTUM_VERTICAL_DIVISOR, + SETTING_MOMENTUM_MAXIMUM_VELOCITY, + SETTING_TRACKPAD_Z_ON, + + /* 20 */ + SETTING_TRACKPAD_Z_OFF, + SETTING_SENSITIVY_SCALE_AMMOUNT, + SETTING_LEFT_TRACKPAD_SECONDARY_MODE, + SETTING_RIGHT_TRACKPAD_SECONDARY_MODE, + SETTING_SMOOTH_ABSOLUTE_MOUSE, + SETTING_STEAMBUTTON_POWEROFF_TIME, + SETTING_UNUSED_1, + SETTING_TRACKPAD_OUTER_RADIUS, + SETTING_TRACKPAD_Z_ON_LEFT, + SETTING_TRACKPAD_Z_OFF_LEFT, + + /* 30 */ + SETTING_TRACKPAD_OUTER_SPIN_VEL, + SETTING_TRACKPAD_OUTER_SPIN_RADIUS, + SETTING_TRACKPAD_OUTER_SPIN_HORIZONTAL_ONLY, + SETTING_TRACKPAD_RELATIVE_MODE_DEADZONE, + SETTING_TRACKPAD_RELATIVE_MODE_MAX_VEL, + SETTING_TRACKPAD_RELATIVE_MODE_INVERT_Y, + SETTING_TRACKPAD_DOUBLE_TAP_BEEP_ENABLED, + SETTING_TRACKPAD_DOUBLE_TAP_BEEP_PERIOD, + SETTING_TRACKPAD_DOUBLE_TAP_BEEP_COUNT, + SETTING_TRACKPAD_OUTER_RADIUS_RELEASE_ON_TRANSITION, + + /* 40 */ + SETTING_RADIAL_MODE_ANGLE, + SETTING_HAPTIC_INTENSITY_MOUSE_MODE, + SETTING_LEFT_DPAD_REQUIRES_CLICK, + SETTING_RIGHT_DPAD_REQUIRES_CLICK, + SETTING_LED_BASELINE_BRIGHTNESS, + SETTING_LED_USER_BRIGHTNESS, + SETTING_ENABLE_RAW_JOYSTICK, + SETTING_ENABLE_FAST_SCAN, + SETTING_IMU_MODE, + SETTING_WIRELESS_PACKET_VERSION, + + /* 50 */ + SETTING_SLEEP_INACTIVITY_TIMEOUT, + SETTING_TRACKPAD_NOISE_THRESHOLD, + SETTING_LEFT_TRACKPAD_CLICK_PRESSURE, + SETTING_RIGHT_TRACKPAD_CLICK_PRESSURE, + SETTING_LEFT_BUMPER_CLICK_PRESSURE, + SETTING_RIGHT_BUMPER_CLICK_PRESSURE, + SETTING_LEFT_GRIP_CLICK_PRESSURE, + SETTING_RIGHT_GRIP_CLICK_PRESSURE, + SETTING_LEFT_GRIP2_CLICK_PRESSURE, + SETTING_RIGHT_GRIP2_CLICK_PRESSURE, + + /* 60 */ + SETTING_PRESSURE_MODE, + SETTING_CONTROLLER_TEST_MODE, + SETTING_TRIGGER_MODE, + SETTING_TRACKPAD_Z_THRESHOLD, + SETTING_FRAME_RATE, + SETTING_TRACKPAD_FILT_CTRL, + SETTING_TRACKPAD_CLIP, + SETTING_DEBUG_OUTPUT_SELECT, + SETTING_TRIGGER_THRESHOLD_PERCENT, + SETTING_TRACKPAD_FREQUENCY_HOPPING, + + /* 70 */ + SETTING_HAPTICS_ENABLED, + SETTING_STEAM_WATCHDOG_ENABLE, + SETTING_TIMP_TOUCH_THRESHOLD_ON, + SETTING_TIMP_TOUCH_THRESHOLD_OFF, + SETTING_FREQ_HOPPING, + SETTING_TEST_CONTROL, + SETTING_HAPTIC_MASTER_GAIN_DB, + SETTING_THUMB_TOUCH_THRESH, + SETTING_DEVICE_POWER_STATUS, + SETTING_HAPTIC_INTENSITY, + + /* 80 */ + SETTING_STABILIZER_ENABLED, + SETTING_TIMP_MODE_MTE, +}; + +/* Input report identifiers */ +enum +{ + ID_CONTROLLER_STATE = 1, + ID_CONTROLLER_DEBUG = 2, + ID_CONTROLLER_WIRELESS = 3, + ID_CONTROLLER_STATUS = 4, + ID_CONTROLLER_DEBUG2 = 5, + ID_CONTROLLER_SECONDARY_STATE = 6, + ID_CONTROLLER_BLE_STATE = 7, + ID_CONTROLLER_DECK_STATE = 9 +}; + +/* String attribute identifiers */ +enum { + ATTRIB_STR_BOARD_SERIAL, + ATTRIB_STR_UNIT_SERIAL, +}; /* Values for GYRO_MODE (bitmask) */ -#define STEAM_GYRO_MODE_OFF 0x0000 -#define STEAM_GYRO_MODE_STEERING 0x0001 -#define STEAM_GYRO_MODE_TILT 0x0002 -#define STEAM_GYRO_MODE_SEND_ORIENTATION 0x0004 -#define STEAM_GYRO_MODE_SEND_RAW_ACCEL 0x0008 -#define STEAM_GYRO_MODE_SEND_RAW_GYRO 0x0010 +enum { + SETTING_GYRO_MODE_OFF = 0, + SETTING_GYRO_MODE_STEERING = BIT(0), + SETTING_GYRO_MODE_TILT = BIT(1), + SETTING_GYRO_MODE_SEND_ORIENTATION = BIT(2), + SETTING_GYRO_MODE_SEND_RAW_ACCEL = BIT(3), + SETTING_GYRO_MODE_SEND_RAW_GYRO = BIT(4), +}; + +/* Trackpad modes */ +enum { + TRACKPAD_ABSOLUTE_MOUSE, + TRACKPAD_RELATIVE_MOUSE, + TRACKPAD_DPAD_FOUR_WAY_DISCRETE, + TRACKPAD_DPAD_FOUR_WAY_OVERLAP, + TRACKPAD_DPAD_EIGHT_WAY, + TRACKPAD_RADIAL_MODE, + TRACKPAD_ABSOLUTE_DPAD, + TRACKPAD_NONE, + TRACKPAD_GESTURE_KEYBOARD, +}; + +/* Pad identifiers for the deck */ +#define STEAM_PAD_LEFT 0 +#define STEAM_PAD_RIGHT 1 +#define STEAM_PAD_BOTH 2 /* Other random constants */ -#define STEAM_SERIAL_LEN 10 +#define STEAM_SERIAL_LEN 0x15 struct steam_device { struct list_head list; spinlock_t lock; struct hid_device *hdev, *client_hdev; - struct mutex mutex; - bool client_opened; + struct mutex report_mutex; + unsigned long client_opened; struct input_dev __rcu *input; + struct input_dev __rcu *sensors; unsigned long quirks; struct work_struct work_connect; bool connected; @@ -134,10 +306,14 @@ struct steam_device { struct power_supply __rcu *battery; u8 battery_charge; u16 voltage; - struct delayed_work heartbeat; + struct delayed_work mode_switch; + bool did_mode_switch; + bool gamepad_mode; struct work_struct rumble_work; u16 rumble_left; u16 rumble_right; + unsigned int sensor_timestamp_us; + struct work_struct unregister_work; }; static int steam_recv_report(struct steam_device *steam, @@ -226,13 +402,13 @@ static inline int steam_send_report_byte(struct steam_device *steam, u8 cmd) return steam_send_report(steam, &cmd, 1); } -static int steam_write_registers(struct steam_device *steam, +static int steam_write_settings(struct steam_device *steam, /* u8 reg, u16 val */...) { /* Send: 0x87 len (reg valLo valHi)* */ u8 reg; u16 val; - u8 cmd[64] = {STEAM_CMD_WRITE_REGISTER, 0x00}; + u8 cmd[64] = {ID_SET_SETTINGS_VALUES, 0x00}; int ret; va_list args; @@ -265,23 +441,29 @@ static int steam_get_serial(struct steam_device *steam) { /* * Send: 0xae 0x15 0x01 - * Recv: 0xae 0x15 0x01 serialnumber (10 chars) + * Recv: 0xae 0x15 0x01 serialnumber */ - int ret; - u8 cmd[] = {STEAM_CMD_GET_SERIAL, 0x15, 0x01}; + int ret = 0; + u8 cmd[] = {ID_GET_STRING_ATTRIBUTE, sizeof(steam->serial_no), ATTRIB_STR_UNIT_SERIAL}; u8 reply[3 + STEAM_SERIAL_LEN + 1]; + mutex_lock(&steam->report_mutex); ret = steam_send_report(steam, cmd, sizeof(cmd)); if (ret < 0) - return ret; + goto out; ret = steam_recv_report(steam, reply, sizeof(reply)); if (ret < 0) - return ret; - if (reply[0] != 0xae || reply[1] != 0x15 || reply[2] != 0x01) - return -EIO; + goto out; + if (reply[0] != ID_GET_STRING_ATTRIBUTE || reply[1] < 1 || + reply[1] > sizeof(steam->serial_no) || reply[2] != ATTRIB_STR_UNIT_SERIAL) { + ret = -EIO; + goto out; + } reply[3 + STEAM_SERIAL_LEN] = 0; - strscpy(steam->serial_no, reply + 3, sizeof(steam->serial_no)); - return 0; + strscpy(steam->serial_no, reply + 3, reply[1]); +out: + mutex_unlock(&steam->report_mutex); + return ret; } /* @@ -291,14 +473,50 @@ static int steam_get_serial(struct steam_device *steam) */ static inline int steam_request_conn_status(struct steam_device *steam) { - return steam_send_report_byte(steam, STEAM_CMD_REQUEST_COMM_STATUS); + 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 + */ +static inline int 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; + + report[2] = pad; + report[3] = duration & 0xFF; + report[4] = duration >> 8; + report[5] = interval & 0xFF; + report[6] = interval >> 8; + report[7] = count & 0xFF; + report[8] = count >> 8; + report[9] = gain; + + mutex_lock(&steam->report_mutex); + ret = steam_send_report(steam, report, sizeof(report)); + mutex_unlock(&steam->report_mutex); + return ret; } static inline int steam_haptic_rumble(struct steam_device *steam, u16 intensity, u16 left_speed, u16 right_speed, u8 left_gain, u8 right_gain) { - u8 report[11] = {STEAM_CMD_HAPTIC_RUMBLE, 9}; + int ret; + u8 report[11] = {ID_TRIGGER_RUMBLE_CMD, 9}; report[3] = intensity & 0xFF; report[4] = intensity >> 8; @@ -309,7 +527,10 @@ static inline int steam_haptic_rumble(struct steam_device *steam, report[9] = left_gain; report[10] = right_gain; - return steam_send_report(steam, report, sizeof(report)); + mutex_lock(&steam->report_mutex); + ret = steam_send_report(steam, report, sizeof(report)); + mutex_unlock(&steam->report_mutex); + return ret; } static void steam_haptic_rumble_cb(struct work_struct *work) @@ -335,63 +556,72 @@ static int steam_play_effect(struct input_dev *dev, void *data, static void steam_set_lizard_mode(struct steam_device *steam, bool enable) { + if (steam->gamepad_mode) + enable = false; + + mutex_lock(&steam->report_mutex); if (enable) { /* enable esc, enter, cursors */ - steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MAPPINGS); - /* enable mouse */ - steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MOUSE); - steam_write_registers(steam, - STEAM_REG_RPAD_MARGIN, 0x01, /* enable margin */ - 0); - - cancel_delayed_work_sync(&steam->heartbeat); + steam_send_report_byte(steam, ID_SET_DEFAULT_DIGITAL_MAPPINGS); + /* reset settings */ + steam_send_report_byte(steam, ID_LOAD_DEFAULT_SETTINGS); } else { /* disable esc, enter, cursor */ - steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS); + steam_send_report_byte(steam, ID_CLEAR_DIGITAL_MAPPINGS); if (steam->quirks & STEAM_QUIRK_DECK) { - steam_write_registers(steam, - STEAM_REG_RPAD_MARGIN, 0x00, /* disable margin */ - STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */ - STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */ - STEAM_REG_LPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */ - STEAM_REG_RPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */ + steam_write_settings(steam, + SETTING_LEFT_TRACKPAD_MODE, TRACKPAD_NONE, /* disable mouse */ + SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_NONE, /* disable mouse */ + SETTING_LEFT_TRACKPAD_CLICK_PRESSURE, 0xFFFF, /* disable haptic click */ + SETTING_RIGHT_TRACKPAD_CLICK_PRESSURE, 0xFFFF, /* disable haptic click */ + SETTING_STEAM_WATCHDOG_ENABLE, 0, /* disable watchdog that tests if Steam is active */ 0); - /* - * The Steam Deck has a watchdog that automatically enables - * lizard mode if it doesn't see any traffic for too long - */ - if (!work_busy(&steam->heartbeat.work)) - schedule_delayed_work(&steam->heartbeat, 5 * HZ); } else { - steam_write_registers(steam, - STEAM_REG_RPAD_MARGIN, 0x00, /* disable margin */ - STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */ - STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */ + steam_write_settings(steam, + SETTING_LEFT_TRACKPAD_MODE, TRACKPAD_NONE, /* disable mouse */ + SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_NONE, /* disable mouse */ 0); } } + mutex_unlock(&steam->report_mutex); } static int steam_input_open(struct input_dev *dev) { struct steam_device *steam = input_get_drvdata(dev); + unsigned long flags; + bool set_lizard_mode; + + /* + * 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); + } - mutex_lock(&steam->mutex); - if (!steam->client_opened && lizard_mode) - steam_set_lizard_mode(steam, false); - mutex_unlock(&steam->mutex); return 0; } static void steam_input_close(struct input_dev *dev) { struct steam_device *steam = input_get_drvdata(dev); + unsigned long flags; + bool set_lizard_mode; - mutex_lock(&steam->mutex); - if (!steam->client_opened && lizard_mode) - steam_set_lizard_mode(steam, true); - mutex_unlock(&steam->mutex); + 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, true); + } } static enum power_supply_property steam_battery_props[] = { @@ -525,15 +755,12 @@ static int steam_input_register(struct steam_device *steam) input_set_capability(input, EV_KEY, BTN_THUMBL); input_set_capability(input, EV_KEY, BTN_THUMB); input_set_capability(input, EV_KEY, BTN_THUMB2); + input_set_capability(input, EV_KEY, BTN_GRIPL); + input_set_capability(input, EV_KEY, BTN_GRIPR); if (steam->quirks & STEAM_QUIRK_DECK) { input_set_capability(input, EV_KEY, BTN_BASE); - input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY1); - input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY2); - input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY3); - input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY4); - } else { - input_set_capability(input, EV_KEY, BTN_GEAR_DOWN); - input_set_capability(input, EV_KEY, BTN_GEAR_UP); + input_set_capability(input, EV_KEY, BTN_GRIPL2); + input_set_capability(input, EV_KEY, BTN_GRIPR2); } input_set_abs_params(input, ABS_X, -32767, 32767, 0, 0); @@ -604,6 +831,74 @@ input_register_fail: return ret; } +static int steam_sensors_register(struct steam_device *steam) +{ + struct hid_device *hdev = steam->hdev; + struct input_dev *sensors; + int ret; + + if (!(steam->quirks & STEAM_QUIRK_DECK)) + return 0; + + rcu_read_lock(); + sensors = rcu_dereference(steam->sensors); + rcu_read_unlock(); + if (sensors) { + dbg_hid("%s: already connected\n", __func__); + return 0; + } + + sensors = input_allocate_device(); + if (!sensors) + return -ENOMEM; + + input_set_drvdata(sensors, steam); + sensors->dev.parent = &hdev->dev; + + sensors->name = "Steam Deck Motion Sensors"; + sensors->phys = hdev->phys; + sensors->uniq = steam->serial_no; + sensors->id.bustype = hdev->bus; + sensors->id.vendor = hdev->vendor; + sensors->id.product = hdev->product; + sensors->id.version = hdev->version; + + __set_bit(INPUT_PROP_ACCELEROMETER, sensors->propbit); + __set_bit(EV_MSC, sensors->evbit); + __set_bit(MSC_TIMESTAMP, sensors->mscbit); + + input_set_abs_params(sensors, ABS_X, -STEAM_DECK_ACCEL_RANGE, + STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0); + input_set_abs_params(sensors, ABS_Y, -STEAM_DECK_ACCEL_RANGE, + STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0); + input_set_abs_params(sensors, ABS_Z, -STEAM_DECK_ACCEL_RANGE, + STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0); + input_abs_set_res(sensors, ABS_X, STEAM_DECK_ACCEL_RES_PER_G); + input_abs_set_res(sensors, ABS_Y, STEAM_DECK_ACCEL_RES_PER_G); + input_abs_set_res(sensors, ABS_Z, STEAM_DECK_ACCEL_RES_PER_G); + + input_set_abs_params(sensors, ABS_RX, -STEAM_DECK_GYRO_RANGE, + STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0); + input_set_abs_params(sensors, ABS_RY, -STEAM_DECK_GYRO_RANGE, + STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0); + input_set_abs_params(sensors, ABS_RZ, -STEAM_DECK_GYRO_RANGE, + STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0); + input_abs_set_res(sensors, ABS_RX, STEAM_DECK_GYRO_RES_PER_DPS); + input_abs_set_res(sensors, ABS_RY, STEAM_DECK_GYRO_RES_PER_DPS); + input_abs_set_res(sensors, ABS_RZ, STEAM_DECK_GYRO_RES_PER_DPS); + + ret = input_register_device(sensors); + if (ret) + goto sensors_register_fail; + + rcu_assign_pointer(steam->sensors, sensors); + return 0; + +sensors_register_fail: + input_free_device(sensors); + return ret; +} + static void steam_input_unregister(struct steam_device *steam) { struct input_dev *input; @@ -617,6 +912,24 @@ static void steam_input_unregister(struct steam_device *steam) input_unregister_device(input); } +static void steam_sensors_unregister(struct steam_device *steam) +{ + struct input_dev *sensors; + + if (!(steam->quirks & STEAM_QUIRK_DECK)) + return; + + rcu_read_lock(); + sensors = rcu_dereference(steam->sensors); + rcu_read_unlock(); + + if (!sensors) + return; + RCU_INIT_POINTER(steam->sensors, NULL); + synchronize_rcu(); + input_unregister_device(sensors); +} + static void steam_battery_unregister(struct steam_device *steam) { struct power_supply *battery; @@ -635,7 +948,8 @@ static void steam_battery_unregister(struct steam_device *steam) static int steam_register(struct steam_device *steam) { int ret; - bool client_opened; + unsigned long client_opened; + unsigned long flags; /* * This function can be called several times in a row with the @@ -648,11 +962,9 @@ static int steam_register(struct steam_device *steam) * Unlikely, but getting the serial could fail, and it is not so * important, so make up a serial number and go on. */ - mutex_lock(&steam->mutex); if (steam_get_serial(steam) < 0) strscpy(steam->serial_no, "XXXXXXXXXX", sizeof(steam->serial_no)); - mutex_unlock(&steam->mutex); hid_info(steam->hdev, "Steam Controller '%s' connected", steam->serial_no); @@ -667,23 +979,31 @@ static int steam_register(struct steam_device *steam) mutex_unlock(&steam_devices_lock); } - mutex_lock(&steam->mutex); + spin_lock_irqsave(&steam->lock, flags); client_opened = steam->client_opened; - if (!client_opened) - steam_set_lizard_mode(steam, lizard_mode); - mutex_unlock(&steam->mutex); + spin_unlock_irqrestore(&steam->lock, flags); - if (!client_opened) + if (!client_opened) { + steam_set_lizard_mode(steam, lizard_mode); ret = steam_input_register(steam); - else - ret = 0; + if (ret != 0) + goto steam_register_input_fail; + ret = steam_sensors_register(steam); + if (ret != 0) + goto steam_register_sensors_fail; + } + return 0; +steam_register_sensors_fail: + steam_input_unregister(steam); +steam_register_input_fail: return ret; } static void steam_unregister(struct steam_device *steam) { steam_battery_unregister(steam); + steam_sensors_unregister(steam); steam_input_unregister(steam); if (steam->serial_no[0]) { hid_info(steam->hdev, "Steam Controller '%s' disconnected", @@ -719,6 +1039,59 @@ static void steam_work_connect_cb(struct work_struct *work) } } +static void steam_mode_switch_cb(struct work_struct *work) +{ + struct steam_device *steam = container_of(to_delayed_work(work), + struct steam_device, mode_switch); + unsigned long flags; + bool client_opened; + if (!lizard_mode) + return; + + steam->gamepad_mode = !steam->gamepad_mode; + if (steam->gamepad_mode) + steam_set_lizard_mode(steam, false); + else { + spin_lock_irqsave(&steam->lock, flags); + client_opened = steam->client_opened; + spin_unlock_irqrestore(&steam->lock, flags); + if (!client_opened) + steam_set_lizard_mode(steam, lizard_mode); + } + + steam_haptic_pulse(steam, STEAM_PAD_RIGHT, 0x190, 0, 1, 0); + if (steam->gamepad_mode) { + steam_haptic_pulse(steam, STEAM_PAD_LEFT, 0x14D, 0x14D, 0x2D, 0); + } else { + steam_haptic_pulse(steam, STEAM_PAD_LEFT, 0x1F4, 0x1F4, 0x1E, 0); + } +} + +static void steam_work_unregister_cb(struct work_struct *work) +{ + struct steam_device *steam = container_of(work, struct steam_device, + unregister_work); + unsigned long flags; + bool connected; + bool opened; + + spin_lock_irqsave(&steam->lock, flags); + opened = steam->client_opened; + connected = steam->connected; + spin_unlock_irqrestore(&steam->lock, flags); + + if (connected) { + if (opened) { + steam_sensors_unregister(steam); + steam_input_unregister(steam); + } else { + steam_set_lizard_mode(steam, lizard_mode); + steam_input_register(steam); + steam_sensors_register(steam); + } + } +} + static bool steam_is_valve_interface(struct hid_device *hdev) { struct hid_report_enum *rep_enum; @@ -738,22 +1111,6 @@ static bool steam_is_valve_interface(struct hid_device *hdev) return !list_empty(&rep_enum->report_list); } -static void steam_lizard_mode_heartbeat(struct work_struct *work) -{ - struct steam_device *steam = container_of(work, struct steam_device, - heartbeat.work); - - mutex_lock(&steam->mutex); - if (!steam->client_opened && steam->client_hdev) { - steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS); - steam_write_registers(steam, - STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */ - 0); - schedule_delayed_work(&steam->heartbeat, 5 * HZ); - } - mutex_unlock(&steam->mutex); -} - static int steam_client_ll_parse(struct hid_device *hdev) { struct steam_device *steam = hdev->driver_data; @@ -774,12 +1131,13 @@ static void steam_client_ll_stop(struct hid_device *hdev) static int steam_client_ll_open(struct hid_device *hdev) { struct steam_device *steam = hdev->driver_data; + unsigned long flags; - mutex_lock(&steam->mutex); - steam->client_opened = true; - mutex_unlock(&steam->mutex); + spin_lock_irqsave(&steam->lock, flags); + steam->client_opened++; + spin_unlock_irqrestore(&steam->lock, flags); - steam_input_unregister(steam); + schedule_work(&steam->unregister_work); return 0; } @@ -789,20 +1147,12 @@ static void steam_client_ll_close(struct hid_device *hdev) struct steam_device *steam = hdev->driver_data; unsigned long flags; - bool connected; spin_lock_irqsave(&steam->lock, flags); - connected = steam->connected; + steam->client_opened--; spin_unlock_irqrestore(&steam->lock, flags); - mutex_lock(&steam->mutex); - steam->client_opened = false; - if (connected) - steam_set_lizard_mode(steam, lizard_mode); - mutex_unlock(&steam->mutex); - - if (connected) - steam_input_register(steam); + schedule_work(&steam->unregister_work); } static int steam_client_ll_raw_request(struct hid_device *hdev, @@ -881,26 +1231,20 @@ static int steam_probe(struct hid_device *hdev, return hid_hw_start(hdev, HID_CONNECT_DEFAULT); steam = devm_kzalloc(&hdev->dev, sizeof(*steam), GFP_KERNEL); - if (!steam) { - ret = -ENOMEM; - goto steam_alloc_fail; - } + if (!steam) + return -ENOMEM; + steam->hdev = hdev; hid_set_drvdata(hdev, steam); spin_lock_init(&steam->lock); - mutex_init(&steam->mutex); + mutex_init(&steam->report_mutex); steam->quirks = id->driver_data; INIT_WORK(&steam->work_connect, steam_work_connect_cb); + INIT_DELAYED_WORK(&steam->mode_switch, steam_mode_switch_cb); INIT_LIST_HEAD(&steam->list); - INIT_DEFERRABLE_WORK(&steam->heartbeat, steam_lizard_mode_heartbeat); INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb); - - steam->client_hdev = steam_create_client_hid(hdev); - if (IS_ERR(steam->client_hdev)) { - ret = PTR_ERR(steam->client_hdev); - goto client_hdev_fail; - } - steam->client_hdev->driver_data = steam; + steam->sensor_timestamp_us = 0; + INIT_WORK(&steam->unregister_work, steam_work_unregister_cb); /* * With the real steam controller interface, do not connect hidraw. @@ -908,18 +1252,14 @@ static int steam_probe(struct hid_device *hdev, */ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDRAW); if (ret) - goto hid_hw_start_fail; - - ret = hid_add_device(steam->client_hdev); - if (ret) - goto client_hdev_add_fail; + goto err_cancel_work; ret = hid_hw_open(hdev); if (ret) { hid_err(hdev, "%s:hid_hw_open\n", __func__); - goto hid_hw_open_fail; + goto err_hw_stop; } if (steam->quirks & STEAM_QUIRK_WIRELESS) { @@ -935,25 +1275,38 @@ static int steam_probe(struct hid_device *hdev, hid_err(hdev, "%s:steam_register failed with error %d\n", __func__, ret); - goto input_register_fail; + 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; + return 0; -input_register_fail: -hid_hw_open_fail: -client_hdev_add_fail: - hid_hw_stop(hdev); -hid_hw_start_fail: +err_destroy: hid_destroy_device(steam->client_hdev); -client_hdev_fail: +err_steam_unregister: + if (steam->connected) + steam_unregister(steam); +err_hw_close: + hid_hw_close(hdev); +err_hw_stop: + hid_hw_stop(hdev); +err_cancel_work: cancel_work_sync(&steam->work_connect); - cancel_delayed_work_sync(&steam->heartbeat); + cancel_delayed_work_sync(&steam->mode_switch); cancel_work_sync(&steam->rumble_work); -steam_alloc_fail: - hid_err(hdev, "%s: failed with error %d\n", - __func__, ret); + cancel_work_sync(&steam->unregister_work); + return ret; } @@ -967,12 +1320,12 @@ static void steam_remove(struct hid_device *hdev) } hid_destroy_device(steam->client_hdev); - mutex_lock(&steam->mutex); - steam->client_hdev = NULL; - steam->client_opened = false; - cancel_delayed_work_sync(&steam->heartbeat); - mutex_unlock(&steam->mutex); + cancel_delayed_work_sync(&steam->mode_switch); cancel_work_sync(&steam->work_connect); + cancel_work_sync(&steam->rumble_work); + cancel_work_sync(&steam->unregister_work); + steam->client_hdev = NULL; + steam->client_opened = 0; if (steam->quirks & STEAM_QUIRK_WIRELESS) { hid_info(hdev, "Steam wireless receiver disconnected"); } @@ -1063,8 +1416,8 @@ static inline s16 steam_le16(u8 *data) * 9.4 | BTN_SELECT | menu left * 9.5 | BTN_MODE | steam logo * 9.6 | BTN_START | menu right - * 9.7 | BTN_GEAR_DOWN | left back lever - * 10.0 | BTN_GEAR_UP | right back lever + * 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) @@ -1129,8 +1482,8 @@ static void steam_do_input_event(struct steam_device *steam, input_event(input, EV_KEY, BTN_SELECT, !!(b9 & BIT(4))); input_event(input, EV_KEY, BTN_MODE, !!(b9 & BIT(5))); input_event(input, EV_KEY, BTN_START, !!(b9 & BIT(6))); - input_event(input, EV_KEY, BTN_GEAR_DOWN, !!(b9 & BIT(7))); - input_event(input, EV_KEY, BTN_GEAR_UP, !!(b10 & BIT(0))); + input_event(input, EV_KEY, BTN_GRIPL, !!(b9 & BIT(7))); + input_event(input, EV_KEY, BTN_GRIPR, !!(b10 & BIT(0))); input_event(input, EV_KEY, BTN_THUMBR, !!(b10 & BIT(2))); input_event(input, EV_KEY, BTN_THUMBL, !!(b10 & BIT(6))); input_event(input, EV_KEY, BTN_THUMB, lpad_touched || lpad_and_joy); @@ -1154,12 +1507,12 @@ static void steam_do_input_event(struct steam_device *steam, * 18-19 | s16 | ABS_HAT0Y | left-pad Y value * 20-21 | s16 | ABS_HAT1X | right-pad X value * 22-23 | s16 | ABS_HAT1Y | right-pad Y value - * 24-25 | s16 | -- | accelerometer X value - * 26-27 | s16 | -- | accelerometer Y value - * 28-29 | s16 | -- | accelerometer Z value - * 30-31 | s16 | -- | gyro X value - * 32-33 | s16 | -- | gyro Y value - * 34-35 | s16 | -- | gyro Z value + * 24-25 | s16 | IMU ABS_X | accelerometer X value + * 26-27 | s16 | IMU ABS_Z | accelerometer Y value + * 28-29 | s16 | IMU ABS_Y | accelerometer Z value + * 30-31 | s16 | IMU ABS_RX | gyro X value + * 32-33 | s16 | IMU ABS_RZ | gyro Y value + * 34-35 | s16 | IMU ABS_RY | gyro Z value * 36-37 | s16 | -- | quaternion W value * 38-39 | s16 | -- | quaternion X value * 40-41 | s16 | -- | quaternion Y value @@ -1191,8 +1544,8 @@ static void steam_do_input_event(struct steam_device *steam, * 9.4 | BTN_SELECT | menu left * 9.5 | BTN_MODE | steam logo * 9.6 | BTN_START | menu right - * 9.7 | BTN_TRIGGER_HAPPY3 | left bottom grip button - * 10.0 | BTN_TRIGGER_HAPPY4 | right bottom grip button + * 9.7 | BTN_GRIPL2 | left bottom grip button + * 10.0 | BTN_GRIPR2 | right bottom grip button * 10.1 | BTN_THUMB | left pad pressed * 10.2 | BTN_THUMB2 | right pad pressed * 10.3 | -- | left pad touched @@ -1217,8 +1570,8 @@ static void steam_do_input_event(struct steam_device *steam, * 12.6 | -- | unknown * 12.7 | -- | unknown * 13.0 | -- | unknown - * 13.1 | BTN_TRIGGER_HAPPY1 | left top grip button - * 13.2 | BTN_TRIGGER_HAPPY2 | right top grip button + * 13.1 | BTN_GRIPL | left top grip button + * 13.2 | BTN_GRIPR | right top grip button * 13.3 | -- | unknown * 13.4 | -- | unknown * 13.5 | -- | unknown @@ -1254,6 +1607,17 @@ static void steam_do_deck_input_event(struct steam_device *steam, b13 = data[13]; b14 = data[14]; + if (!(b9 & BIT(6)) && steam->did_mode_switch) { + steam->did_mode_switch = false; + cancel_delayed_work(&steam->mode_switch); + } else if (!steam->client_opened && (b9 & BIT(6)) && !steam->did_mode_switch) { + steam->did_mode_switch = true; + schedule_delayed_work(&steam->mode_switch, 45 * HZ / 100); + } + + if (!steam->gamepad_mode && lizard_mode) + return; + lpad_touched = b10 & BIT(3); rpad_touched = b10 & BIT(4); @@ -1292,8 +1656,8 @@ static void steam_do_deck_input_event(struct steam_device *steam, input_event(input, EV_KEY, BTN_SELECT, !!(b9 & BIT(4))); input_event(input, EV_KEY, BTN_MODE, !!(b9 & BIT(5))); input_event(input, EV_KEY, BTN_START, !!(b9 & BIT(6))); - input_event(input, EV_KEY, BTN_TRIGGER_HAPPY3, !!(b9 & BIT(7))); - input_event(input, EV_KEY, BTN_TRIGGER_HAPPY4, !!(b10 & BIT(0))); + input_event(input, EV_KEY, BTN_GRIPL2, !!(b9 & BIT(7))); + input_event(input, EV_KEY, BTN_GRIPR2, !!(b10 & BIT(0))); input_event(input, EV_KEY, BTN_THUMBL, !!(b10 & BIT(6))); input_event(input, EV_KEY, BTN_THUMBR, !!(b11 & BIT(2))); input_event(input, EV_KEY, BTN_DPAD_UP, !!(b9 & BIT(0))); @@ -1302,13 +1666,39 @@ static void steam_do_deck_input_event(struct steam_device *steam, input_event(input, EV_KEY, BTN_DPAD_DOWN, !!(b9 & BIT(3))); input_event(input, EV_KEY, BTN_THUMB, !!(b10 & BIT(1))); input_event(input, EV_KEY, BTN_THUMB2, !!(b10 & BIT(2))); - input_event(input, EV_KEY, BTN_TRIGGER_HAPPY1, !!(b13 & BIT(1))); - input_event(input, EV_KEY, BTN_TRIGGER_HAPPY2, !!(b13 & BIT(2))); + input_event(input, EV_KEY, BTN_GRIPL, !!(b13 & BIT(1))); + input_event(input, EV_KEY, BTN_GRIPR, !!(b13 & BIT(2))); input_event(input, EV_KEY, BTN_BASE, !!(b14 & BIT(2))); input_sync(input); } +static void 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->gamepad_mode && lizard_mode) + return; + + input_event(sensors, EV_MSC, MSC_TIMESTAMP, steam->sensor_timestamp_us); + input_report_abs(sensors, ABS_X, steam_le16(data + 24)); + input_report_abs(sensors, ABS_Z, -steam_le16(data + 26)); + input_report_abs(sensors, ABS_Y, steam_le16(data + 28)); + input_report_abs(sensors, ABS_RX, steam_le16(data + 30)); + input_report_abs(sensors, ABS_RZ, -steam_le16(data + 32)); + input_report_abs(sensors, ABS_RY, steam_le16(data + 34)); + + input_sync(sensors); +} + /* * The size for this message payload is 11. * The known values are: @@ -1346,6 +1736,7 @@ static int steam_raw_event(struct hid_device *hdev, { struct steam_device *steam = hid_get_drvdata(hdev); struct input_dev *input; + struct input_dev *sensors; struct power_supply *battery; if (!steam) @@ -1375,7 +1766,7 @@ static int steam_raw_event(struct hid_device *hdev, return 0; switch (data[2]) { - case STEAM_EV_INPUT_DATA: + case ID_CONTROLLER_STATE: if (steam->client_opened) return 0; rcu_read_lock(); @@ -1384,16 +1775,19 @@ static int steam_raw_event(struct hid_device *hdev, steam_do_input_event(steam, input, data); rcu_read_unlock(); break; - case STEAM_EV_DECK_INPUT_DATA: + case ID_CONTROLLER_DECK_STATE: if (steam->client_opened) return 0; rcu_read_lock(); input = rcu_dereference(steam->input); if (likely(input)) steam_do_deck_input_event(steam, input, data); + sensors = rcu_dereference(steam->sensors); + if (likely(sensors)) + steam_do_deck_sensors_event(steam, sensors, data); rcu_read_unlock(); break; - case STEAM_EV_CONNECT: + case ID_CONTROLLER_WIRELESS: /* * The payload of this event is a single byte: * 0x01: disconnected. @@ -1408,7 +1802,7 @@ static int steam_raw_event(struct hid_device *hdev, break; } break; - case STEAM_EV_BATTERY: + case ID_CONTROLLER_STATUS: if (steam->quirks & STEAM_QUIRK_WIRELESS) { rcu_read_lock(); battery = rcu_dereference(steam->battery); @@ -1439,10 +1833,8 @@ static int steam_param_set_lizard_mode(const char *val, mutex_lock(&steam_devices_lock); list_for_each_entry(steam, &steam_devices, list) { - mutex_lock(&steam->mutex); if (!steam->client_opened) steam_set_lizard_mode(steam, lizard_mode); - mutex_unlock(&steam->mutex); } mutex_unlock(&steam_devices_lock); return 0; |
