public inbox for linux-input@vger.kernel.org
 help / color / mirror / Atom feed
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 10/10] Input: xbox_gip - Add flight stick support
Date: Mon,  9 Mar 2026 22:20:04 -0700	[thread overview]
Message-ID: <20260310052017.1289494-11-vi@endrift.com> (raw)
In-Reply-To: <20260310052017.1289494-1-vi@endrift.com>

This adds preliminary flight stick support, with a few caveats:

- Flight sticks support up to 64 extra buttons. This only exposes the first
  50, as there isn't any good place to map the remainder.
- Flight sticks support up to 12 extra axes. This picks a fairly abritrary
  mapping for them, as there's again no good place to map them.

Flight sticks also have addressible LEDs, but I don't have a device that
supports them so I can't test them yet.

Signed-off-by: Vicki Pfau <vi@endrift.com>
---
 drivers/input/joystick/gip/Makefile           |   2 +-
 drivers/input/joystick/gip/gip-core.c         |   4 +-
 drivers/input/joystick/gip/gip-flight-stick.c | 179 ++++++++++++++++++
 drivers/input/joystick/gip/gip.h              |   1 +
 4 files changed, 184 insertions(+), 2 deletions(-)
 create mode 100644 drivers/input/joystick/gip/gip-flight-stick.c

diff --git a/drivers/input/joystick/gip/Makefile b/drivers/input/joystick/gip/Makefile
index db6c9079c7e18..4de873f77a020 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-arcade-stick.o gip-core.o gip-drivers.o gip-wheel.o
+xbox-gip-y := gip-arcade-stick.o gip-core.o gip-drivers.o gip-flight-stick.o gip-wheel.o
diff --git a/drivers/input/joystick/gip/gip-core.c b/drivers/input/joystick/gip/gip-core.c
index 773d7705b7be8..17e2be5cd2444 100644
--- a/drivers/input/joystick/gip/gip-core.c
+++ b/drivers/input/joystick/gip/gip-core.c
@@ -11,9 +11,10 @@
  * - Sending fragmented messages
  * - Raw character device
  * - Wheel force feedback
- * - Flight stick support
  * - More arcade stick testing
  * - Arcade stick extra buttons
+ * - More flight stick testing
+ * - Flight stick LEDs
  * - Split into driver-per-attachment GIP-as-a-bus approach drivers
  *
  * This driver is based on the Microsoft GIP spec at:
@@ -319,6 +320,7 @@ static const struct gip_driver* base_drivers[] = {
 	&gip_driver_arcade_stick,
 	&gip_driver_trueforce_wheel,
 	&gip_driver_wheel,
+	&gip_driver_flight_stick,
 	NULL /* Sentinel */
 };
 
