From: Vicki Pfau <vi@endrift.com>
To: Dmitry Torokhov <dmitry.torokhov@gmail.com>, linux-input@vger.kernel.org
Cc: Vicki Pfau <vi@endrift.com>
Subject: [PATCH v3 06/10] Input: xbox_gip - Add arcade stick support
Date: Mon, 9 Mar 2026 22:20:00 -0700 [thread overview]
Message-ID: <20260310052017.1289494-7-vi@endrift.com> (raw)
In-Reply-To: <20260310052017.1289494-1-vi@endrift.com>
This adds support for the arcade stick device type. Currently I'm only
aware of one Xbox controller that uses this device type, the Razer Atrox,
so testing coverage is limited.
Signed-off-by: Vicki Pfau <vi@endrift.com>
---
drivers/input/joystick/gip/Makefile | 2 +-
drivers/input/joystick/gip/gip-arcade-stick.c | 169 ++++++++++++++++++
drivers/input/joystick/gip/gip-core.c | 6 +-
drivers/input/joystick/gip/gip.h | 4 +-
4 files changed, 176 insertions(+), 5 deletions(-)
create mode 100644 drivers/input/joystick/gip/gip-arcade-stick.c
diff --git a/drivers/input/joystick/gip/Makefile b/drivers/input/joystick/gip/Makefile
index a75e0cace0f92..94ce6462d7ab0 100644
--- a/drivers/input/joystick/gip/Makefile
+++ b/drivers/input/joystick/gip/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-or-later
obj-$(CONFIG_JOYSTICK_XBOX_GIP) += xbox-gip.o
-xbox-gip-y := gip-core.o gip-drivers.o
+xbox-gip-y := gip-arcade-stick.o gip-core.o gip-drivers.o
diff --git a/drivers/input/joystick/gip/gip-arcade-stick.c b/drivers/input/joystick/gip/gip-arcade-stick.c
new file mode 100644
index 0000000000000..c9ee3483e02d8
--- /dev/null
+++ b/drivers/input/joystick/gip/gip-arcade-stick.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Drivers for GIP arcade sticks
+ *
+ * Copyright (c) 2025 Valve Software
+ *
+ * This driver is based on the Microsoft GIP spec at:
+ * https://aka.ms/gipdocs
+ * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gipusb/e7c90904-5e21-426e-b9ad-d82adeee0dbc
+ */
+#include "gip.h"
+
+enum gip_arcade_stick_vibration_motor_support {
+ GIP_VIBRATION_NO_MOTOR = 0,
+ GIP_VIBRATION_SINGLE_MOTOR = 1,
+ GIP_VIBRATION_DUAL_MOTOR = 2,
+};
+
+struct gip_arcade_stick_info {
+ uint8_t vibration_motor;
+ uint8_t actuator_bits;
+};
+
+struct gip_arcade_stick_static_coniguration {
+ uint8_t button_count;
+ uint8_t report_version;
+ uint8_t vibration_motor_support;
+ uint8_t actuator_bits;
+};
+
+static int gip_arcade_stick_probe(struct gip_attachment *attachment)
+{
+ struct gip_arcade_stick_info *info = kzalloc(sizeof(*info), GFP_KERNEL);
+
+ if (!info)
+ return -ENOMEM;
+
+ attachment->quirks |= GIP_QUIRK_SWAP_LB_RB;
+ attachment->driver_data = info;
+
+ return 0;
+}
+
+static void gip_arcade_stick_remove(struct gip_attachment *attachment)
+{
+ kfree(attachment->driver_data);
+ attachment->driver_data = NULL;
+}
+
+static int gip_init_arcade_stick(struct gip_attachment *attachment)
+{
+ if (gip_supports_vendor_message(attachment, GIP_CMD_INITIAL_REPORTS_REQUEST, false)) {
+ uint8_t request = GIP_LL_STATIC_CONFIGURATION;
+ int rc = gip_send_vendor_message(attachment, GIP_CMD_INITIAL_REPORTS_REQUEST, 0,
+ &request, sizeof(request));
+
+ if (rc < 0)
+ return rc;
+
+ return GIP_INIT_NO_INPUT;
+ }
+
+ return 0;
+}
+
+static int gip_setup_arcade_stick_input(struct gip_attachment *attachment, struct input_dev* input)
+{
+ struct gip_arcade_stick_info *info = attachment->driver_data;
+ int rc;
+
+ if (!info)
+ return -ENODEV;
+
+ rc = gip_driver_navigation.setup_input(attachment, input);
+ if (rc < 0)
+ return rc;
+
+ if (info->actuator_bits > 0) {
+ input_set_abs_params(input, ABS_X, 0, (1 << info->actuator_bits) - 1, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, (1 << info->actuator_bits) - 1, 0, 0);
+ }
+
+ if (attachment->extra_buttons >= 1)
+ input_set_capability(input, EV_KEY, BTN_TR2);
+
+ if (attachment->extra_buttons >= 2)
+ input_set_capability(input, EV_KEY, BTN_TL2);
+ return 0;
+}
+
+static int gip_handle_arcade_stick_report(struct gip_attachment *attachment,
+ struct input_dev* input, const uint8_t *bytes, int num_bytes)
+{
+ struct gip_arcade_stick_info *info = attachment->driver_data;
+ int16_t axis;
+ int rc;
+
+ if (!info)
+ return -ENODEV;
+
+ rc = gip_driver_navigation.handle_input_report(attachment, input, bytes, num_bytes);
+ if (rc < 0)
+ return rc;
+
+ if (num_bytes < 6) {
+ dev_dbg(GIP_DEV(attachment), "Discarding too-short input report\n");
+ return -EINVAL;
+ }
+
+ if (info->actuator_bits > 0) {
+ axis = bytes[2];
+ axis |= bytes[3] << 8;
+ input_report_abs(input, ABS_X, axis);
+
+ axis = bytes[4];
+ axis |= bytes[5] << 8;
+ input_report_abs(input, ABS_Y, axis);
+ }
+
+ if (num_bytes >= 19) {
+ /* Extra button 6 */
+ input_report_key(input, BTN_TR2, bytes[18] & BIT(6));
+ /* Extra button 7 */
+ input_report_key(input, BTN_TL2, bytes[18] & BIT(7));
+ }
+
+ return 0;
+}
+
+static int gip_handle_arcade_stick_ll_static_configuration(struct gip_attachment *attachment,
+ const struct gip_header *header, const uint8_t *bytes, int num_bytes)
+{
+ const struct gip_arcade_stick_static_coniguration *config =
+ (const struct gip_arcade_stick_static_coniguration*)bytes;
+ struct gip_arcade_stick_info *info = attachment->driver_data;
+
+ if (!info)
+ return -ENODEV;
+
+ if (num_bytes < 4)
+ return -EINVAL;
+
+ attachment->extra_buttons = clamp(config->button_count, 6, 38) - 6;
+ info->actuator_bits = min(config->actuator_bits, 8);
+
+ if (config->vibration_motor_support == GIP_VIBRATION_NO_MOTOR)
+ attachment->features &= ~GIP_FEATURE_MOTOR_CONTROL;
+
+ return gip_setup_input_device(attachment);
+}
+
+const struct gip_driver gip_driver_arcade_stick = {
+ .types = (const char* const[]) {
+ "Windows.Xbox.Input.ArcadeStick",
+ "Microsoft.Xbox.Input.ArcadeStick",
+ NULL
+ },
+ .guid = GUID_INIT(0x332054cc, 0xa34b, 0x41d5, 0xa3, 0x4a,
+ 0xa6, 0xa6, 0x71, 0x1e, 0xc4, 0xb3),
+
+ .probe = gip_arcade_stick_probe,
+ .remove = gip_arcade_stick_remove,
+ .init = gip_init_arcade_stick,
+ .setup_input = gip_setup_arcade_stick_input,
+ .handle_input_report = gip_handle_arcade_stick_report,
+ .vendor_handlers = {
+ [GIP_LL_STATIC_CONFIGURATION] = gip_handle_arcade_stick_ll_static_configuration,
+ },
+};
diff --git a/drivers/input/joystick/gip/gip-core.c b/drivers/input/joystick/gip/gip-core.c
index 89f8e0ed47344..1164b689856e7 100644
--- a/drivers/input/joystick/gip/gip-core.c
+++ b/drivers/input/joystick/gip/gip-core.c
@@ -12,7 +12,8 @@
* - Raw character device
* - Wheel support
* - Flight stick support
- * - Arcade stick support
+ * - More arcade stick testing
+ * - Arcade stick extra buttons
* - Split into driver-per-attachment GIP-as-a-bus approach drivers
*
* This driver is based on the Microsoft GIP spec at:
@@ -315,6 +316,7 @@ struct gip_direct_motor {
static const struct gip_driver* base_drivers[] = {
&gip_driver_navigation,
&gip_driver_gamepad,
+ &gip_driver_arcade_stick,
NULL /* Sentinel */
};
@@ -1344,7 +1346,7 @@ static int gip_handle_command_raw_report(struct gip_attachment *attachment,
return 0;
}
-static int gip_setup_input_device(struct gip_attachment *attachment)
+int gip_setup_input_device(struct gip_attachment *attachment)
{
struct input_dev *input;
int rc;
diff --git a/drivers/input/joystick/gip/gip.h b/drivers/input/joystick/gip/gip.h
index dd77f16b00fd8..6e5ab8f357aaa 100644
--- a/drivers/input/joystick/gip/gip.h
+++ b/drivers/input/joystick/gip/gip.h
@@ -307,6 +307,8 @@ static inline struct device *gip_device_dev(struct gip_device *device)
return &device->udev->dev;
}
+int gip_setup_input_device(struct gip_attachment *attachment);
+
bool gip_supports_vendor_message(struct gip_attachment *attachment, uint8_t command, bool upstream);
int gip_send_system_message(struct gip_attachment *attachment,
@@ -317,6 +319,4 @@ int gip_send_vendor_message(struct gip_attachment *attachment,
extern const struct gip_driver gip_driver_navigation;
extern const struct gip_driver gip_driver_gamepad;
extern const struct gip_driver gip_driver_arcade_stick;
-extern const struct gip_driver gip_driver_wheel;
-extern const struct gip_driver gip_driver_flight_stick;
#endif
--
2.53.0
next prev parent reply other threads:[~2026-03-10 5:20 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-10 5:19 [PATCH v3 00/10] Input: xbox_gip - Add new driver for Xbox GIP Vicki Pfau
2026-03-10 5:19 ` [PATCH v3 01/10] " Vicki Pfau
2026-03-11 0:41 ` Vicki Pfau
2026-03-10 5:19 ` [PATCH v3 02/10] Input: xpad - Remove Xbox One support Vicki Pfau
2026-03-10 5:19 ` [PATCH v3 03/10] Input: xbox_gip - Add controllable LED support Vicki Pfau
2026-03-10 5:19 ` [PATCH v3 04/10] Input: xbox_gip - Add HID relaying Vicki Pfau
2026-03-10 5:19 ` [PATCH v3 05/10] Input: xbox_gip - Add battery support Vicki Pfau
2026-03-10 5:20 ` Vicki Pfau [this message]
2026-03-10 5:20 ` [PATCH v3 07/10] Input: Add ABS_CLUTCH, HANDBRAKE, and SHIFTER Vicki Pfau
2026-03-10 5:20 ` [PATCH v3 08/10] HID: Map more automobile simulation inputs Vicki Pfau
2026-03-10 5:20 ` [PATCH v3 09/10] Input: xbox_gip - Add wheel support Vicki Pfau
2026-03-10 5:20 ` [PATCH v3 10/10] Input: xbox_gip - Add flight stick support Vicki Pfau
2026-03-10 5:23 ` Vicki Pfau
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=20260310052017.1289494-7-vi@endrift.com \
--to=vi@endrift.com \
--cc=dmitry.torokhov@gmail.com \
--cc=linux-input@vger.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