linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Benjamin Tissoires <bentiss@kernel.org>
To: Jiri Kosina <jikos@kernel.org>,
	Benjamin Tissoires <bentiss@kernel.org>,
	 Shuah Khan <shuah@kernel.org>,
	Peter Hutterer <peter.hutterer@who-t.net>
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	 linux-kselftest@vger.kernel.org
Subject: [PATCH 16/18] selftests/hid: import base_gamepad.py from hid-tools
Date: Wed, 10 Apr 2024 19:19:36 +0200	[thread overview]
Message-ID: <20240410-bpf_sources-v1-16-a8bf16033ef8@kernel.org> (raw)
In-Reply-To: <20240410-bpf_sources-v1-0-a8bf16033ef8@kernel.org>

We need to slightly change base_device.py for supporting HID-BPF,
so instead of monkey patching, let's just embed it in the kernel tree.

Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
---
 tools/testing/selftests/hid/tests/base_gamepad.py | 238 ++++++++++++++++++++++
 tools/testing/selftests/hid/tests/test_gamepad.py |   5 +-
 2 files changed, 242 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/hid/tests/base_gamepad.py b/tools/testing/selftests/hid/tests/base_gamepad.py
new file mode 100644
index 000000000000..ec74d75767a2
--- /dev/null
+++ b/tools/testing/selftests/hid/tests/base_gamepad.py
@@ -0,0 +1,238 @@
+# SPDX-License-Identifier: GPL-2.0
+import libevdev
+
+from .base_device import BaseDevice
+from hidtools.util import BusType
+
+
+class InvalidHIDCommunication(Exception):
+    pass
+
+
+class GamepadData(object):
+    pass
+
+
+class AxisMapping(object):
+    """Represents a mapping between a HID type
+    and an evdev event"""
+
+    def __init__(self, hid, evdev=None):
+        self.hid = hid.lower()
+
+        if evdev is None:
+            evdev = f"ABS_{hid.upper()}"
+
+        self.evdev = libevdev.evbit("EV_ABS", evdev)
+
+
+class BaseGamepad(BaseDevice):
+    buttons_map = {
+        1: "BTN_SOUTH",
+        2: "BTN_EAST",
+        3: "BTN_C",
+        4: "BTN_NORTH",
+        5: "BTN_WEST",
+        6: "BTN_Z",
+        7: "BTN_TL",
+        8: "BTN_TR",
+        9: "BTN_TL2",
+        10: "BTN_TR2",
+        11: "BTN_SELECT",
+        12: "BTN_START",
+        13: "BTN_MODE",
+        14: "BTN_THUMBL",
+        15: "BTN_THUMBR",
+    }
+
+    axes_map = {
+        "left_stick": {
+            "x": AxisMapping("x"),
+            "y": AxisMapping("y"),
+        },
+        "right_stick": {
+            "x": AxisMapping("z"),
+            "y": AxisMapping("Rz"),
+        },
+    }
+
+    def __init__(self, rdesc, application="Game Pad", name=None, input_info=None):
+        assert rdesc is not None
+        super().__init__(name, application, input_info=input_info, rdesc=rdesc)
+        self.buttons = (1, 2, 3)
+        self._buttons = {}
+        self.left = (127, 127)
+        self.right = (127, 127)
+        self.hat_switch = 15
+        assert self.parsed_rdesc is not None
+
+        self.fields = []
+        for r in self.parsed_rdesc.input_reports.values():
+            if r.application_name == self.application:
+                self.fields.extend([f.usage_name for f in r])
+
+    def store_axes(self, which, gamepad, data):
+        amap = self.axes_map[which]
+        x, y = data
+        setattr(gamepad, amap["x"].hid, x)
+        setattr(gamepad, amap["y"].hid, y)
+
+    def create_report(
+        self,
+        *,
+        left=(None, None),
+        right=(None, None),
+        hat_switch=None,
+        buttons=None,
+        reportID=None,
+        application="Game Pad",
+    ):
+        """
+        Return an input report for this device.
+
+        :param left: a tuple of absolute (x, y) value of the left joypad
+            where ``None`` is "leave unchanged"
+        :param right: a tuple of absolute (x, y) value of the right joypad
+            where ``None`` is "leave unchanged"
+        :param hat_switch: an absolute angular value of the hat switch
+            (expressed in 1/8 of circle, 0 being North, 2 East)
+            where ``None`` is "leave unchanged"
+        :param buttons: a dict of index/bool for the button states,
+            where ``None`` is "leave unchanged"
+        :param reportID: the numeric report ID for this report, if needed
+        :param application: the application used to report the values
+        """
+        if buttons is not None:
+            for i, b in buttons.items():
+                if i not in self.buttons:
+                    raise InvalidHIDCommunication(
+                        f"button {i} is not part of this {self.application}"
+                    )
+                if b is not None:
+                    self._buttons[i] = b
+
+        def replace_none_in_tuple(item, default):
+            if item is None:
+                item = (None, None)
+
+            if None in item:
+                if item[0] is None:
+                    item = (default[0], item[1])
+                if item[1] is None:
+                    item = (item[0], default[1])
+
+            return item
+
+        right = replace_none_in_tuple(right, self.right)
+        self.right = right
+        left = replace_none_in_tuple(left, self.left)
+        self.left = left
+
+        if hat_switch is None:
+            hat_switch = self.hat_switch
+        else:
+            self.hat_switch = hat_switch
+
+        reportID = reportID or self.default_reportID
+
+        gamepad = GamepadData()
+        for i, b in self._buttons.items():
+            gamepad.__setattr__(f"b{i}", int(b) if b is not None else 0)
+
+        self.store_axes("left_stick", gamepad, left)
+        self.store_axes("right_stick", gamepad, right)
+        gamepad.hatswitch = hat_switch  # type: ignore  ### gamepad is by default empty
+        return super().create_report(
+            gamepad, reportID=reportID, application=application
+        )
+
+    def event(
+        self, *, left=(None, None), right=(None, None), hat_switch=None, buttons=None
+    ):
+        """
+        Send an input event on the default report ID.
+
+        :param left: a tuple of absolute (x, y) value of the left joypad
+            where ``None`` is "leave unchanged"
+        :param right: a tuple of absolute (x, y) value of the right joypad
+            where ``None`` is "leave unchanged"
+        :param hat_switch: an absolute angular value of the hat switch
+            where ``None`` is "leave unchanged"
+        :param buttons: a dict of index/bool for the button states,
+            where ``None`` is "leave unchanged"
+        """
+        r = self.create_report(
+            left=left, right=right, hat_switch=hat_switch, buttons=buttons
+        )
+        self.call_input_event(r)
+        return [r]
+
+
+class JoystickGamepad(BaseGamepad):
+    buttons_map = {
+        1: "BTN_TRIGGER",
+        2: "BTN_THUMB",
+        3: "BTN_THUMB2",
+        4: "BTN_TOP",
+        5: "BTN_TOP2",
+        6: "BTN_PINKIE",
+        7: "BTN_BASE",
+        8: "BTN_BASE2",
+        9: "BTN_BASE3",
+        10: "BTN_BASE4",
+        11: "BTN_BASE5",
+        12: "BTN_BASE6",
+        13: "BTN_DEAD",
+    }
+
+    axes_map = {
+        "left_stick": {
+            "x": AxisMapping("x"),
+            "y": AxisMapping("y"),
+        },
+        "right_stick": {
+            "x": AxisMapping("rudder"),
+            "y": AxisMapping("throttle"),
+        },
+    }
+
+    def __init__(self, rdesc, application="Joystick", name=None, input_info=None):
+        super().__init__(rdesc, application, name, input_info)
+
+    def create_report(
+        self,
+        *,
+        left=(None, None),
+        right=(None, None),
+        hat_switch=None,
+        buttons=None,
+        reportID=None,
+        application=None,
+    ):
+        """
+        Return an input report for this device.
+
+        :param left: a tuple of absolute (x, y) value of the left joypad
+            where ``None`` is "leave unchanged"
+        :param right: a tuple of absolute (x, y) value of the right joypad
+            where ``None`` is "leave unchanged"
+        :param hat_switch: an absolute angular value of the hat switch
+            where ``None`` is "leave unchanged"
+        :param buttons: a dict of index/bool for the button states,
+            where ``None`` is "leave unchanged"
+        :param reportID: the numeric report ID for this report, if needed
+        :param application: the application for this report, if needed
+        """
+        if application is None:
+            application = "Joystick"
+        return super().create_report(
+            left=left,
+            right=right,
+            hat_switch=hat_switch,
+            buttons=buttons,
+            reportID=reportID,
+            application=application,
+        )
+
+    def store_right_joystick(self, gamepad, data):
+        gamepad.rudder, gamepad.throttle = data
diff --git a/tools/testing/selftests/hid/tests/test_gamepad.py b/tools/testing/selftests/hid/tests/test_gamepad.py
index 26c74040b796..900fff044348 100644
--- a/tools/testing/selftests/hid/tests/test_gamepad.py
+++ b/tools/testing/selftests/hid/tests/test_gamepad.py
@@ -10,7 +10,10 @@ from . import base
 import libevdev
 import pytest
 
