Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#!/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3# -*- coding: utf-8 -*-
4#
5# Copyright (c) 2020 Benjamin Tissoires <benjamin.tissoires@gmail.com>
6# Copyright (c) 2020 Red Hat, Inc.
7#
8
9from .base import application_matches
10from .base import KernelModule
11from .test_gamepad import BaseTest
12from hidtools.device.sony_gamepad import (
13 PS3Controller,
14 PS4ControllerBluetooth,
15 PS4ControllerUSB,
16 PS5ControllerBluetooth,
17 PS5ControllerUSB,
18 PSTouchPoint,
19)
20from hidtools.util import BusType
21
22import libevdev
23import logging
24import pytest
25
26logger = logging.getLogger("hidtools.test.sony")
27
28PS3_MODULE = KernelModule("sony", "hid_sony")
29PS4_MODULE = KernelModule("playstation", "hid_playstation")
30PS5_MODULE = KernelModule("playstation", "hid_playstation")
31
32
33class 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
220class 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
254class 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
276class CalibratedPS4ControllerBluetooth(CalibratedPS4Controller, PS4ControllerBluetooth):
277 pass
278
279
280class TestPS4ControllerBluetooth(SonyBaseTest.SonyPS4ControllerTest):
281 def create_device(self):
282 controller = CalibratedPS4ControllerBluetooth()
283 controller.application_matches = application_matches
284 return controller
285
286
287class CalibratedPS4ControllerUSB(CalibratedPS4Controller, PS4ControllerUSB):
288 pass
289
290
291class TestPS4ControllerUSB(SonyBaseTest.SonyPS4ControllerTest):
292 def create_device(self):
293 controller = CalibratedPS4ControllerUSB()
294 controller.application_matches = application_matches
295 return controller
296
297
298class 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
320class CalibratedPS5ControllerBluetooth(CalibratedPS5Controller, PS5ControllerBluetooth):
321 pass
322
323
324class 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
333class CalibratedPS5ControllerUSB(CalibratedPS5Controller, PS5ControllerUSB):
334 pass
335
336
337class 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