Skip to content

Commit cd438e5

Browse files
endriftJiri Kosina
authored andcommitted
HID: hid-steam: Add gamepad-only mode switched to by holding options
This commit adds a hotkey to switch between "gamepad" mode (mouse and keyboard disabled) and "desktop" mode (gamepad disabled) by holding down the options button (mapped here as the start button). This mirrors the behavior of the official Steam client. This also adds and uses a function for generating haptic pulses, as Steam also does when engaging this hotkey. Signed-off-by: Vicki Pfau <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 43565b6 commit cd438e5

File tree

1 file changed

+103
-10
lines changed

1 file changed

+103
-10
lines changed

drivers/hid/hid-steam.c

Lines changed: 103 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,11 @@ enum {
273273
TRACKPAD_GESTURE_KEYBOARD,
274274
};
275275

276+
/* Pad identifiers for the deck */
277+
#define STEAM_PAD_LEFT 0
278+
#define STEAM_PAD_RIGHT 1
279+
#define STEAM_PAD_BOTH 2
280+
276281
/* Other random constants */
277282
#define STEAM_SERIAL_LEN 0x15
278283

@@ -291,6 +296,9 @@ struct steam_device {
291296
struct power_supply __rcu *battery;
292297
u8 battery_charge;
293298
u16 voltage;
299+
struct delayed_work mode_switch;
300+
bool did_mode_switch;
301+
bool gamepad_mode;
294302
struct work_struct rumble_work;
295303
u16 rumble_left;
296304
u16 rumble_right;
@@ -460,6 +468,37 @@ static inline int steam_request_conn_status(struct steam_device *steam)
460468
return ret;
461469
}
462470

471+
/*
472+
* Send a haptic pulse to the trackpads
473+
* Duration and interval are measured in microseconds, count is the number
474+
* of pulses to send for duration time with interval microseconds between them
475+
* and gain is measured in decibels, ranging from -24 to +6
476+
*/
477+
static inline int steam_haptic_pulse(struct steam_device *steam, u8 pad,
478+
u16 duration, u16 interval, u16 count, u8 gain)
479+
{
480+
int ret;
481+
u8 report[10] = {ID_TRIGGER_HAPTIC_PULSE, 8};
482+
483+
/* Left and right are swapped on this report for legacy reasons */
484+
if (pad < STEAM_PAD_BOTH)
485+
pad ^= 1;
486+
487+
report[2] = pad;
488+
report[3] = duration & 0xFF;
489+
report[4] = duration >> 8;
490+
report[5] = interval & 0xFF;
491+
report[6] = interval >> 8;
492+
report[7] = count & 0xFF;
493+
report[8] = count >> 8;
494+
report[9] = gain;
495+
496+
mutex_lock(&steam->report_mutex);
497+
ret = steam_send_report(steam, report, sizeof(report));
498+
mutex_unlock(&steam->report_mutex);
499+
return ret;
500+
}
501+
463502
static inline int steam_haptic_rumble(struct steam_device *steam,
464503
u16 intensity, u16 left_speed, u16 right_speed,
465504
u8 left_gain, u8 right_gain)
@@ -505,6 +544,9 @@ static int steam_play_effect(struct input_dev *dev, void *data,
505544

506545
static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
507546
{
547+
if (steam->gamepad_mode)
548+
enable = false;
549+
508550
if (enable) {
509551
mutex_lock(&steam->report_mutex);
510552
/* enable esc, enter, cursors */
@@ -542,11 +584,18 @@ static int steam_input_open(struct input_dev *dev)
542584
unsigned long flags;
543585
bool set_lizard_mode;
544586

545-
spin_lock_irqsave(&steam->lock, flags);
546-
set_lizard_mode = !steam->client_opened && lizard_mode;
547-
spin_unlock_irqrestore(&steam->lock, flags);
548-
if (set_lizard_mode)
549-
steam_set_lizard_mode(steam, false);
587+
/*
588+
* Disabling lizard mode automatically is only done on the Steam
589+
* Controller. On the Steam Deck, this is toggled manually by holding
590+
* the options button instead, handled by steam_mode_switch_cb.
591+
*/
592+
if (!(steam->quirks & STEAM_QUIRK_DECK)) {
593+
spin_lock_irqsave(&steam->lock, flags);
594+
set_lizard_mode = !steam->client_opened && lizard_mode;
595+
spin_unlock_irqrestore(&steam->lock, flags);
596+
if (set_lizard_mode)
597+
steam_set_lizard_mode(steam, false);
598+
}
550599

551600
return 0;
552601
}
@@ -557,11 +606,13 @@ static void steam_input_close(struct input_dev *dev)
557606
unsigned long flags;
558607
bool set_lizard_mode;
559608

560-
spin_lock_irqsave(&steam->lock, flags);
561-
set_lizard_mode = !steam->client_opened && lizard_mode;
562-
spin_unlock_irqrestore(&steam->lock, flags);
563-
if (set_lizard_mode)
564-
steam_set_lizard_mode(steam, true);
609+
if (!(steam->quirks & STEAM_QUIRK_DECK)) {
610+
spin_lock_irqsave(&steam->lock, flags);
611+
set_lizard_mode = !steam->client_opened && lizard_mode;
612+
spin_unlock_irqrestore(&steam->lock, flags);
613+
if (set_lizard_mode)
614+
steam_set_lizard_mode(steam, true);
615+
}
565616
}
566617

567618
static enum power_supply_property steam_battery_props[] = {
@@ -886,6 +937,34 @@ static void steam_work_connect_cb(struct work_struct *work)
886937
}
887938
}
888939