-from hidtools.device.base_gamepad import AsusGamepad, SaitekGamepad
+from .base_gamepad import (
+    AsusGamepad,
+    SaitekGamepad,
+)
 
 import logging
 

-- 
2.44.0


  parent reply	other threads:[~2024-04-10 17:20 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-10 17:19 [PATCH 00/18] HID: Include current HID-BPF fixes in tree Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 01/18] HID: do not assume HAT Switch logical max < 8 Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 02/18] HID: bpf: add first in-tree HID-BPF fix for the XPPen Artist 24 Benjamin Tissoires
2024-04-11  7:09   ` Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 03/18] HID: bpf: add in-tree HID-BPF fix for the XPPen Artist 16 Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 04/18] HID: bpf: add in-tree HID-BPF fix for the HP Elite Presenter Mouse Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 05/18] HID: bpf: add in-tree HID-BPF fix for the IOGear Kaliber Gaming MMOmentum mouse Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 06/18] HID: bpf: add in-tree HID-BPF fix for the Wacom ArtPen Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 07/18] HID: bpf: add in-tree HID-BPF fix for the XBox Elite 2 over Bluetooth Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 08/18] HID: bpf: add in-tree HID-BPF fix for the Huion Kamvas Pro 19 Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 09/18] HID: bpf: add in-tree HID-BPF fix for the Raptor Mach 2 Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 10/18] selftests/hid: import base_device.py from hid-tools Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 11/18] selftests/hid: add support for HID-BPF pre-loading before starting a test Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 12/18] selftests/hid: tablets: reduce the number of pen state Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 13/18] selftests/hid: tablets: add a couple of XP-PEN tablets Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 14/18] selftests/hid: tablets: also check for XP-Pen offset correction Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 15/18] selftests/hid: add Huion Kamvas Pro 19 tests Benjamin Tissoires
2024-04-10 17:19 ` Benjamin Tissoires [this message]
2024-04-10 17:19 ` [PATCH 17/18] selftests/hid: move the gamepads definitions in the test file Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 18/18] selftests/hid: add tests for the Raptor Mach 2 joystick Benjamin Tissoires
2024-05-06 14:36 ` [PATCH 19/18] selftests/hid: skip tests with HID-BPF if udev-hid-bpf is not installed bentiss
2024-05-07  5:43   ` Peter Hutterer
2024-05-07 15:02 ` [PATCH 00/18] HID: Include current HID-BPF fixes in tree Benjamin Tissoires

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240410-bpf_sources-v1-16-a8bf16033ef8@kernel.org \
    --to=bentiss@kernel.org \
    --cc=jikos@kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=peter.hutterer@who-t.net \
    --cc=shuah@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).