diff --git a/drivers/input/joystick/gip/gip-flight-stick.c b/drivers/input/joystick/gip/gip-flight-stick.c
new file mode 100644
index 0000000000000..c2b913a012d0e
--- /dev/null
+++ b/drivers/input/joystick/gip/gip-flight-stick.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Drivers for GIP flight stick devices
+ *
+ * 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"
+
+/* FlightStick vendor messages */
+#define GIP_CMD_DEVICE_CAPABILITIES	0x00
+#define GIP_CMD_LED_CAPABILITIES	0x01
+#define GIP_CMD_SET_LED_STATE		0x02
+
+/*
+ * The spec defines up to 64 extra buttons and 12 extra axes, but there's
+ * currently no good way to map all of them. For now, let's leave it as a much
+ * smaller number.and add more when we get a better way.
+ */
+#define MAX_GIP_FLIGHT_STICK_BUTTONS 10
+#define MAX_GIP_FLIGHT_STICK_AXES 1
+
+struct gip_flight_stick_capabilities_response {
+	uint8_t extra_button_count;
+	uint8_t extra_axis_count;
+	uint8_t led_count;
+	uint8_t max_global_led_gain;
+};
+
+static const unsigned int gip_flight_stick_extra_buttons[] = {
+	BTN_0,
+	BTN_1,
+	BTN_2,
+	BTN_3,
+	BTN_4,
+	BTN_5,
+	BTN_6,
+	BTN_7,
+	BTN_8,
+	BTN_9,
+};
+
+static const unsigned int gip_flight_stick_extra_axes[] = {
+	ABS_RUDDER,
+};
+
+static int gip_flight_stick_init(struct gip_attachment *attachment)
+{
+	int rc = gip_send_vendor_message(attachment, GIP_CMD_DEVICE_CAPABILITIES, 0, NULL, 0);
+
+	if (rc < 0)
+		return rc;
+
+	return GIP_INIT_NO_INPUT;
+}
+
+static int gip_setup_flight_stick_input(struct gip_attachment *attachment, struct input_dev* input)
+{
+	int i;
+	int rc = gip_driver_navigation.setup_input(attachment, input);
+
+	if (rc)
+		return rc;
+
+	input_set_capability(input, EV_KEY, BTN_TOP);
+	input_set_capability(input, EV_KEY, BTN_TOP2);
+	for (i = 0; i < attachment->extra_buttons && i < MAX_GIP_FLIGHT_STICK_BUTTONS; i++)
+		input_set_capability(input, EV_KEY, BTN_0 + i);
+	if (attachment->extra_buttons > MAX_GIP_FLIGHT_STICK_BUTTONS)
+		dev_info(GIP_DEV(attachment),
+			"Device has too many extra buttons, %i through %i ignored\n",
+			MAX_GIP_FLIGHT_STICK_BUTTONS,
+			attachment->extra_buttons + 1);
+	input_set_abs_params(input, ABS_X, -32768, 32767, 0, 0);
+	input_set_abs_params(input, ABS_Y, -32768, 32767, 0, 0);
+	input_set_abs_params(input, ABS_Z, -32768, 32767, 0, 0);
+	input_set_abs_params(input, ABS_THROTTLE, 0, 65535, 0, 0);
+	for (i = 0; i < attachment->extra_axes && i < MAX_GIP_FLIGHT_STICK_AXES; i++)
+		input_set_abs_params(input, gip_flight_stick_extra_axes[i], 0, 65535, 0, 0);
+
+	return 0;
+}
+
+static int gip_handle_flight_stick_report(struct gip_attachment *attachment,
+	struct input_dev *input, const uint8_t *bytes, int num_bytes)
+{
+	int32_t axis;
+	int rc = gip_driver_navigation.handle_input_report(attachment, input, bytes, num_bytes);
+	int i;
+
+	if (rc)
+		return rc;
+
+	if (num_bytes < 19)
+		return -EINVAL;
+
+	/* Fire 1 and 2 */
+	input_report_key(input, BTN_TOP, bytes[2] & BIT(0));
+	input_report_key(input, BTN_TOP2, bytes[2] & BIT(1));
+
+	for (i = 0; i < attachment->extra_buttons && i < MAX_GIP_FLIGHT_STICK_BUTTONS; i++) {
+		input_report_key(input, gip_flight_stick_extra_buttons[i],
+			bytes[i / 8 + 3] & BIT(i));
+	}
+
+	/*
+	 * Roll, pitch and yaw are signed. Throttle and any
+	 * extra axes are unsigned. All values are full-range.
+	 */
+	axis = bytes[11];
+	axis |= bytes[12] << 8;
+	input_report_abs(input, ABS_X, (int16_t) axis);
+
+	axis = bytes[13];
+	axis |= bytes[14] << 8;
+	input_report_abs(input, ABS_Y, (int16_t) axis);
+
+	axis = bytes[15];
+	axis |= bytes[16] << 8;
+	input_report_abs(input, ABS_Z, (int16_t) axis);
+
+	axis = bytes[17];
+	axis |= bytes[18] << 8;
+	input_report_abs(input, ABS_THROTTLE, axis);
+
+	for (i = 0; i < attachment->extra_axes && i < MAX_GIP_FLIGHT_STICK_AXES; i++) {
+		if (20 + i * 2 >= num_bytes)
+			break;
+
+		axis = bytes[19 + i * 2];
+		axis |= bytes[20 + i * 2] << 8;
+		input_report_abs(input, gip_flight_stick_extra_axes[i], axis);
+	}
+
+	return 0;
+}
+
+static int gip_handle_flight_stick_cmd_device_capabilities(struct gip_attachment *attachment,
+	const struct gip_header *header, const uint8_t *bytes, int num_bytes)
+{
+	const struct gip_flight_stick_capabilities_response *response =
+		(const struct gip_flight_stick_capabilities_response*)bytes;
+	struct input_dev *input;
+
+	rcu_read_lock();
+	input = rcu_dereference(attachment->input);
+	rcu_read_unlock();
+	if (input)
+		return 0;
+
+	if (num_bytes < 4)
+		return -EINVAL;
+
+	attachment->extra_axes = min(response->extra_axis_count, MAX_GIP_FLIGHT_STICK_AXES);
+	attachment->extra_buttons = min(response->extra_button_count, MAX_GIP_FLIGHT_STICK_BUTTONS);
+	return gip_setup_input_device(attachment);
+}
+
+const struct gip_driver gip_driver_flight_stick = {
+	.types = (const char* const[]) {
+		"Windows.Xbox.Input.FlightStick",
+		"Microsoft.Xbox.Input.FlightStick",
+		NULL
+	},
+	.guid = GUID_INIT(0x03f1a011, 0xefe9, 0x4cc1, 0x96, 0x9c,
+		0x38, 0xdc, 0x55, 0xf4, 0x04, 0xd0),
+
+	.probe = NULL,
+	.remove = NULL,
+	.init = gip_flight_stick_init,
+	.setup_input = gip_setup_flight_stick_input,
+	.handle_input_report = gip_handle_flight_stick_report,
+	.vendor_handlers = {
+		[GIP_CMD_DEVICE_CAPABILITIES] = gip_handle_flight_stick_cmd_device_capabilities,
+	},
+};
diff --git a/drivers/input/joystick/gip/gip.h b/drivers/input/joystick/gip/gip.h
index 3b5cab96dc0b7..6d70a69d99a39 100644
--- a/drivers/input/joystick/gip/gip.h
+++ b/drivers/input/joystick/gip/gip.h
@@ -323,4 +323,5 @@ 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_trueforce_wheel;
+extern const struct gip_driver gip_driver_flight_stick;
 #endif
-- 
2.53.0


  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 ` [PATCH v3 06/10] Input: xbox_gip - Add arcade stick support Vicki Pfau
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 ` Vicki Pfau [this message]
2026-03-10  5:23   ` [PATCH v3 10/10] Input: xbox_gip - Add flight stick support 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-11-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