public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] xpad: expose xinput capabilities via sysattr
@ 2026-03-04  1:03 Sanjay Govind
  2026-03-04  1:22 ` Dmitry Torokhov
  0 siblings, 1 reply; 5+ messages in thread
From: Sanjay Govind @ 2026-03-04  1:03 UTC (permalink / raw)
  To: Dmitry Torokhov, Vicki Pfau, Mario Limonciello, Sanjay Govind,
	Nilton Perim Neto
  Cc: Pierre-Loup A. Griffais, linux-input, linux-kernel

Fetch xinput capabilities for x360 wired and wireless
and then expose them via the following attributes:

ATTRS{xpad/flags}=="3"
ATTRS{xpad/gamepad_buttons}=="ffff"
ATTRS{xpad/gamepad_lsx}=="0"
ATTRS{xpad/gamepad_lsy}=="0"
ATTRS{xpad/gamepad_lt}=="3f"
ATTRS{xpad/gamepad_rsx}=="ffc0"
ATTRS{xpad/gamepad_rsy}=="ffc0"
ATTRS{xpad/gamepad_rt}=="ff"
ATTRS{xpad/rumble_l}=="0"
ATTRS{xpad/rumble_r}=="0"
ATTRS{xpad/subtype}=="7"
ATTRS{xpad/type}=="1"

Signed-off-by: Sanjay Govind <sanjay.govind9@gmail.com>
---
 drivers/input/joystick/xpad.c | 197 ++++++++++++++++++++++++++++++++--
 1 file changed, 188 insertions(+), 9 deletions(-)

diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index bf4accf3f581..70e4a7c85ab5 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -94,6 +94,12 @@
 #define XTYPE_XBOXONE     3
 #define XTYPE_UNKNOWN     4
 
+#define FLAG_FORCE_FEEDBACK 0x01
+#define FLAG_WIRELESS       0x02
+#define FLAG_VOICE          0x04
+#define FLAG_PLUGIN_MODULES 0x08
+#define FLAG_NO_NAVIGATION  0x10
+
 /* Send power-off packet to xpad360w after holding the mode button for this many
  * seconds
  */
@@ -747,6 +753,47 @@ static const struct xboxone_init_packet xboxone_init_packets[] = {
 	XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumbleend_init),
 };
 
+struct xpad_x360_gamepad_descriptor {
+	u8 bLength;
+	u8 bDescriptorType;
+	u8 reserved;
+	u8 type;
+	u8 subType;
+	u8 reserved2;
+	u8 bEndpointAddressIn;
+	u8 bMaxDataSizeIn;
+	u8 reserved3[5];
+	u8 bEndpointAddressOut;
+	u8 bMaxDataSizeOut;
+	u8 reserved4[2];
+} __packed;
+
+struct x360_capabilities {
+	u8 type;
+	u8 subType;
+	struct {
+		u8 id;
+		u8 rsize;
+		u16 buttons;
+		u8 leftTrigger;
+		u8 rightTrigger;
+		u16 leftThumbX;
+		u16 leftThumbY;
+		u16 rightThumbX;
+		u16 rightThumbY;
+		u8 reserved[4];
+		u16 flags;
+	} gamepad;
+	struct {
+		u8 id;
+		u8 rsize;
+		u8 padding;
+		u16 leftMotorSpeed;
+		u16 rightMotorSpeed;
+		u8 padding2[3];
+	} vibration;
+} __packed;
+
 struct xpad_output_packet {
 	u8 data[XPAD_PKT_LEN];
 	u8 len;
@@ -795,6 +842,7 @@ struct usb_xpad {
 	int xtype;			/* type of xbox device */
 	int packet_type;		/* type of the extended packet */
 	int pad_nr;			/* the order x360 pads were attached */
+	struct x360_capabilities capabilities; /* capabilities of the device */
 	const char *name;		/* name of the device */
 	struct work_struct work;	/* init/remove device from callback */
 	time64_t mode_btn_down_ts;
@@ -802,11 +850,63 @@ struct usb_xpad {
 	bool delayed_init_done;
 };
 
+#define XPAD_SHOW(name, object) \
+static ssize_t name##_show(struct device *dev,\
+			   struct device_attribute *attr,\
+			   char *buf)\
+{\
+	struct usb_xpad *xpad = input_get_drvdata(to_input_dev(dev));\
+\
+	return sysfs_emit(buf, "%x\n", xpad->capabilities.object);\
+} \
+\
+static DEVICE_ATTR_RO(name)
+
+XPAD_SHOW(subtype, subType);
+XPAD_SHOW(type, type);
+XPAD_SHOW(flags, gamepad.flags);
+XPAD_SHOW(gamepad_buttons, gamepad.buttons);
+XPAD_SHOW(gamepad_lt, gamepad.leftTrigger);
+XPAD_SHOW(gamepad_rt, gamepad.rightTrigger);
+XPAD_SHOW(gamepad_lsx, gamepad.leftThumbX);
+XPAD_SHOW(gamepad_lsy, gamepad.leftThumbY);
+XPAD_SHOW(gamepad_rsx, gamepad.rightThumbX);
+XPAD_SHOW(gamepad_rsy, gamepad.rightThumbY);
+XPAD_SHOW(rumble_l, vibration.leftMotorSpeed);
+XPAD_SHOW(rumble_r, vibration.rightMotorSpeed);
+
+static struct attribute *xpad_attrs[] = {
+	&dev_attr_type.attr,
+	&dev_attr_subtype.attr,
+	&dev_attr_flags.attr,
+	&dev_attr_gamepad_buttons.attr,
+	&dev_attr_gamepad_lt.attr,
+	&dev_attr_gamepad_rt.attr,
+	&dev_attr_gamepad_lsx.attr,
+	&dev_attr_gamepad_lsy.attr,
+	&dev_attr_gamepad_rsx.attr,
+	&dev_attr_gamepad_rsy.attr,
+	&dev_attr_rumble_l.attr,
+	&dev_attr_rumble_r.attr,
+	NULL
+};
+
+static struct attribute_group xpad_group = {
+	.attrs = xpad_attrs,
+	.name = "xpad"
+};
+
+static const struct attribute_group *xpad_groups[] = {
+	&xpad_group,
+	NULL,
+};
+
 static int xpad_init_input(struct usb_xpad *xpad);
 static void xpad_deinit_input(struct usb_xpad *xpad);
 static int xpad_start_input(struct usb_xpad *xpad);
 static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num);
 static void xpad360w_poweroff_controller(struct usb_xpad *xpad);
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad);
 
 /*
  *	xpad_process_packet
@@ -1032,6 +1132,29 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
 		}
 	}
 
+	/* Link report */
+	if (data[0] == 0x00 && data[1] == 0x0F) {
+		xpad->capabilities.subType = data[25] & 0x7f;
+		xpad->capabilities.gamepad.flags = FLAG_WIRELESS;
+		if ((data[25] & 0x80) != 0)
+			xpad->capabilities.gamepad.flags |= FLAG_FORCE_FEEDBACK;
+		xpad_inquiry_pad_capabilities(xpad);
+	}
+
+	/* Capabilities report */
+	if (data[0] == 0x00 && data[1] == 0x05 && data[5] == 0x12) {
+		xpad->capabilities.gamepad.buttons = (data[7] << 8) | data[6];
+		xpad->capabilities.gamepad.leftTrigger = data[8];
+		xpad->capabilities.gamepad.rightTrigger = data[9];
+		xpad->capabilities.gamepad.leftThumbX = (data[11] << 8) | data[10];
+		xpad->capabilities.gamepad.leftThumbY = (data[13] << 8) | data[12];
+		xpad->capabilities.gamepad.rightThumbX = (data[15] << 8) | data[14];
+		xpad->capabilities.gamepad.rightThumbY = (data[17] << 8) | data[16];
+		xpad->capabilities.gamepad.flags |= data[20];
+		xpad->capabilities.vibration.leftMotorSpeed = data[18];
+		xpad->capabilities.vibration.rightMotorSpeed = data[19];
+	}
+
 	/* Valid pad data */
 	if (data[1] != 0x1)
 		return;
@@ -1495,6 +1618,31 @@ static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
 	return xpad_try_sending_next_out_packet(xpad);
 }
 
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad)
+{
+	struct xpad_output_packet *packet =
+			&xpad->out_packets[XPAD_OUT_CMD_IDX];
+
+	guard(spinlock_irqsave)(&xpad->odata_lock);
+
+	packet->data[0] = 0x00;
+	packet->data[1] = 0x00;
+	packet->data[2] = 0x02;
+	packet->data[3] = 0x80;
+	packet->data[4] = 0x00;
+	packet->data[5] = 0x00;
+	packet->data[6] = 0x00;
+	packet->data[7] = 0x00;
+	packet->data[8] = 0x00;
+	packet->data[9] = 0x00;
+	packet->data[10] = 0x00;
+	packet->data[11] = 0x00;
+	packet->len = 12;
+	packet->pending = true;
+
+	return xpad_try_sending_next_out_packet(xpad);
+}
+
 static int xpad_start_xbox_one(struct usb_xpad *xpad)
 {
 	int error;
@@ -1808,25 +1956,37 @@ static int xpad_start_input(struct usb_xpad *xpad)
 		}
 	}
 	if (xpad->xtype == XTYPE_XBOX360) {
-		/*
-		 * Some third-party controllers Xbox 360-style controllers
-		 * require this message to finish initialization.
-		 */
-		u8 dummy[20];
-
 		error = usb_control_msg_recv(xpad->udev, 0,
 					     /* bRequest */ 0x01,
 					     /* bmRequestType */
 					     USB_TYPE_VENDOR | USB_DIR_IN |
-						USB_RECIP_INTERFACE,
+					     USB_RECIP_INTERFACE,
 					     /* wValue */ 0x100,
 					     /* wIndex */ 0x00,
-					     dummy, sizeof(dummy),
+					     &xpad->capabilities.gamepad,
+					     sizeof(xpad->capabilities.gamepad),
 					     25, GFP_KERNEL);
 		if (error)
 			dev_warn(&xpad->dev->dev,
-				 "unable to receive magic message: %d\n",
+				 "unable to receive input capabilities: %d\n",
 				 error);
+
+		if (xpad->capabilities.gamepad.flags & FLAG_FORCE_FEEDBACK) {
+			error = usb_control_msg_recv(xpad->udev, 0,
+						     /* bRequest */ 0x01,
+						     /* bmRequestType */
+						     USB_TYPE_VENDOR | USB_DIR_IN |
+						     USB_RECIP_INTERFACE,
+						     /* wValue */ 0x00,
+						     /* wIndex */ 0x00,
+						     &xpad->capabilities.vibration,
+						     sizeof(xpad->capabilities.vibration),
+						     25, GFP_KERNEL);
+			if (error)
+				dev_warn(&xpad->dev->dev,
+					 "unable to receive vibration capabilities: %d\n",
+					 error);
+		}
 	}
 
 	return 0;
@@ -1953,6 +2113,7 @@ static void xpad_deinit_input(struct usb_xpad *xpad)
 static int xpad_init_input(struct usb_xpad *xpad)
 {
 	struct input_dev *input_dev;
+	struct xpad_x360_gamepad_descriptor *input_desc;
 	int i, error;
 
 	input_dev = input_allocate_device();
@@ -1962,11 +2123,29 @@ static int xpad_init_input(struct usb_xpad *xpad)
 	xpad->dev = input_dev;
 	input_dev->name = xpad->name;
 	input_dev->phys = xpad->phys;
+	xpad->capabilities.subType = 1;
+	xpad->capabilities.type = 1;
+	xpad->capabilities.gamepad.flags = 0;
+	xpad->capabilities.gamepad.buttons = 0xFFFF;
+	xpad->capabilities.gamepad.leftTrigger = 0xFF;
+	xpad->capabilities.gamepad.rightTrigger = 0xFF;
+	xpad->capabilities.gamepad.leftThumbX = 0xFFC0;
+	xpad->capabilities.gamepad.leftThumbY = 0xFFC0;
+	xpad->capabilities.gamepad.rightThumbX = 0xFFC0;
+	xpad->capabilities.gamepad.rightThumbY = 0xFFC0;
+	xpad->dev->dev.groups = xpad_groups;
 	usb_to_input_id(xpad->udev, &input_dev->id);
 
 	if (xpad->xtype == XTYPE_XBOX360W) {
 		/* x360w controllers and the receiver have different ids */
 		input_dev->id.product = 0x02a1;
+		xpad->capabilities.gamepad.flags = FLAG_WIRELESS;
+	}
+
+	if (xpad->xtype == XTYPE_XBOX360 &&
+	    usb_get_extra_descriptor(xpad->intf->cur_altsetting, 0x21, &input_desc) == 0) {
+		xpad->capabilities.subType = input_desc->subType;
+		xpad->capabilities.type = input_desc->type;
 	}
 
 	input_dev->dev.parent = &xpad->intf->dev;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] xpad: expose xinput capabilities via sysattr
  2026-03-04  1:03 [PATCH] xpad: expose xinput capabilities via sysattr Sanjay Govind
@ 2026-03-04  1:22 ` Dmitry Torokhov
       [not found]   ` <CALQgdA0PL82yLxWbjcqchWzsf+bA7_Egq9hZFzz7toL4kByAvw@mail.gmail.com>
  0 siblings, 1 reply; 5+ messages in thread
From: Dmitry Torokhov @ 2026-03-04  1:22 UTC (permalink / raw)
  To: Sanjay Govind
  Cc: Vicki Pfau, Mario Limonciello, Nilton Perim Neto,
	Pierre-Loup A. Griffais, linux-input, linux-kernel

Hi Sanjay,

On Wed, Mar 04, 2026 at 02:03:43PM +1300, Sanjay Govind wrote:
> Fetch xinput capabilities for x360 wired and wireless
> and then expose them via the following attributes:
> 
> ATTRS{xpad/flags}=="3"
> ATTRS{xpad/gamepad_buttons}=="ffff"
> ATTRS{xpad/gamepad_lsx}=="0"
> ATTRS{xpad/gamepad_lsy}=="0"
> ATTRS{xpad/gamepad_lt}=="3f"
> ATTRS{xpad/gamepad_rsx}=="ffc0"
> ATTRS{xpad/gamepad_rsy}=="ffc0"
> ATTRS{xpad/gamepad_rt}=="ff"
> ATTRS{xpad/rumble_l}=="0"
> ATTRS{xpad/rumble_r}=="0"
> ATTRS{xpad/subtype}=="7"
> ATTRS{xpad/type}=="1"

My inclination is "No" as we have other ways to communicate this

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] xpad: expose xinput capabilities via sysattr
       [not found]   ` <CALQgdA0PL82yLxWbjcqchWzsf+bA7_Egq9hZFzz7toL4kByAvw@mail.gmail.com>
