diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c index a0ed8812e7ead..b3c4e50e248aa 100644 --- a/drivers/hid/hid-steam.c +++ b/drivers/hid/hid-steam.c @@ -273,6 +273,11 @@ enum { 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 0x15 @@ -291,6 +296,9 @@ struct steam_device { struct power_supply __rcu *battery; u8 battery_charge; u16 voltage; + struct delayed_work mode_switch; + bool did_mode_switch; + bool gamepad_mode; struct work_struct rumble_work; u16 rumble_left; u16 rumble_right; @@ -460,6 +468,37 @@ static inline int steam_request_conn_status(struct steam_device *steam) 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) @@ -505,6 +544,9 @@ 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 */ @@ -542,11 +584,18 @@ static int steam_input_open(struct input_dev *dev) unsigned long flags; bool set_lizard_mode; - 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); + /* + * 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); + } return 0; } @@ -557,11 +606,13 @@ static void steam_input_close(struct input_dev *dev) unsigned long flags; bool set_lizard_mode; - 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); + 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[] = { @@ -886,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; @@ -1040,6 +1119,7 @@ static int steam_probe(struct hid_device *hdev, 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_WORK(&steam->rumble_work, steam_haptic_rumble_cb); @@ -1097,6 +1177,7 @@ static int steam_probe(struct hid_device *hdev, hid_hw_open_fail: hid_hw_start_fail: cancel_work_sync(&steam->work_connect); + 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", @@ -1113,6 +1194,7 @@ 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); steam->client_hdev = NULL; @@ -1398,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);