940+
static void steam_mode_switch_cb(struct work_struct *work)
941+
{
942+
struct steam_device *steam = container_of(to_delayed_work(work),
943+
struct steam_device, mode_switch);
944+
unsigned long flags;
945+
bool client_opened;
946+
steam->gamepad_mode = !steam->gamepad_mode;
947+
if (!lizard_mode)
948+
return;
949+
950+
if (steam->gamepad_mode)
951+
steam_set_lizard_mode(steam, false);
952+
else {
953+
spin_lock_irqsave(&steam->lock, flags);
954+
client_opened = steam->client_opened;
955+
spin_unlock_irqrestore(&steam->lock, flags);
956+
if (!client_opened)
957+
steam_set_lizard_mode(steam, lizard_mode);
958+
}
959+
960+
steam_haptic_pulse(steam, STEAM_PAD_RIGHT, 0x190, 0, 1, 0);
961+
if (steam->gamepad_mode) {
962+
steam_haptic_pulse(steam, STEAM_PAD_LEFT, 0x14D, 0x14D, 0x2D, 0);
963+
} else {
964+
steam_haptic_pulse(steam, STEAM_PAD_LEFT, 0x1F4, 0x1F4, 0x1E, 0);
965+
}
966+
}
967+
889968
static bool steam_is_valve_interface(struct hid_device *hdev)
890969
{
891970
struct hid_report_enum *rep_enum;
@@ -1040,6 +1119,7 @@ static int steam_probe(struct hid_device *hdev,
10401119
mutex_init(&steam->report_mutex);
10411120
steam->quirks = id->driver_data;
10421121
INIT_WORK(&steam->work_connect, steam_work_connect_cb);
1122+
INIT_DELAYED_WORK(&steam->mode_switch, steam_mode_switch_cb);
10431123
INIT_LIST_HEAD(&steam->list);
10441124
INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
10451125

@@ -1097,6 +1177,7 @@ static int steam_probe(struct hid_device *hdev,
10971177
hid_hw_open_fail:
10981178
hid_hw_start_fail:
10991179
cancel_work_sync(&steam->work_connect);
1180+
cancel_delayed_work_sync(&steam->mode_switch);
11001181
cancel_work_sync(&steam->rumble_work);
11011182
steam_alloc_fail:
11021183
hid_err(hdev, "%s: failed with error %d\n",
@@ -1113,6 +1194,7 @@ static void steam_remove(struct hid_device *hdev)
11131194
return;
11141195
}
11151196

1197+
cancel_delayed_work_sync(&steam->mode_switch);
11161198
cancel_work_sync(&steam->work_connect);
11171199
hid_destroy_device(steam->client_hdev);
11181200
steam->client_hdev = NULL;
@@ -1398,6 +1480,17 @@ static void steam_do_deck_input_event(struct steam_device *steam,
13981480
b13 = data[13];
13991481
b14 = data[14];
14001482

1483+
if (!(b9 & BIT(6)) && steam->did_mode_switch) {
1484+
steam->did_mode_switch = false;
1485+
cancel_delayed_work_sync(&steam->mode_switch);
1486+
} else if (!steam->client_opened && (b9 & BIT(6)) && !steam->did_mode_switch) {
1487+
steam->did_mode_switch = true;
1488+
schedule_delayed_work(&steam->mode_switch, 45 * HZ / 100);
1489+
}
1490+
1491+
if (!steam->gamepad_mode)
1492+
return;
1493+
14011494
lpad_touched = b10 & BIT(3);
14021495
rpad_touched = b10 & BIT(4);
14031496

0 commit comments

Comments
 (0)