From nobody Wed Apr 15 13:34:46 2026 Received: from mail-pj1-f51.google.com (mail-pj1-f51.google.com [209.85.216.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DD7B16FC5 for ; Wed, 4 Mar 2026 01:04:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772586301; cv=none; b=DpgeD9IwCyJ5xCTd6zCIDVWbfOr9HMyshoG3XKuzEiiTiBgG1nG6+WuQZFfEDiIxx8m/1CnestKhMqZF9AjQO9gvg5gZie1cso5TijCboIBxzvcD6ZwO9MOP+BKHqu/8tMVMf9sfcJV6AOgbvdyurGlJ8AJLHYYYfpK7o/82omo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772586301; c=relaxed/simple; bh=gaRuJO7wzObKSeJFzbCH5S8fe6XSRkoKw4n5enqMhmg=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=m9/hZwreW2wSeDmZfn37cIR5VFKsWLolqHs3E5O1EnR/O4hKzKLyP+0C+ArZlYo129ah6GxgjBsRwRlFOWb31EF93eu4vUSzH0TON7zO0utN/gGQz370Q0B2EPRSF8drsatLCWoAtIzIbyXtE83+r1aPOkQOF5TPrUC5klm3DT0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=NVUJva0f; arc=none smtp.client-ip=209.85.216.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="NVUJva0f" Received: by mail-pj1-f51.google.com with SMTP id 98e67ed59e1d1-3597fea200dso2400779a91.3 for ; Tue, 03 Mar 2026 17:04:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772586298; x=1773191098; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=wAPoSa4AQv8RODkGu3q6FPMnaa+Z1UifihLeOdR33/Y=; b=NVUJva0f8UMP/xQwtPG7FerwW7qGC0rAo4krpzw6uYQd6s5FwdGHN78ZHwJJcwInwz fcdg6CiLSC+pBcTeJp/YwNo0IrS1cOggWLb9+FFUIElQfH7cUIeEsjhEXV6ml04FmQxF BubkGIn921vF+G09d9yg0uk8FzGqWG7H3sx7zB2llh1NNFPIabMzr8A6d6s2mIvAlQVM wfg7UByxcf/LhigCiNFD6uKv2cdNfkw/r/NP0yY9F4SP2QmKJOvWGx5XBX2eX8T5AJ+D 7ZXwmnANgt6LrC0KiHbgzL/lhs31bsIldZYqCcSrTJaQF44Uep4eLm4Rzcbp65sj2oNL pwXw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772586298; x=1773191098; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=wAPoSa4AQv8RODkGu3q6FPMnaa+Z1UifihLeOdR33/Y=; b=OB5KSna/Fo3PBBisivK/KJVPE8wuWh8gZ4TR8dP/AraUz6pIMhDVLl9x/DVTmO7GI/ XTyEenwK9gF1zVndjO8zoAl9FY76JolfcWMacBqUto+oz1hJ6Mi9S92qqKEWtqvWQFno brx6l0t/PyEDPJXa5YmskdoAFkLXH34Ue3QO0mZ43JeixlLrIn/sLgN9szjnbboOyLBZ /+8A6Qi21vSZYZI5InGpSFK54nt981ODTrH7sIHcFbT+wAhuunj6PoFIRBpCVJHjxyyj Kum3zfbbmuA7kph3EdkxCzFqhLk2iSrEdDmU43KJznyGCpMhqzpIckWVHXOf0G75kPP9 Jvtg== X-Forwarded-Encrypted: i=1; AJvYcCVzpD+VhYc23RWn4GU4h0L+xoSrgCxI7u7mIPV2NsGHMr6fdq1aO9RXjXJEHQ03ch6XgR26oaY9EZw2zKI=@vger.kernel.org X-Gm-Message-State: AOJu0YxrSKPFlmw/J7IRFeC+UfACORCLnOs7fqPi3fWE9og/4R9EFnIM /evIz5WA6cb9tC6igraBZe8GV2BSPhQQ/DxA9hmebrPRGx2PNr60aCJ9 X-Gm-Gg: ATEYQzzcyXgLdGkcVJ+tPtYCTK5o3oRQ5RgmggOXvBYqMTg758iJypYWDszNlaNsMl3 58nwpyhbpyb3jmehfJ9gEJTt8rWmfH3De4FmDW4fXV9S0pxUStq0niYlYnfn722bWtxvyOrMzhV Q9b7IO6h5lCQDoYFqXP6OSjwij+QxQpv8oxIytcKlhiDfnCqwZOiF/TWPOtvvBouPnNWD0ZDWwt wksG0C9NjEK9BxpAWW9TAwcVyX5DqKNN3ru/DRV/uzzzNj/rDTGIxp1igomL28JxaJnPm/l4iKG rVOlR60gV/Q03ePStArRUcYqhYRekGkcgXyfgEIdo+sTc8iX0N1DxzI571mGfkpdK5QZBtbkf8/ 7xhO2GHt9jMvCqNc8hXDUOtT7Crfij5LKOXgdMokgSRX6vMXPtFRZDtoobk43U3RvzYXHmmHpxC y9RB1E7hA4KXHOj9KuAikTBd9EJBwzSR6+y5zxAzoGIQabsygmbrV3PqCjJ8C1h+/Qej1zaMSZb JdOlEm13tsK7/AFAcYzVcDhwg4fDl342X5iOI2fGVrnb1/B2aEqUMVsf3Me4ushSTY= X-Received: by 2002:a17:90a:ec8e:b0:356:3cfd:3ee1 with SMTP id 98e67ed59e1d1-359a6a509camr319746a91.23.1772586298127; Tue, 03 Mar 2026 17:04:58 -0800 (PST) Received: from sanjays-pc.govzhome.govindz.co.nz ([116.251.152.103]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3599c408389sm3937421a91.8.2026.03.03.17.04.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Mar 2026 17:04:57 -0800 (PST) From: Sanjay Govind To: Dmitry Torokhov , Vicki Pfau , Mario Limonciello , Sanjay Govind , Nilton Perim Neto Cc: "Pierre-Loup A. Griffais" , linux-input@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH] xpad: expose xinput capabilities via sysattr Date: Wed, 4 Mar 2026 14:03:43 +1300 Message-ID: <20260304010345.1355896-2-sanjay.govind9@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Fetch xinput capabilities for x360 wired and wireless and then expose them via the following attributes: ATTRS{xpad/flags}=3D=3D"3" ATTRS{xpad/gamepad_buttons}=3D=3D"ffff" ATTRS{xpad/gamepad_lsx}=3D=3D"0" ATTRS{xpad/gamepad_lsy}=3D=3D"0" ATTRS{xpad/gamepad_lt}=3D=3D"3f" ATTRS{xpad/gamepad_rsx}=3D=3D"ffc0" ATTRS{xpad/gamepad_rsy}=3D=3D"ffc0" ATTRS{xpad/gamepad_rt}=3D=3D"ff" ATTRS{xpad/rumble_l}=3D=3D"0" ATTRS{xpad/rumble_r}=3D=3D"0" ATTRS{xpad/subtype}=3D=3D"7" ATTRS{xpad/type}=3D=3D"1" Signed-off-by: Sanjay Govind --- 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 =20 +#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 thi= s many * seconds */ @@ -747,6 +753,47 @@ static const struct xboxone_init_packet xboxone_init_p= ackets[] =3D { XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumbleend_init), }; =20 +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; }; =20 +#define XPAD_SHOW(name, object) \ +static ssize_t name##_show(struct device *dev,\ + struct device_attribute *attr,\ + char *buf)\ +{\ + struct usb_xpad *xpad =3D 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[] =3D { + &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 =3D { + .attrs =3D xpad_attrs, + .name =3D "xpad" +}; + +static const struct attribute_group *xpad_groups[] =3D { + &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); =20 /* * xpad_process_packet @@ -1032,6 +1132,29 @@ static void xpad360w_process_packet(struct usb_xpad = *xpad, u16 cmd, unsigned cha } } =20 + /* Link report */ + if (data[0] =3D=3D 0x00 && data[1] =3D=3D 0x0F) { + xpad->capabilities.subType =3D data[25] & 0x7f; + xpad->capabilities.gamepad.flags =3D FLAG_WIRELESS; + if ((data[25] & 0x80) !=3D 0) + xpad->capabilities.gamepad.flags |=3D FLAG_FORCE_FEEDBACK; + xpad_inquiry_pad_capabilities(xpad); + } + + /* Capabilities report */ + if (data[0] =3D=3D 0x00 && data[1] =3D=3D 0x05 && data[5] =3D=3D 0x12) { + xpad->capabilities.gamepad.buttons =3D (data[7] << 8) | data[6]; + xpad->capabilities.gamepad.leftTrigger =3D data[8]; + xpad->capabilities.gamepad.rightTrigger =3D data[9]; + xpad->capabilities.gamepad.leftThumbX =3D (data[11] << 8) | data[10]; + xpad->capabilities.gamepad.leftThumbY =3D (data[13] << 8) | data[12]; + xpad->capabilities.gamepad.rightThumbX =3D (data[15] << 8) | data[14]; + xpad->capabilities.gamepad.rightThumbY =3D (data[17] << 8) | data[16]; + xpad->capabilities.gamepad.flags |=3D data[20]; + xpad->capabilities.vibration.leftMotorSpeed =3D data[18]; + xpad->capabilities.vibration.rightMotorSpeed =3D data[19]; + } + /* Valid pad data */ if (data[1] !=3D 0x1) return; @@ -1495,6 +1618,31 @@ static int xpad_inquiry_pad_presence(struct usb_xpad= *xpad) return xpad_try_sending_next_out_packet(xpad); } =20 +static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad) +{ + struct xpad_output_packet *packet =3D + &xpad->out_packets[XPAD_OUT_CMD_IDX]; + + guard(spinlock_irqsave)(&xpad->odata_lock); + + packet->data[0] =3D 0x00; + packet->data[1] =3D 0x00; + packet->data[2] =3D 0x02; + packet->data[3] =3D 0x80; + packet->data[4] =3D 0x00; + packet->data[5] =3D 0x00; + packet->data[6] =3D 0x00; + packet->data[7] =3D 0x00; + packet->data[8] =3D 0x00; + packet->data[9] =3D 0x00; + packet->data[10] =3D 0x00; + packet->data[11] =3D 0x00; + packet->len =3D 12; + packet->pending =3D 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 =3D=3D XTYPE_XBOX360) { - /* - * Some third-party controllers Xbox 360-style controllers - * require this message to finish initialization. - */ - u8 dummy[20]; - error =3D 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 =3D 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); + } } =20 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; =20 input_dev =3D input_allocate_device(); @@ -1962,11 +2123,29 @@ static int xpad_init_input(struct usb_xpad *xpad) xpad->dev =3D input_dev; input_dev->name =3D xpad->name; input_dev->phys =3D xpad->phys; + xpad->capabilities.subType =3D 1; + xpad->capabilities.type =3D 1; + xpad->capabilities.gamepad.flags =3D 0; + xpad->capabilities.gamepad.buttons =3D 0xFFFF; + xpad->capabilities.gamepad.leftTrigger =3D 0xFF; + xpad->capabilities.gamepad.rightTrigger =3D 0xFF; + xpad->capabilities.gamepad.leftThumbX =3D 0xFFC0; + xpad->capabilities.gamepad.leftThumbY =3D 0xFFC0; + xpad->capabilities.gamepad.rightThumbX =3D 0xFFC0; + xpad->capabilities.gamepad.rightThumbY =3D 0xFFC0; + xpad->dev->dev.groups =3D xpad_groups; usb_to_input_id(xpad->udev, &input_dev->id); =20 if (xpad->xtype =3D=3D XTYPE_XBOX360W) { /* x360w controllers and the receiver have different ids */ input_dev->id.product =3D 0x02a1; + xpad->capabilities.gamepad.flags =3D FLAG_WIRELESS; + } + + if (xpad->xtype =3D=3D XTYPE_XBOX360 && + usb_get_extra_descriptor(xpad->intf->cur_altsetting, 0x21, &input_des= c) =3D=3D 0) { + xpad->capabilities.subType =3D input_desc->subType; + xpad->capabilities.type =3D input_desc->type; } =20 input_dev->dev.parent =3D &xpad->intf->dev; --=20 2.53.0