selftests: hid: import hid-tools hid-sony and hid-playstation tests
These tests have been developed in the hid-tools[0] tree for a while. Now that we have a proper selftests/hid kernel entry and that the tests are more reliable, it is time to directly include those in the kernel tree. The code is taken from [1] to fix a change in v6.3. [0] https://gitlab.freedesktop.org/libevdev/hid-tools Link: https://gitlab.freedesktop.org/libevdev/hid-tools/-/merge_requests/143 [1] Cc: Roderick Colenbrander <roderick.colenbrander@sony.com> Cc: Jose Torreguitar <jtguitar@google.com> Signed-off-by: Roderick Colenbrander <roderick.colenbrander@sony.com> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
This commit is contained in:
parent
ff3b2228e3
commit
a4ee40b6ac
@ -12,6 +12,7 @@ TEST_PROGS += hid-ite.sh
|
||||
TEST_PROGS += hid-keyboard.sh
|
||||
TEST_PROGS += hid-mouse.sh
|
||||
TEST_PROGS += hid-multitouch.sh
|
||||
TEST_PROGS += hid-sony.sh
|
||||
TEST_PROGS += hid-tablet.sh
|
||||
TEST_PROGS += hid-wacom.sh
|
||||
|
||||
|
@ -20,9 +20,14 @@ CONFIG_HID=y
|
||||
CONFIG_HID_BPF=y
|
||||
CONFIG_INPUT_EVDEV=y
|
||||
CONFIG_UHID=y
|
||||
CONFIG_LEDS_CLASS_MULTICOLOR=y
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_HID=y
|
||||
CONFIG_HID_APPLE=y
|
||||
CONFIG_HID_ITE=y
|
||||
CONFIG_HID_MULTITOUCH=y
|
||||
CONFIG_HID_PLAYSTATION=y
|
||||
CONFIG_PLAYSTATION_FF=y
|
||||
CONFIG_HID_SONY=y
|
||||
CONFIG_SONY_FF=y
|
||||
CONFIG_HID_WACOM=y
|
||||
|
7
tools/testing/selftests/hid/hid-sony.sh
Executable file
7
tools/testing/selftests/hid/hid-sony.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Runs tests for the HID subsystem
|
||||
|
||||
export TARGET=test_sony.py
|
||||
|
||||
bash ./run-hid-tools-tests.sh
|
342
tools/testing/selftests/hid/tests/test_sony.py
Normal file
342
tools/testing/selftests/hid/tests/test_sony.py
Normal file
@ -0,0 +1,342 @@
|
||||
#!/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2020 Benjamin Tissoires <benjamin.tissoires@gmail.com>
|
||||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
#
|
||||
|
||||
from .base import application_matches
|
||||
from .test_gamepad import BaseTest
|
||||
from hidtools.device.sony_gamepad import (
|
||||
PS3Controller,
|
||||
PS4ControllerBluetooth,
|
||||
PS4ControllerUSB,
|
||||
PS5ControllerBluetooth,
|
||||
PS5ControllerUSB,
|
||||
PSTouchPoint,
|
||||
)
|
||||
from hidtools.util import BusType
|
||||
|
||||
import libevdev
|
||||
import logging
|
||||
import pytest
|
||||
|
||||
logger = logging.getLogger("hidtools.test.sony")
|
||||
|
||||
PS3_MODULE = ("sony", "hid_sony")
|
||||
PS4_MODULE = ("playstation", "hid_playstation")
|
||||
PS5_MODULE = ("playstation", "hid_playstation")
|
||||
|
||||
|
||||
class SonyBaseTest:
|
||||
class SonyTest(BaseTest.TestGamepad):
|
||||
pass
|
||||
|
||||
class SonyPS4ControllerTest(SonyTest):
|
||||
kernel_modules = [PS4_MODULE]
|
||||
|
||||
def test_accelerometer(self):
|
||||
uhdev = self.uhdev
|
||||
evdev = uhdev.get_evdev("Accelerometer")
|
||||
|
||||
for x in range(-32000, 32000, 4000):
|
||||
r = uhdev.event(accel=(x, None, None))
|
||||
events = uhdev.next_sync_events("Accelerometer")
|
||||
self.debug_reports(r, uhdev, events)
|
||||
|
||||
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_X) in events
|
||||
value = evdev.value[libevdev.EV_ABS.ABS_X]
|
||||
# Check against range due to small loss in precision due
|
||||
# to inverse calibration, followed by calibration by hid-sony.
|
||||
assert x - 1 <= value <= x + 1
|
||||
|
||||
for y in range(-32000, 32000, 4000):
|
||||
r = uhdev.event(accel=(None, y, None))
|
||||
events = uhdev.next_sync_events("Accelerometer")
|
||||
self.debug_reports(r, uhdev, events)
|
||||
|
||||
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Y) in events
|
||||
value = evdev.value[libevdev.EV_ABS.ABS_Y]
|
||||
assert y - 1 <= value <= y + 1
|
||||
|
||||
for z in range(-32000, 32000, 4000):
|
||||
r = uhdev.event(accel=(None, None, z))
|
||||
events = uhdev.next_sync_events("Accelerometer")
|
||||
self.debug_reports(r, uhdev, events)
|
||||
|
||||
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Z) in events
|
||||
value = evdev.value[libevdev.EV_ABS.ABS_Z]
|
||||
assert z - 1 <= value <= z + 1
|
||||
|
||||
def test_gyroscope(self):
|
||||
uhdev = self.uhdev
|
||||
evdev = uhdev.get_evdev("Accelerometer")
|
||||
|
||||
for rx in range(-2000000, 2000000, 200000):
|
||||
r = uhdev.event(gyro=(rx, None, None))
|
||||
events = uhdev.next_sync_events("Accelerometer")
|
||||
self.debug_reports(r, uhdev, events)
|
||||
|
||||
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RX) in events
|
||||
value = evdev.value[libevdev.EV_ABS.ABS_RX]
|
||||
# Sensor internal value is 16-bit, but calibrated is 22-bit, so
|
||||
# 6-bit (64) difference, so allow a range of +/- 64.
|
||||
assert rx - 64 <= value <= rx + 64
|
||||
|
||||
for ry in range(-2000000, 2000000, 200000):
|
||||
r = uhdev.event(gyro=(None, ry, None))
|
||||
events = uhdev.next_sync_events("Accelerometer")
|
||||
self.debug_reports(r, uhdev, events)
|
||||
|
||||
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RY) in events
|
||||
value = evdev.value[libevdev.EV_ABS.ABS_RY]
|
||||
assert ry - 64 <= value <= ry + 64
|
||||
|
||||
for rz in range(-2000000, 2000000, 200000):
|
||||
r = uhdev.event(gyro=(None, None, rz))
|
||||
events = uhdev.next_sync_events("Accelerometer")
|
||||
self.debug_reports(r, uhdev, events)
|
||||
|
||||
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RZ) in events
|
||||
value = evdev.value[libevdev.EV_ABS.ABS_RZ]
|
||||
assert rz - 64 <= value <= rz + 64
|
||||
|
||||
def test_battery(self):
|
||||
uhdev = self.uhdev
|
||||
|
||||
assert uhdev.power_supply_class is not None
|
||||
|
||||
# DS4 capacity levels are in increments of 10.
|
||||
# Battery is never below 5%.
|
||||
for i in range(5, 105, 10):
|
||||
uhdev.battery.capacity = i
|
||||
uhdev.event()
|
||||
assert uhdev.power_supply_class.capacity == i
|
||||
|
||||
# Discharging tests only make sense for BlueTooth.
|
||||
if uhdev.bus == BusType.BLUETOOTH:
|
||||
uhdev.battery.cable_connected = False
|
||||
uhdev.battery.capacity = 45
|
||||
uhdev.event()
|
||||
assert uhdev.power_supply_class.status == "Discharging"
|
||||
|
||||
uhdev.battery.cable_connected = True
|
||||
uhdev.battery.capacity = 5
|
||||
uhdev.event()
|
||||
assert uhdev.power_supply_class.status == "Charging"
|
||||
|
||||
uhdev.battery.capacity = 100
|
||||
uhdev.event()
|
||||
assert uhdev.power_supply_class.status == "Charging"
|
||||
|
||||
uhdev.battery.full = True
|
||||
uhdev.event()
|
||||
assert uhdev.power_supply_class.status == "Full"
|
||||
|
||||
def test_mt_single_touch(self):
|
||||
"""send a single touch in the first slot of the device,
|
||||
and release it."""
|
||||
uhdev = self.uhdev
|
||||
evdev = uhdev.get_evdev("Touch Pad")
|
||||
|
||||
t0 = PSTouchPoint(1, 50, 100)
|
||||
r = uhdev.event(touch=[t0])
|
||||
events = uhdev.next_sync_events("Touch Pad")
|
||||
self.debug_reports(r, uhdev, events)
|
||||
|
||||
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events
|
||||
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
|
||||
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
|
||||
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
|
||||
|
||||
t0.tipswitch = False
|
||||
r = uhdev.event(touch=[t0])
|
||||
events = uhdev.next_sync_events("Touch Pad")
|
||||
self.debug_reports(r, uhdev, events)
|
||||
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events
|
||||
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
|
||||
|
||||
def test_mt_dual_touch(self):
|
||||
"""Send 2 touches in the first 2 slots.
|
||||
Make sure the kernel sees this as a dual touch.
|
||||
Release and check
|
||||
|
||||
Note: PTP will send here BTN_DOUBLETAP emulation"""
|
||||
uhdev = self.uhdev
|
||||
evdev = uhdev.get_evdev("Touch Pad")
|
||||
|
||||
t0 = PSTouchPoint(1, 50, 100)
|
||||
t1 = PSTouchPoint(2, 150, 200)
|
||||
|
||||
r = uhdev.event(touch=[t0])
|
||||
events = uhdev.next_sync_events("Touch Pad")
|
||||
self.debug_reports(r, uhdev, events)
|
||||
|
||||
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events
|
||||
assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1
|
||||
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
|
||||
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
|
||||
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
|
||||
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
|
||||
|
||||
r = uhdev.event(touch=[t0, t1])
|
||||
events = uhdev.next_sync_events("Touch Pad")
|
||||
self.debug_reports(r, uhdev, events)
|
||||
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH) not in events
|
||||
assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1
|
||||
assert (
|
||||
libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X, 5) not in events
|
||||
)
|
||||
assert (
|
||||
libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y, 10) not in events
|
||||
)
|
||||
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
|
||||
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
|
||||
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
|
||||
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1
|
||||
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_X] == 150
|
||||
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 200
|
||||
|
||||
t0.tipswitch = False
|
||||
r = uhdev.event(touch=[t0, t1])
|
||||
events = uhdev.next_sync_events("Touch Pad")
|
||||
self.debug_reports(r, uhdev, events)
|
||||
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
|
||||
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1
|
||||
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X) not in events
|
||||
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y) not in events
|
||||
|
||||
t1.tipswitch = False
|
||||
r = uhdev.event(touch=[t1])
|
||||
|
||||
events = uhdev.next_sync_events("Touch Pad")
|
||||
self.debug_reports(r, uhdev, events)
|
||||
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
|
||||
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
|
||||
|
||||
|
||||
class TestPS3Controller(SonyBaseTest.SonyTest):
|
||||
kernel_modules = [PS3_MODULE]
|
||||
|
||||
def create_device(self):
|
||||
controller = PS3Controller()
|
||||
controller.application_matches = application_matches
|
||||
return controller
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def start_controller(self):
|
||||
# emulate a 'PS' button press to tell the kernel we are ready to accept events
|
||||
self.assert_button(17)
|
||||
|
||||
# drain any remaining udev events
|
||||
while self.uhdev.dispatch(10):
|
||||
pass
|
||||
|
||||
def test_led(self):
|
||||
for k, v in self.uhdev.led_classes.items():
|
||||
# the kernel might have set a LED for us
|
||||
logger.info(f"{k}: {v.brightness}")
|
||||
|
||||
idx = int(k[-1]) - 1
|
||||
assert self.uhdev.hw_leds.get_led(idx)[0] == bool(v.brightness)
|
||||
|
||||
v.brightness = 0
|
||||
self.uhdev.dispatch(10)
|
||||
assert self.uhdev.hw_leds.get_led(idx)[0] is False
|
||||
|
||||
v.brightness = v.max_brightness
|
||||
self.uhdev.dispatch(10)
|
||||
assert self.uhdev.hw_leds.get_led(idx)[0]
|
||||
|
||||
|
||||
class CalibratedPS4Controller(object):
|
||||
# DS4 reports uncalibrated sensor data. Calibration coefficients
|
||||
# can be retrieved using a feature report (0x2 USB / 0x5 BT).
|
||||
# The values below are the processed calibration values for the
|
||||
# DS4s matching the feature reports of PS4ControllerBluetooth/USB
|
||||
# as dumped from hid-sony 'ds4_get_calibration_data'.
|
||||
#
|
||||
# Note we duplicate those values here in case the kernel changes them
|
||||
# so we can have tests passing even if hid-tools doesn't have the
|
||||
# correct values.
|
||||
accelerometer_calibration_data = {
|
||||
"x": {"bias": -73, "numer": 16384, "denom": 16472},
|
||||
"y": {"bias": -352, "numer": 16384, "denom": 16344},
|
||||
"z": {"bias": 81, "numer": 16384, "denom": 16319},
|
||||
}
|
||||
gyroscope_calibration_data = {
|
||||
"x": {"bias": 0, "numer": 1105920, "denom": 17827},
|
||||
"y": {"bias": 0, "numer": 1105920, "denom": 17777},
|
||||
"z": {"bias": 0, "numer": 1105920, "denom": 17748},
|
||||
}
|
||||
|
||||
|
||||
class CalibratedPS4ControllerBluetooth(CalibratedPS4Controller, PS4ControllerBluetooth):
|
||||
pass
|
||||
|
||||
|
||||
class TestPS4ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest):
|
||||
def create_device(self):
|
||||
controller = CalibratedPS4ControllerBluetooth()
|
||||
controller.application_matches = application_matches
|
||||
return controller
|
||||
|
||||
|
||||
class CalibratedPS4ControllerUSB(CalibratedPS4Controller, PS4ControllerUSB):
|
||||
pass
|
||||
|
||||
|
||||
class TestPS4ControllerUSB(SonyBaseTest.SonyPS4ControllerTest):
|
||||
def create_device(self):
|
||||
controller = CalibratedPS4ControllerUSB()
|
||||
controller.application_matches = application_matches
|
||||
return controller
|
||||
|
||||
|
||||
class CalibratedPS5Controller(object):
|
||||
# DualSense reports uncalibrated sensor data. Calibration coefficients
|
||||
# can be retrieved using feature report 0x09.
|
||||
# The values below are the processed calibration values for the
|
||||
# DualSene matching the feature reports of PS5ControllerBluetooth/USB
|
||||
# as dumped from hid-playstation 'dualsense_get_calibration_data'.
|
||||
#
|
||||
# Note we duplicate those values here in case the kernel changes them
|
||||
# so we can have tests passing even if hid-tools doesn't have the
|
||||
# correct values.
|
||||
accelerometer_calibration_data = {
|
||||
"x": {"bias": 0, "numer": 16384, "denom": 16374},
|
||||
"y": {"bias": -114, "numer": 16384, "denom": 16362},
|
||||
"z": {"bias": 2, "numer": 16384, "denom": 16395},
|
||||
}
|
||||
gyroscope_calibration_data = {
|
||||
"x": {"bias": 0, "numer": 1105920, "denom": 17727},
|
||||
"y": {"bias": 0, "numer": 1105920, "denom": 17728},
|
||||
"z": {"bias": 0, "numer": 1105920, "denom": 17769},
|
||||
}
|
||||
|
||||
|
||||
class CalibratedPS5ControllerBluetooth(CalibratedPS5Controller, PS5ControllerBluetooth):
|
||||
pass
|
||||
|
||||
|
||||
class TestPS5ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest):
|
||||
kernel_modules = [PS5_MODULE]
|
||||
|
||||
def create_device(self):
|
||||
controller = CalibratedPS5ControllerBluetooth()
|
||||
controller.application_matches = application_matches
|
||||
return controller
|
||||
|
||||
|
||||
class CalibratedPS5ControllerUSB(CalibratedPS5Controller, PS5ControllerUSB):
|
||||
pass
|
||||
|
||||
|
||||
class TestPS5ControllerUSB(SonyBaseTest.SonyPS4ControllerTest):
|
||||
kernel_modules = [PS5_MODULE]
|
||||
|
||||
def create_device(self):
|
||||
controller = CalibratedPS5ControllerUSB()
|
||||
controller.application_matches = application_matches
|
||||
return controller
|
Loading…
x
Reference in New Issue
Block a user