@ 2026-03-04  1:34     ` Dmitry Torokhov
       [not found]       ` <CALQgdA0q=+6L2mQBqNPUBzo+HE6=AjhSPv5G_dK9g=V2npSsWA@mail.gmail.com>
  0 siblings, 1 reply; 5+ messages in thread
From: Dmitry Torokhov @ 2026-03-04  1:34 UTC (permalink / raw)
  To: Sanjay Govind
  Cc: Vicki Pfau, Mario Limonciello, Nilton Perim Neto,
	Pierre-Loup A. Griffais, linux-input, linux-kernel

On Wed, Mar 04, 2026 at 02:27:42PM +1300, Sanjay Govind wrote:
> I don't really mind how we communicate this data, if there's a better route
> for exposing these capabilities I'd be happy to use that.

Please do not top post.

You need to start with enumerating what data is currently not available
through other means and why it is needed. Is this something that other
gamepad-like devices also lack?

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] xpad: expose xinput capabilities via sysattr
       [not found]       ` <CALQgdA0q=+6L2mQBqNPUBzo+HE6=AjhSPv5G_dK9g=V2npSsWA@mail.gmail.com>
@ 2026-03-04 19:58         ` Sanjay Govind
  2026-03-11  5:47           ` Sanjay Govind
  0 siblings, 1 reply; 5+ messages in thread
From: Sanjay Govind @ 2026-03-04 19:58 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Vicki Pfau, Mario Limonciello, Nilton Perim Neto,
	Pierre-Loup A. Griffais, linux-input, linux-kernel

On Wed, Mar 4, 2026 at 2:34 PM Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> You need to start with enumerating what data is currently not available
> through other means and why it is needed. Is this something that other
> gamepad-like devices also lack?

Other controllers do have this sort of thing though, PS4 and PS5
controllers have a capabilities report, but since they are HID based,
SDL just requests the report directly via a hid get feature report
request.
Since xinput isn't HID based, there isn't a way to fetch these
reports, and right now that makes it difficult to discern these
devices in SDL and Wine.
Usually you could also fall back and rely on vids and pids, but 360
wireless devices don't include pids anywhere, so that isn't a usable
method here.
The only other devices I can think of that would have similar problems
would be the Xbox One controllers, but at least there we do have a
workable vid and pid, so we can just rely on that since the descriptor
format is much more complicated in that scenario.

If it was prefered, we could go some route where we put the entire
capabilities blob into a single sysattr as JSON or something, which is
similar to the route unity went with for this sort of thing, or if
there is some way to request data in a similar fashion to how HID has
feature reports, i'd be happy to just implement some sort of
capabilities request, I just didn't see a way to do that when i was
trying to implement this.

On Wed, Mar 4, 2026 at 2:46 PM Sanjay Govind <sanjay.govind9@gmail.com> wrote:
>
> On Wed, Mar 4, 2026 at 2:34 PM Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote:
> > You need to start with enumerating what data is currently not available
> > through other means and why it is needed. Is this something that other
> > gamepad-like devices also lack?
>
> This information in particular is important for xinput specifically, as it gives context for determining what kind of controller is in use.
> For most other gamepads, that was done via the vid and pid, but for xinput that isn't enough as the vid and pid isn't exposed over wireless.
> XInput also just doesn't expose the vid and pid on windows, so a lot of controllers encode information into the other capabilities like the sticks,
> and the flags, so you need the entire capabilities blob to properly differentiate every controller, and my goal is to then feed this into Wine and SDL,
> so that they can properly differentiate these controllers.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] xpad: expose xinput capabilities via sysattr
  2026-03-04 19:58         ` Sanjay Govind
