Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/hid/tests/test_sony.py
26308 views
1
#!/bin/env python3
2
# SPDX-License-Identifier: GPL-2.0
3
# -*- coding: utf-8 -*-
4
#
5
# Copyright (c) 2020 Benjamin Tissoires <[email protected]>
6
# Copyright (c) 2020 Red Hat, Inc.
7
#
8
9
from .base import application_matches
10
from .base import KernelModule
11
from .test_gamepad import BaseTest
12
from hidtools.device.sony_gamepad import (
13
PS3Controller,
14
PS4ControllerBluetooth,
15
PS4ControllerUSB,
16
PS5ControllerBluetooth,
17
PS5ControllerUSB,
18
PSTouchPoint,
19
)
20
from hidtools.util import BusType
21
22
import libevdev
23
import logging
24
import pytest
25
26
logger = logging.getLogger("hidtools.test.sony")
27
28
PS3_MODULE = KernelModule("sony", "hid_sony")
29
PS4_MODULE = KernelModule("playstation", "hid_playstation")
30
PS5_MODULE = KernelModule("playstation", "hid_playstation")
31
32
33
class SonyBaseTest:
34
class SonyTest(BaseTest.TestGamepad):
35
pass
36
37
class SonyPS4ControllerTest(SonyTest):
38
kernel_modules = [PS4_MODULE]
39
40
def test_accelerometer(self):
41
uhdev = self.uhdev
42
evdev = uhdev.get_evdev("Accelerometer")
43
44
for x in range(-32000, 32000, 4000):
45
r = uhdev.event(accel=(x, None, None))
46
events = uhdev.next_sync_events("Accelerometer")
47
self.debug_reports(r, uhdev, events)
48
49
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_X) in events
50
value = evdev.value[libevdev.EV_ABS.ABS_X]
51
# Check against range due to small loss in precision due
52
# to inverse calibration, followed by calibration by hid-sony.
53
assert x - 1 <= value <= x + 1
54
55
for y in range(-32000, 32000, 4000):
56
r = uhdev.event(accel=(None, y, None))
57
events = uhdev.next_sync_events("Accelerometer")
58
self.debug_reports(r, uhdev, events)
59
60
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Y) in events
61
value = evdev.value[libevdev.EV_ABS.ABS_Y]
62
assert y - 1 <= value <= y + 1
63
64
for z in range(-32000, 32000, 4000):
65
r = uhdev.event(accel=(None, None, z))
66
events = uhdev.next_sync_events("Accelerometer")
67
self.debug_reports(r, uhdev, events)
68
69
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_Z) in events
70
value = evdev.value[libevdev.EV_ABS.ABS_Z]
71
assert z - 1 <= value <= z + 1
72
73
def test_gyroscope(self):
74
uhdev = self.uhdev
75
evdev = uhdev.get_evdev("Accelerometer")
76
77
for rx in range(-2000000, 2000000, 200000):
78
r = uhdev.event(gyro=(rx, None, None))
79
events = uhdev.next_sync_events("Accelerometer")
80
self.debug_reports(r, uhdev, events)
81
82
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RX) in events
83
value = evdev.value[libevdev.EV_ABS.ABS_RX]
84
# Sensor internal value is 16-bit, but calibrated is 22-bit, so
85
# 6-bit (64) difference, so allow a range of +/- 64.
86
assert rx - 64 <= value <= rx + 64
87
88
for ry in range(-2000000, 2000000, 200000):
89
r = uhdev.event(gyro=(None, ry, None))
90
events = uhdev.next_sync_events("Accelerometer")
91
self.debug_reports(r, uhdev, events)
92
93
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RY) in events
94
value = evdev.value[libevdev.EV_ABS.ABS_RY]
95
assert ry - 64 <= value <= ry + 64
96
97
for rz in range(-2000000, 2000000, 200000):
98
r = uhdev.event(gyro=(None, None, rz))
99
events = uhdev.next_sync_events("Accelerometer")
100
self.debug_reports(r, uhdev, events)
101
102
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_RZ) in events
103
value = evdev.value[libevdev.EV_ABS.ABS_RZ]
104
assert rz - 64 <= value <= rz + 64
105
106
def test_battery(self):
107
uhdev = self.uhdev
108
109
assert uhdev.power_supply_class is not None
110
111
# DS4 capacity levels are in increments of 10.
112
# Battery is never below 5%.
113
for i in range(5, 105, 10):
114
uhdev.battery.capacity = i
115
uhdev.event()
116
assert uhdev.power_supply_class.capacity == i
117
118
# Discharging tests only make sense for BlueTooth.
119
if uhdev.bus == BusType.BLUETOOTH:
120
uhdev.battery.cable_connected = False
121
uhdev.battery.capacity = 45
122
uhdev.event()
123
assert uhdev.power_supply_class.status == "Discharging"
124
125
uhdev.battery.cable_connected = True
126
uhdev.battery.capacity = 5
127
uhdev.event()
128
assert uhdev.power_supply_class.status == "Charging"
129
130
uhdev.battery.capacity = 100
131
uhdev.event()
132
assert uhdev.power_supply_class.status == "Charging"
133
134
uhdev.battery.full = True
135
uhdev.event()
136
assert uhdev.power_supply_class.status == "Full"
137
138
def test_mt_single_touch(self):
139
"""send a single touch in the first slot of the device,
140
and release it."""
141
uhdev = self.uhdev
142
evdev = uhdev.get_evdev("Touch Pad")
143
144
t0 = PSTouchPoint(1, 50, 100)
145
r = uhdev.event(touch=[t0])
146
events = uhdev.next_sync_events("Touch Pad")
147
self.debug_reports(r, uhdev, events)
148
149
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events
150
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
151
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
152
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
153
154
t0.tipswitch = False
155
r = uhdev.event(touch=[t0])
156
events = uhdev.next_sync_events("Touch Pad")
157
self.debug_reports(r, uhdev, events)
158
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events
159
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
160
161
def test_mt_dual_touch(self):
162
"""Send 2 touches in the first 2 slots.
163
Make sure the kernel sees this as a dual touch.
164
Release and check
165
166
Note: PTP will send here BTN_DOUBLETAP emulation"""
167
uhdev = self.uhdev
168
evdev = uhdev.get_evdev("Touch Pad")
169
170
t0 = PSTouchPoint(1, 50, 100)
171
t1 = PSTouchPoint(2, 150, 200)
172
173
r = uhdev.event(touch=[t0])
174
events = uhdev.next_sync_events("Touch Pad")
175
self.debug_reports(r, uhdev, events)
176
177
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events
178
assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1
179
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
180
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
181
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
182
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
183
184
r = uhdev.event(touch=[t0, t1])
185
events = uhdev.next_sync_events("Touch Pad")
186
self.debug_reports(r, uhdev, events)
187
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH) not in events
188
assert evdev.value[libevdev.EV_KEY.BTN_TOUCH] == 1
189
assert (
190
libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X, 5) not in events
191
)
192
assert (
193
libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y, 10) not in events
194
)
195
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
196
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
197
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
198
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1
199
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_X] == 150
200
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 200
201
202
t0.tipswitch = False
203
r = uhdev.event(touch=[t0, t1])
204
events = uhdev.next_sync_events("Touch Pad")
205
self.debug_reports(r, uhdev, events)
206
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
207
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 1
208
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_X) not in events
209
assert libevdev.InputEvent(libevdev.EV_ABS.ABS_MT_POSITION_Y) not in events
210
211
t1.tipswitch = False
212
r = uhdev.event(touch=[t1])
213
214
events = uhdev.next_sync_events("Touch Pad")
215
self.debug_reports(r, uhdev, events)
216
assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
217
assert evdev.slots[1][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
218
219
220
class TestPS3Controller(SonyBaseTest.SonyTest):
221
kernel_modules = [PS3_MODULE]
222
223
def create_device(self):
224
controller = PS3Controller()
225
controller.application_matches = application_matches
226
return controller
227
228
@pytest.fixture(autouse=True)
229
def start_controller(self):
230
# emulate a 'PS' button press to tell the kernel we are ready to accept events
231
self.assert_button(17)
232
233
# drain any remaining udev events
234
while self.uhdev.dispatch(10):
235
pass
236
237
def test_led(self):
238
for k, v in self.uhdev.led_classes.items():
239
# the kernel might have set a LED for us
240
logger.info(f"{k}: {v.brightness}")
241
242
idx = int(k[-1]) - 1
243
assert self.uhdev.hw_leds.get_led(idx)[0] == bool(v.brightness)
244
245
v.brightness = 0
246
self.uhdev.dispatch(10)
247
assert self.uhdev.hw_leds.get_led(idx)[0] is False
248
249
v.brightness = v.max_brightness
250
self.uhdev.dispatch(10)
251
assert self.uhdev.hw_leds.get_led(idx)[0]
252
253
254
class CalibratedPS4Controller(object):
255
# DS4 reports uncalibrated sensor data. Calibration coefficients
256
# can be retrieved using a feature report (0x2 USB / 0x5 BT).
257
# The values below are the processed calibration values for the
258
# DS4s matching the feature reports of PS4ControllerBluetooth/USB
259
# as dumped from hid-sony 'ds4_get_calibration_data'.
260
#
261
# Note we duplicate those values here in case the kernel changes them
262
# so we can have tests passing even if hid-tools doesn't have the
263
# correct values.
264
accelerometer_calibration_data = {
265
"x": {"bias": -73, "numer": 16384, "denom": 16472},
266
"y": {"bias": -352, "numer": 16384, "denom": 16344},
267
"z": {"bias": 81, "numer": 16384, "denom": 16319},
268
}
269
gyroscope_calibration_data = {
270
"x": {"bias": 0, "numer": 1105920, "denom": 17827},
271
"y": {"bias": 0, "numer": 1105920, "denom": 17777},
272
"z": {"bias": 0, "numer": 1105920, "denom": 17748},
273
}
274
275
276
class CalibratedPS4ControllerBluetooth(CalibratedPS4Controller, PS4ControllerBluetooth):
277
pass
278
279
280
class TestPS4ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest):
281
def create_device(self):
282
controller = CalibratedPS4ControllerBluetooth()
283
controller.application_matches = application_matches
284
return controller
285
286
287
class CalibratedPS4ControllerUSB(CalibratedPS4Controller, PS4ControllerUSB):
288
pass
289
290
291
class TestPS4ControllerUSB(SonyBaseTest.SonyPS4ControllerTest):
292
def create_device(self):
293
controller = CalibratedPS4ControllerUSB()
294
controller.application_matches = application_matches
295
return controller
296
297
298
class CalibratedPS5Controller(object):
299
# DualSense reports uncalibrated sensor data. Calibration coefficients
300
# can be retrieved using feature report 0x09.
301
# The values below are the processed calibration values for the
302
# DualSene matching the feature reports of PS5ControllerBluetooth/USB
303
# as dumped from hid-playstation 'dualsense_get_calibration_data'.
304
#
305
# Note we duplicate those values here in case the kernel changes them
306
# so we can have tests passing even if hid-tools doesn't have the
307
# correct values.
308
accelerometer_calibration_data = {
309
"x": {"bias": 0, "numer": 16384, "denom": 16374},
310
"y": {"bias": -114, "numer": 16384, "denom": 16362},
311
"z": {"bias": 2, "numer": 16384, "denom": 16395},
312
}
313
gyroscope_calibration_data = {
314
"x": {"bias": 0, "numer": 1105920, "denom": 17727},
315
"y": {"bias": 0, "numer": 1105920, "denom": 17728},
316
"z": {"bias": 0, "numer": 1105920, "denom": 17769},
317
}
318
319
320
class CalibratedPS5ControllerBluetooth(CalibratedPS5Controller, PS5ControllerBluetooth):
321
pass
322
323
324
class TestPS5ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest):
325
kernel_modules = [PS5_MODULE]
326
327
def create_device(self):
328
controller = CalibratedPS5ControllerBluetooth()
329
controller.application_matches = application_matches
330
return controller
331
332
333
class CalibratedPS5ControllerUSB(CalibratedPS5Controller, PS5ControllerUSB):
334
pass
335
336
337
class TestPS5ControllerUSB(SonyBaseTest.SonyPS4ControllerTest):
338
kernel_modules = [PS5_MODULE]
339
340
def create_device(self):
341
controller = CalibratedPS5ControllerUSB()
342
controller.application_matches = application_matches
343
return controller
344
345