Loading drivers/hid/hid-steam.c +391 −156 Original line number Diff line number Diff line Loading @@ -71,60 +71,222 @@ static LIST_HEAD(steam_devices); /* * 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 idenitifiers */ 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; unsigned long quirks; struct work_struct work_connect; Loading @@ -134,7 +296,9 @@ 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; Loading Loading @@ -226,13 +390,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; Loading Loading @@ -265,23 +429,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; } /* Loading @@ -291,14 +461,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; Loading @@ -309,7 +515,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) Loading @@ -335,40 +544,36 @@ 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; if (enable) { mutex_lock(&steam->report_mutex); /* 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); mutex_unlock(&steam->report_mutex); } else { mutex_lock(&steam->report_mutex); /* 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); mutex_unlock(&steam->report_mutex); } 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); } } } Loading @@ -376,22 +581,38 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable) static int steam_input_open(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) /* * 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_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) 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); mutex_unlock(&steam->mutex); } } static enum power_supply_property steam_battery_props[] = { Loading Loading @@ -635,7 +856,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 Loading @@ -648,11 +870,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); Loading @@ -667,15 +887,13 @@ 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) spin_unlock_irqrestore(&steam->lock, flags); if (!client_opened) { steam_set_lizard_mode(steam, lizard_mode); mutex_unlock(&steam->mutex); if (!client_opened) ret = steam_input_register(steam); else } else ret = 0; return ret; Loading Loading @@ -719,6 +937,34 @@ 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; steam->gamepad_mode = !steam->gamepad_mode; if (!lizard_mode) return; 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 bool steam_is_valve_interface(struct hid_device *hdev) { struct hid_report_enum *rep_enum; Loading @@ -738,22 +984,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; Loading @@ -774,10 +1004,11 @@ 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); Loading @@ -792,18 +1023,15 @@ static void steam_client_ll_close(struct hid_device *hdev) bool connected; spin_lock_irqsave(&steam->lock, flags); connected = steam->connected; steam->client_opened--; connected = steam->connected && !steam->client_opened; spin_unlock_irqrestore(&steam->lock, flags); mutex_lock(&steam->mutex); steam->client_opened = false; if (connected) if (connected) { steam_set_lizard_mode(steam, lizard_mode); mutex_unlock(&steam->mutex); if (connected) steam_input_register(steam); } } static int steam_client_ll_raw_request(struct hid_device *hdev, unsigned char reportnum, u8 *buf, Loading Loading @@ -888,20 +1116,13 @@ static int steam_probe(struct hid_device *hdev, 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; /* * With the real steam controller interface, do not connect hidraw. * Instead, create the client_hid and connect that. Loading @@ -910,10 +1131,6 @@ static int steam_probe(struct hid_device *hdev, if (ret) goto hid_hw_start_fail; ret = hid_add_device(steam->client_hdev); if (ret) goto client_hdev_add_fail; ret = hid_hw_open(hdev); if (ret) { hid_err(hdev, Loading @@ -939,17 +1156,28 @@ static int steam_probe(struct hid_device *hdev, } } 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; ret = hid_add_device(steam->client_hdev); if (ret) goto client_hdev_add_fail; return 0; input_register_fail: hid_hw_open_fail: client_hdev_add_fail: hid_hw_stop(hdev); hid_hw_start_fail: hid_destroy_device(steam->client_hdev); client_hdev_fail: hid_destroy_device(steam->client_hdev); input_register_fail: hid_hw_open_fail: hid_hw_start_fail: 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", Loading @@ -966,13 +1194,11 @@ static void steam_remove(struct hid_device *hdev) return; } cancel_delayed_work_sync(&steam->mode_switch); cancel_work_sync(&steam->work_connect); 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_work_sync(&steam->work_connect); steam->client_opened = 0; if (steam->quirks & STEAM_QUIRK_WIRELESS) { hid_info(hdev, "Steam wireless receiver disconnected"); } Loading Loading @@ -1254,6 +1480,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_sync(&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) return; lpad_touched = b10 & BIT(3); rpad_touched = b10 & BIT(4); Loading Loading @@ -1375,7 +1612,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(); Loading @@ -1384,7 +1621,7 @@ 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(); Loading @@ -1393,7 +1630,7 @@ static int steam_raw_event(struct hid_device *hdev, steam_do_deck_input_event(steam, input, data); rcu_read_unlock(); break; case STEAM_EV_CONNECT: case ID_CONTROLLER_WIRELESS: /* * The payload of this event is a single byte: * 0x01: disconnected. Loading @@ -1408,7 +1645,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); Loading Loading @@ -1439,10 +1676,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; Loading Loading
drivers/hid/hid-steam.c +391 −156 Original line number Diff line number Diff line Loading @@ -71,60 +71,222 @@ static LIST_HEAD(steam_devices); /* * 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 idenitifiers */ 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; unsigned long quirks; struct work_struct work_connect; Loading @@ -134,7 +296,9 @@ 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; Loading Loading @@ -226,13 +390,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; Loading Loading @@ -265,23 +429,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; } /* Loading @@ -291,14 +461,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; Loading @@ -309,7 +515,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) Loading @@ -335,40 +544,36 @@ 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; if (enable) { mutex_lock(&steam->report_mutex); /* 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); mutex_unlock(&steam->report_mutex); } else { mutex_lock(&steam->report_mutex); /* 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); mutex_unlock(&steam->report_mutex); } 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); } } } Loading @@ -376,22 +581,38 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable) static int steam_input_open(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) /* * 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_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) 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); mutex_unlock(&steam->mutex); } } static enum power_supply_property steam_battery_props[] = { Loading Loading @@ -635,7 +856,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 Loading @@ -648,11 +870,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); Loading @@ -667,15 +887,13 @@ 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) spin_unlock_irqrestore(&steam->lock, flags); if (!client_opened) { steam_set_lizard_mode(steam, lizard_mode); mutex_unlock(&steam->mutex); if (!client_opened) ret = steam_input_register(steam); else } else ret = 0; return ret; Loading Loading @@ -719,6 +937,34 @@ 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; steam->gamepad_mode = !steam->gamepad_mode; if (!lizard_mode) return; 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 bool steam_is_valve_interface(struct hid_device *hdev) { struct hid_report_enum *rep_enum; Loading @@ -738,22 +984,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; Loading @@ -774,10 +1004,11 @@ 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); Loading @@ -792,18 +1023,15 @@ static void steam_client_ll_close(struct hid_device *hdev) bool connected; spin_lock_irqsave(&steam->lock, flags); connected = steam->connected; steam->client_opened--; connected = steam->connected && !steam->client_opened; spin_unlock_irqrestore(&steam->lock, flags); mutex_lock(&steam->mutex); steam->client_opened = false; if (connected) if (connected) { steam_set_lizard_mode(steam, lizard_mode); mutex_unlock(&steam->mutex); if (connected) steam_input_register(steam); } } static int steam_client_ll_raw_request(struct hid_device *hdev, unsigned char reportnum, u8 *buf, Loading Loading @@ -888,20 +1116,13 @@ static int steam_probe(struct hid_device *hdev, 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; /* * With the real steam controller interface, do not connect hidraw. * Instead, create the client_hid and connect that. Loading @@ -910,10 +1131,6 @@ static int steam_probe(struct hid_device *hdev, if (ret) goto hid_hw_start_fail; ret = hid_add_device(steam->client_hdev); if (ret) goto client_hdev_add_fail; ret = hid_hw_open(hdev); if (ret) { hid_err(hdev, Loading @@ -939,17 +1156,28 @@ static int steam_probe(struct hid_device *hdev, } } 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; ret = hid_add_device(steam->client_hdev); if (ret) goto client_hdev_add_fail; return 0; input_register_fail: hid_hw_open_fail: client_hdev_add_fail: hid_hw_stop(hdev); hid_hw_start_fail: hid_destroy_device(steam->client_hdev); client_hdev_fail: hid_destroy_device(steam->client_hdev); input_register_fail: hid_hw_open_fail: hid_hw_start_fail: 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", Loading @@ -966,13 +1194,11 @@ static void steam_remove(struct hid_device *hdev) return; } cancel_delayed_work_sync(&steam->mode_switch); cancel_work_sync(&steam->work_connect); 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_work_sync(&steam->work_connect); steam->client_opened = 0; if (steam->quirks & STEAM_QUIRK_WIRELESS) { hid_info(hdev, "Steam wireless receiver disconnected"); } Loading Loading @@ -1254,6 +1480,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_sync(&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) return; lpad_touched = b10 & BIT(3); rpad_touched = b10 & BIT(4); Loading Loading @@ -1375,7 +1612,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(); Loading @@ -1384,7 +1621,7 @@ 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(); Loading @@ -1393,7 +1630,7 @@ static int steam_raw_event(struct hid_device *hdev, steam_do_deck_input_event(steam, input, data); rcu_read_unlock(); break; case STEAM_EV_CONNECT: case ID_CONTROLLER_WIRELESS: /* * The payload of this event is a single byte: * 0x01: disconnected. Loading @@ -1408,7 +1645,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); Loading Loading @@ -1439,10 +1676,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; Loading