summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/hid/hid-steam.c113
1 files changed, 103 insertions, 10 deletions
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index a0ed8812e7ea..b3c4e50e248a 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 @@ input_register_fail:
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);