mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
HID: hid-steam: Add Deck IMU support
[ Upstream commit 3347e1654f24dbbd357ea4e3c0d8dcc12d8586c7 ] The Deck's controller features an accelerometer and gyroscope which send their measurement values by default in the main HID input report. Expose both sensors to userspace through a separate evdev node as it is done by the hid-nintendo and hid-playstation drivers. Signed-off-by: Max Maisel <mmm-1@posteo.net> Reviewed-by: Vicki Pfau <vi@endrift.com> Signed-off-by: Jiri Kosina <jkosina@suse.com> Stable-dep-of: 79504249d7e2 ("HID: hid-steam: Move hidraw input (un)registering to work") Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
9f82ed6e41
commit
4b996b6182
@@ -66,6 +66,14 @@ 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
|
||||
|
||||
@@ -288,6 +296,7 @@ struct steam_device {
|
||||
struct mutex report_mutex;
|
||||
bool client_opened;
|
||||
struct input_dev __rcu *input;
|
||||
struct input_dev __rcu *sensors;
|
||||
unsigned long quirks;
|
||||
struct work_struct work_connect;
|
||||
bool connected;
|
||||
@@ -302,6 +311,7 @@ struct steam_device {
|
||||
struct work_struct rumble_work;
|
||||
u16 rumble_left;
|
||||
u16 rumble_right;
|
||||
unsigned int sensor_timestamp_us;
|
||||
};
|
||||
|
||||
static int steam_recv_report(struct steam_device *steam,
|
||||
@@ -824,6 +834,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;
|
||||
@@ -837,6 +915,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;
|
||||
@@ -889,18 +985,28 @@ static int steam_register(struct steam_device *steam)
|
||||
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);
|
||||
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",
|
||||
@@ -1009,6 +1115,7 @@ static int steam_client_ll_open(struct hid_device *hdev)
|
||||
steam->client_opened = true;
|
||||
spin_unlock_irqrestore(&steam->lock, flags);
|
||||
|
||||
steam_sensors_unregister(steam);
|
||||
steam_input_unregister(steam);
|
||||
|
||||
return 0;
|
||||
@@ -1029,6 +1136,7 @@ static void steam_client_ll_close(struct hid_device *hdev)
|
||||
if (connected) {
|
||||
steam_set_lizard_mode(steam, lizard_mode);
|
||||
steam_input_register(steam);
|
||||
steam_sensors_register(steam);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1120,6 +1228,7 @@ static int steam_probe(struct hid_device *hdev,
|
||||
INIT_DELAYED_WORK(&steam->mode_switch, steam_mode_switch_cb);
|
||||
INIT_LIST_HEAD(&steam->list);
|
||||
INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
|
||||
steam->sensor_timestamp_us = 0;
|
||||
|
||||
/*
|
||||
* With the real steam controller interface, do not connect hidraw.
|
||||
@@ -1379,12 +1488,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
|
||||
@@ -1545,6 +1654,32 @@ static void steam_do_deck_input_event(struct steam_device *steam,
|
||||
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)
|
||||
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:
|
||||
@@ -1582,6 +1717,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)
|
||||
@@ -1627,6 +1763,9 @@ static int steam_raw_event(struct hid_device *hdev,
|
||||
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 ID_CONTROLLER_WIRELESS:
|
||||
|
||||
Reference in New Issue
Block a user