@ 2026-03-11  5:47           ` Sanjay Govind
  0 siblings, 0 replies; 5+ messages in thread
From: Sanjay Govind @ 2026-03-11  5:47 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Vicki Pfau, Mario Limonciello, Nilton Perim Neto,
	Pierre-Loup A. Griffais, linux-input, linux-kernel

On Wed, Mar 4, 2026 at 2:34 PM Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> You need to start with enumerating what data is currently not available
> through other means and why it is needed. Is this something that other
> gamepad-like devices also lack?

Thinking about this some more, I think what I'm really after is just a
generic way for an app to request a vendor-defined report from a
controller, so that libraries like SDL can better request the
capabilities of a device when trying to handle devices properly. As it
stands, PS4, PS5, OG Xbox, Xbox 360 and Xbox One all have vendor
defined reports that can be fetched for describing controller
information, but only PS4 and PS5 actually even give something like
SDL the ability to fetch that data right now because they can just do
hid feature report reads to get it, while the Xbox controllers don't
use HID and thus we need to expose that from the input layer.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-03-11  5:47 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-04  1:03 [PATCH] xpad: expose xinput capabilities via sysattr Sanjay Govind
2026-03-04  1:22 ` Dmitry Torokhov
     [not found]   ` <CALQgdA0PL82yLxWbjcqchWzsf+bA7_Egq9hZFzz7toL4kByAvw@mail.gmail.com>
2026-03-04  1:34     ` Dmitry Torokhov
     [not found]       ` <CALQgdA0q=+6L2mQBqNPUBzo+HE6=AjhSPv5G_dK9g=V2npSsWA@mail.gmail.com>
2026-03-04 19:58         ` Sanjay Govind
2026-03-11  5:47           ` Sanjay Govind

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox