From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
To: Sanjay Govind <sanjay.govind9@gmail.com>
Cc: Vicki Pfau <vi@endrift.com>,
Nilton Perim Neto <niltonperimneto@gmail.com>,
Mario Limonciello <mario.limonciello@amd.com>,
Antheas Kapenekakis <lkml@antheas.dev>,
"Pierre-Loup A. Griffais" <pgriffais@valvesoftware.com>,
linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2] xpad: Overhaul device data for wireless devices
Date: Mon, 6 Apr 2026 21:46:19 -0700 [thread overview]
Message-ID: <adSJp5U47g1JnImj@google.com> (raw)
In-Reply-To: <20260404083928.489966-3-sanjay.govind9@gmail.com>
Hi Sanjay,
On Sat, Apr 04, 2026 at 09:39:27PM +1300, Sanjay Govind wrote:
> Xbox 360 wireless controllers expose information in the link and
> capabilities reports.
>
> Extract and use the vendor id for wireless controllers, and use
> the subtype to build a nicer device name and product id.
>
> Some xbox 360 controllers put a vid and pid into the stick capability
> data, so check if this was done, and pull the vid, pid and revision from
> there.
Sashuko correctly identified issues areoud re-scheduling work that
already completed, please see:
https://sashiko.dev/#/patchset/20260404083928.489966-3-sanjay.govind9@gmail.com
>
> Signed-off-by: Sanjay Govind <sanjay.govind9@gmail.com>
> ---
> v2: Delay marking device as present until after capabilities or timeout
> drivers/input/joystick/xpad.c | 162 ++++++++++++++++++++++++++++++++--
> 1 file changed, 155 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
> index bf4accf3f581..c35512c7c199 100644
> --- a/drivers/input/joystick/xpad.c
> +++ b/drivers/input/joystick/xpad.c
> @@ -68,6 +68,7 @@
> #include <linux/slab.h>
> #include <linux/stat.h>
> #include <linux/module.h>
> +#include <linux/unaligned.h>
> #include <linux/usb/input.h>
> #include <linux/usb/quirks.h>
>
> @@ -94,6 +95,22 @@
> #define XTYPE_XBOXONE 3
> #define XTYPE_UNKNOWN 4
>
> +#define FLAG_FORCE_FEEDBACK 0x01
> +
> +#define SUBTYPE_GAMEPAD 0x01
> +#define SUBTYPE_WHEEL 0x02
> +#define SUBTYPE_ARCADE_STICK 0x03
> +#define SUBTYPE_FLIGHT_SICK 0x04
> +#define SUBTYPE_DANCE_PAD 0x05
> +#define SUBTYPE_GUITAR 0x06
> +#define SUBTYPE_GUITAR_ALTERNATE 0x07
> +#define SUBTYPE_DRUM_KIT 0x08
> +#define SUBTYPE_GUITAR_BASS 0x0B
> +#define SUBTYPE_RB_KEYBOARD 0x0F
> +#define SUBTYPE_ARCADE_PAD 0x13
> +#define SUBTYPE_TURNTABLE 0x17
> +#define SUBTYPE_PRO_GUITAR 0x19
> +
> /* Send power-off packet to xpad360w after holding the mode button for this many
> * seconds
> */
> @@ -795,8 +812,13 @@ 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 */
> + u8 sub_type;
> + u16 flags;
> + u16 wireless_vid;
> + u16 wireless_pid;
> + u16 wireless_version;
> const char *name; /* name of the device */
> - struct work_struct work; /* init/remove device from callback */
> + struct delayed_work work; /* init/remove device from callback */
> time64_t mode_btn_down_ts;
> bool delay_init; /* init packets should be delayed */
> bool delayed_init_done;
> @@ -807,6 +829,8 @@ 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
> @@ -980,7 +1004,7 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
>
> static void xpad_presence_work(struct work_struct *work)
> {
> - struct usb_xpad *xpad = container_of(work, struct usb_xpad, work);
> + struct usb_xpad *xpad = container_of(work, struct usb_xpad, work.work);
> int error;
>
> if (xpad->pad_present) {
> @@ -1028,10 +1052,60 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
>
> if (xpad->pad_present != present) {
> xpad->pad_present = present;
> - schedule_work(&xpad->work);
> + if (present) {
> + /*
> + * Delay marking device as present, so we can make sure
> + * we have received all the information from the capabilities
> + * report. Some devices don't send one, so the delay
> + * guarantees that these devices are still initialized.
> + */
> + schedule_delayed_work(&xpad->work, msecs_to_jiffies(500));
> + } else {
> + schedule_delayed_work(&xpad->work, 0);
schedule_delayed_work(&xpad->work,
present ? msecs_to_jiffies(500) : 0);
> + }
> }
> }
>
> + /* Link report */
> + if (data[0] == 0x00 && data[1] == 0x0F) {
> + xpad->sub_type = data[25] & 0x7f;
> +
> + /* Decode vendor id from link report */
> + xpad->wireless_vid = ((data[0x16] & 0xf) | data[0x18] << 4) << 8 | data[0x17];
> + /*
> + * x360w controllers on windows put the subtype into the product
> + * for wheels and gamepads, but it makes sense to do it for all
> + * subtypes. This will be used if the capabilities report
> + * doesn't provide us with a product id later.
> + */
> + xpad->wireless_pid = 0x02a0 + xpad->sub_type;
> + xpad->wireless_version = 0;
> +
> + if ((data[25] & 0x80) != 0)
> + xpad->flags |= FLAG_FORCE_FEEDBACK;
> +
> + xpad_inquiry_pad_capabilities(xpad);
> + }
> +
> + /* Capabilities report */
> + if (data[0] == 0x00 && data[1] == 0x05 && data[5] == 0x12) {
> + xpad->flags |= data[20];
> + /*
> + * A bunch of vendors started putting vids and pids
> + * into capabilities data because they can't be
> + * retrieved by xinput easliy.
> + * Not all of them do though, so check the vids match
> + * before extracting that info.
> + */
> + if (get_unaligned_le16(data + 10) == xpad->wireless_vid) {
> + xpad->wireless_pid = get_unaligned_le16(data + 12);
> + xpad->wireless_version = get_unaligned_le16(data + 14);
> + }
> + /* We got the capabilities report, so mark the device present now */
> + cancel_delayed_work(&xpad->work);
> + schedule_delayed_work(&xpad->work, 0);
mod_delayed_work(&xpad->work, 0);
> + }
> +
> /* Valid pad data */
> if (data[1] != 0x1)
> return;
> @@ -1495,6 +1569,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;
> @@ -1893,7 +1992,7 @@ static void xpad360w_stop_input(struct usb_xpad *xpad)
> usb_kill_urb(xpad->irq_in);
>
> /* Make sure we are done with presence work if it was scheduled */
> - flush_work(&xpad->work);
> + flush_delayed_work(&xpad->work);
Maybe this should be disable_delayed_work() instead...
> }
>
> static int xpad_open(struct input_dev *dev)
> @@ -1965,8 +2064,57 @@ static int xpad_init_input(struct usb_xpad *xpad)
> 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;
> + /* If the Link report has provided a vid, it won't be set to 1 */
> + if (xpad->wireless_vid != 1)
When will it be set to 1?
Thanks.
--
Dmitry
next prev parent reply other threads:[~2026-04-07 4:46 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-04 8:39 [PATCH v2] xpad: Overhaul device data for wireless devices Sanjay Govind
2026-04-07 4:46 ` Dmitry Torokhov [this message]
2026-04-07 6:36 ` Sanjay Govind
2026-04-07 19:22 ` Sanjay Govind
2026-04-08 4:52 ` Dmitry Torokhov
2026-04-08 5:04 ` Sanjay Govind
2026-04-08 15:47 ` Antheas Kapenekakis
2026-04-08 17:23 ` Dmitry Torokhov
2026-04-08 17:45 ` Roman Gushchin
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=adSJp5U47g1JnImj@google.com \
--to=dmitry.torokhov@gmail.com \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lkml@antheas.dev \
--cc=mario.limonciello@amd.com \
--cc=niltonperimneto@gmail.com \
--cc=pgriffais@valvesoftware.com \
--cc=sanjay.govind9@gmail.com \
--cc=vi@endrift.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.