Path: blob/master/tools/testing/selftests/hid/tests/test_sony.py
26308 views
#!/bin/env python31# SPDX-License-Identifier: GPL-2.02# -*- coding: utf-8 -*-3#4# Copyright (c) 2020 Benjamin Tissoires <[email protected]>5# Copyright (c) 2020 Red Hat, Inc.6#78from .base import application_matches9from .base import KernelModule10from .test_gamepad import BaseTest11from hidtools.device.sony_gamepad import (12PS3Controller,13PS4ControllerBluetooth,14PS4ControllerUSB,15PS5ControllerBluetooth,16PS5ControllerUSB,17PSTouchPoint,18)19from hidtools.util import BusType2021import libevdev22import logging23import pytest2425logger = logging.getLogger("hidtools.test.sony")2627PS3_MODULE = KernelModule("sony", "hid_sony")28PS4_MODULE = KernelModule("playstation", "hid_playstation")29PS5_MODULE = KernelModule("playstation", "hid_playstation")303132class SonyBaseTest:33class SonyTest(BaseTest.TestGamepad):34pass3536class SonyPS4ControllerTest(SonyTest):37kernel_modules = [PS4_MODULE]3839def test_accelerometer(self):40uhdev = self.uhdev41evdev = uhdev.get_evdev("Accelerometer")4243for x in range(-32000, 32000, 4000):44r = uhdev.event(accel=(x, None, None))45events = uhdev.next_sync_events("Accelerometer")46self.debug_reports(r, uhdev, events)4748assert libevdev.InputEvent(libevdev.EV_ABS.ABS_X) in events49value = evdev.value[libevdev.EV_ABS.ABS_X]50# Check against range due to small loss in precision due51# to inverse calibration, followed by calibration by hid-sony.52assert x - 1 <= value <= x + 15354for y in range(-32000, 32000, 4000):55r = uhdev.event(accel=(None, y, None))56events = uhdev.next_sync_events("Accelerometer")57self.debug_reports(r, uhdev, events)5859assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Y) in events60value = evdev.value[libevdev.EV_ABS.ABS_Y]61assert y - 1 <= value <= y + 16263for z in range(-32000, 32000, 4000):64r = uhdev.event(accel=(None, None, z))65events = uhdev.next_sync_events("Accelerometer")66self.debug_reports(r, uhdev, events)6768assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Z) in events69value = evdev.value[libevdev.EV_ABS.ABS_Z]70assert z - 1 <= value <= z + 17172def test_gyroscope(self):73uhdev = self.uhdev74evdev = uhdev.get_evdev("Accelerometer")7576for rx in range(-2000000, 2000000, 200000):77r = uhdev.event(gyro=(rx, None, None))78events = uhdev.next_sync_events("Accelerometer")79self.debug_reports(r, uhdev, events)8081assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RX) in events82value = evdev.value[libevdev.EV_ABS.ABS_RX]83# Sensor internal value is 16-bit, but calibrated is 22-bit, so84# 6-bit (64) difference, so allow a range of +/- 64.85assert rx - 64 <= value <= rx + 648687for ry in range(-2000000, 2000000, 200000):88r = uhdev.event(gyro=(None, ry, None))89events = uhdev.next_sync_events("Accelerometer")90self.debug_reports(r, uhdev, events)9192assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RY) in events93value = evdev.value[libevdev.EV_ABS.ABS_RY]94assert ry - 64 <= value <= ry + 649596for rz in range(-2000000, 2000000, 200000):97r = uhdev.event(gyro=(None, None, rz))98events = uhdev.next_sync_events("Accelerometer")99self.debug_reports(r, uhdev, events)100101assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RZ) in events102value = evdev.value[libevdev.EV_ABS.ABS_RZ]103assert rz - 64 <= value <= rz + 64104105def test_battery(self):106uhdev = self.uhdev107108assert uhdev.power_supply_class is not None109110# DS4 capacity levels are in increments of 10.111# Battery is never below 5%.112for i in range(5, 105, 10):113uhdev.battery.capacity = i114uhdev.event()115assert uhdev.power_supply_class.capacity == i116117# Discharging tests only make sense for BlueTooth.118if uhdev.bus == BusType.BLUETOOTH:119uhdev.battery.cable_connected = False120uhdev.battery.capacity = 45121uhdev.event()122assert uhdev.power_supply_class.status == "Discharging"123124uhdev.battery.cable_connected = True125uhdev.battery.capacity = 5126uhdev.event()127assert uhdev.power_supply_class.status == "Charging"128129uhdev.battery.capacity = 100130uhdev.event()131assert uhdev.power_supply_class.status == "Charging"132133uhdev.battery.full = True134uhdev.event()135assert uhdev.power_supply_class.status == "Full"136137def test_mt_single_touch(self):138"""send a single touch in the first slot of the device,139and release it."""140uhdev = self.uhdev141evdev = uhdev.get_evdev("Touch Pad")142143t0 = PSTouchPoint(1, 50, 100)144r = uhdev.event(touch=[t0])145events = uhdev.next_sync_events("Touch Pad")146self.debug_reports(r, uhdev, events)147148assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events149assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0150assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50151assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100152153t0.tipswitch = False154r = uhdev.event(touch=[t0])155events = uhdev.next_sync_events("Touch Pad")156self.debug_reports(r, uhdev, events)157assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events158assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1159160def test_mt_dual_touch(self):161"""Send 2 touches in the first 2 slots.162Make sure the kernel sees this as a dual touch.163Release and check164165Note: PTP will send here BTN_DOUBLETAP emulation"""166uhdev = self.uhdev167evdev = uhdev.get_evdev("Touch Pad")168169t0 = PSTouchPoint(1, 50, 100)170t1 = PSTouchPoint(2, 150, 200)171172r = uhdev.event(touch=[t0])173events = uhdev.next_sync_events("Touch Pad")174self.debug_reports(r, uhdev, events)175176assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events177assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1178assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0179assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50180assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100181assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1182183r = uhdev.event(touch=[t0, t1])184events = uhdev.next_sync_events("Touch Pad")185self.debug_reports(r, uhdev, events)186assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH) not in events187assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1188assert (189libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X, 5) not in events190)191assert (192libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y, 10) not in events193)194assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0195assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50196assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100197assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1198assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_X] == 150199assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 200200201t0.tipswitch = False202r = uhdev.event(touch=[t0, t1])203events = uhdev.next_sync_events("Touch Pad")204self.debug_reports(r, uhdev, events)205assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1206assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1207assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X) not in events208assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y) not in events209210t1.tipswitch = False211r = uhdev.event(touch=[t1])212213events = uhdev.next_sync_events("Touch Pad")214self.debug_reports(r, uhdev, events)215assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1216assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1217218219class TestPS3Controller(SonyBaseTest.SonyTest):220kernel_modules = [PS3_MODULE]221222def create_device(self):223controller = PS3Controller()224controller.application_matches = application_matches225return controller226227@pytest.fixture(autouse=True)228def start_controller(self):229# emulate a 'PS' button press to tell the kernel we are ready to accept events230self.assert_button(17)231232# drain any remaining udev events233while self.uhdev.dispatch(10):234pass235236def test_led(self):237for k, v in self.uhdev.led_classes.items():238# the kernel might have set a LED for us239logger.info(f"{k}: {v.brightness}")240241idx = int(k[-1]) - 1242assert self.uhdev.hw_leds.get_led(idx)[0] == bool(v.brightness)243244v.brightness = 0245self.uhdev.dispatch(10)246assert self.uhdev.hw_leds.get_led(idx)[0] is False247248v.brightness = v.max_brightness249self.uhdev.dispatch(10)250assert self.uhdev.hw_leds.get_led(idx)[0]251252253class CalibratedPS4Controller(object):254# DS4 reports uncalibrated sensor data. Calibration coefficients255# can be retrieved using a feature report (0x2 USB / 0x5 BT).256# The values below are the processed calibration values for the257# DS4s matching the feature reports of PS4ControllerBluetooth/USB258# as dumped from hid-sony 'ds4_get_calibration_data'.259#260# Note we duplicate those values here in case the kernel changes them261# so we can have tests passing even if hid-tools doesn't have the262# correct values.263accelerometer_calibration_data = {264"x": {"bias": -73, "numer": 16384, "denom": 16472},265"y": {"bias": -352, "numer": 16384, "denom": 16344},266"z": {"bias": 81, "numer": 16384, "denom": 16319},267}268gyroscope_calibration_data = {269"x": {"bias": 0, "numer": 1105920, "denom": 17827},270"y": {"bias": 0, "numer": 1105920, "denom": 17777},271"z": {"bias": 0, "numer": 1105920, "denom": 17748},272}273274275class CalibratedPS4ControllerBluetooth(CalibratedPS4Controller, PS4ControllerBluetooth):276pass277278279class TestPS4ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest):280def create_device(self):281controller = CalibratedPS4ControllerBluetooth()282controller.application_matches = application_matches283return controller284285286class CalibratedPS4ControllerUSB(CalibratedPS4Controller, PS4ControllerUSB):287pass288289290class TestPS4ControllerUSB(SonyBaseTest.SonyPS4ControllerTest):291def create_device(self):292controller = CalibratedPS4ControllerUSB()293controller.application_matches = application_matches294return controller295296297class CalibratedPS5Controller(object):298# DualSense reports uncalibrated sensor data. Calibration coefficients299# can be retrieved using feature report 0x09.300# The values below are the processed calibration values for the301# DualSene matching the feature reports of PS5ControllerBluetooth/USB302# as dumped from hid-playstation 'dualsense_get_calibration_data'.303#304# Note we duplicate those values here in case the kernel changes them305# so we can have tests passing even if hid-tools doesn't have the306# correct values.307accelerometer_calibration_data = {308"x": {"bias": 0, "numer": 16384, "denom": 16374},309"y": {"bias": -114, "numer": 16384, "denom": 16362},310"z": {"bias": 2, "numer": 16384, "denom": 16395},311}312gyroscope_calibration_data = {313"x": {"bias": 0, "numer": 1105920, "denom": 17727},314"y": {"bias": 0, "numer": 1105920, "denom": 17728},315"z": {"bias": 0, "numer": 1105920, "denom": 17769},316}317318319class CalibratedPS5ControllerBluetooth(CalibratedPS5Controller, PS5ControllerBluetooth):320pass321322323class TestPS5ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest):324kernel_modules = [PS5_MODULE]325326def create_device(self):327controller = CalibratedPS5ControllerBluetooth()328controller.application_matches = application_matches329return controller330331332class CalibratedPS5ControllerUSB(CalibratedPS5Controller, PS5ControllerUSB):333pass334335336class TestPS5ControllerUSB(SonyBaseTest.SonyPS4ControllerTest):337kernel_modules = [PS5_MODULE]338339def create_device(self):340controller = CalibratedPS5ControllerUSB()341controller.application_matches = application_matches342return controller343344345