summaryrefslogtreecommitdiffstats
path: root/drivers/input/joystick/xpad.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/joystick/xpad.c')
-rw-r--r--drivers/input/joystick/xpad.c455
1 files changed, 342 insertions, 113 deletions
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 18190b529bca..2959d80f7fdb 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -61,6 +61,7 @@
* Later changes can be tracked in SCM.
*/
+#include <linux/bits.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/rcupdate.h>
@@ -80,6 +81,9 @@
#define MAP_TRIGGERS_TO_BUTTONS (1 << 1)
#define MAP_STICKS_TO_NULL (1 << 2)
#define MAP_SELECT_BUTTON (1 << 3)
+#define MAP_PADDLES (1 << 4)
+#define MAP_PROFILE_BUTTON (1 << 5)
+
#define DANCEPAD_MAP_CONFIG (MAP_DPAD_TO_BUTTONS | \
MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL)
@@ -89,6 +93,17 @@
#define XTYPE_XBOXONE 3
#define XTYPE_UNKNOWN 4
+/* Send power-off packet to xpad360w after holding the mode button for this many
+ * seconds
+ */
+#define XPAD360W_POWEROFF_TIMEOUT 5
+
+#define PKT_XB 0
+#define PKT_XBE1 1
+#define PKT_XBE2_FW_OLD 2
+#define PKT_XBE2_FW_5_EARLY 3
+#define PKT_XBE2_FW_5_11 4
+
static bool dpad_to_buttons;
module_param(dpad_to_buttons, bool, S_IRUGO);
MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads");
@@ -111,8 +126,11 @@ static const struct xpad_device {
char *name;
u8 mapping;
u8 xtype;
+ u8 packet_type;
} xpad_device[] = {
{ 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 },
+ { 0x03eb, 0xff01, "Wooting One (Legacy)", 0, XTYPE_XBOX360 },
+ { 0x03eb, 0xff02, "Wooting Two (Legacy)", 0, XTYPE_XBOX360 },
{ 0x044f, 0x0f00, "Thrustmaster Wheel", 0, XTYPE_XBOX },
{ 0x044f, 0x0f03, "Thrustmaster Wheel", 0, XTYPE_XBOX },
{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },
@@ -128,9 +146,11 @@ static const struct xpad_device {
{ 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
{ 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE },
{ 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE },
- { 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", 0, XTYPE_XBOXONE },
+ { 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", MAP_PADDLES, XTYPE_XBOXONE },
+ { 0x045e, 0x0b00, "Microsoft X-Box One Elite 2 pad", MAP_PADDLES, XTYPE_XBOXONE },
{ 0x045e, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE },
{ 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
+ { 0x045e, 0x0b0a, "Microsoft X-Box Adaptive Controller", MAP_PROFILE_BUTTON, XTYPE_XBOXONE },
{ 0x045e, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE },
{ 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 },
{ 0x046d, 0xc21e, "Logitech Gamepad F510", 0, XTYPE_XBOX360 },
@@ -244,6 +264,7 @@ static const struct xpad_device {
{ 0x0f0d, 0x0063, "Hori Real Arcade Pro Hayabusa (USA) Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
{ 0x0f0d, 0x0067, "HORIPAD ONE", 0, XTYPE_XBOXONE },
{ 0x0f0d, 0x0078, "Hori Real Arcade Pro V Kai Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
+ { 0x0f0d, 0x00c5, "Hori Fighting Commander ONE", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
{ 0x0f30, 0x010b, "Philips Recoil", 0, XTYPE_XBOX },
{ 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX },
{ 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX },
@@ -260,6 +281,7 @@ static const struct xpad_device {
{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
{ 0x1430, 0xf801, "RedOctane Controller", 0, XTYPE_XBOX360 },
{ 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 },
+ { 0x146b, 0x0604, "Bigben Interactive DAIJA Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 },
{ 0x1532, 0x0a00, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
{ 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE },
@@ -325,6 +347,7 @@ static const struct xpad_device {
{ 0x24c6, 0x5502, "Hori Fighting Stick VX Alt", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x5510, "Hori Fighting Commander ONE (Xbox 360/PC Mode)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 },
{ 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x551a, "PowerA FUSION Pro Controller", 0, XTYPE_XBOXONE },
@@ -334,6 +357,14 @@ static const struct xpad_device {
{ 0x24c6, 0x5b03, "Thrustmaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 },
{ 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 },
{ 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
+ { 0x2563, 0x058d, "OneXPlayer Gamepad", 0, XTYPE_XBOX360 },
+ { 0x2dc8, 0x2000, "8BitDo Pro 2 Wired Controller fox Xbox", 0, XTYPE_XBOXONE },
+ { 0x31e3, 0x1100, "Wooting One", 0, XTYPE_XBOX360 },
+ { 0x31e3, 0x1200, "Wooting Two", 0, XTYPE_XBOX360 },
+ { 0x31e3, 0x1210, "Wooting Lekker", 0, XTYPE_XBOX360 },
+ { 0x31e3, 0x1220, "Wooting Two HE", 0, XTYPE_XBOX360 },
+ { 0x31e3, 0x1300, "Wooting 60HE (AVR)", 0, XTYPE_XBOX360 },
+ { 0x31e3, 0x1310, "Wooting 60HE (ARM)", 0, XTYPE_XBOX360 },
{ 0x3285, 0x0607, "Nacon GC-100", 0, XTYPE_XBOX360 },
{ 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX },
{ 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX },
@@ -390,6 +421,13 @@ static const signed short xpad_abs_triggers[] = {
-1
};
+/* used when the controller has extra paddle buttons */
+static const signed short xpad_btn_paddles[] = {
+ BTN_TRIGGER_HAPPY5, BTN_TRIGGER_HAPPY6, /* paddle upper right, lower right */
+ BTN_TRIGGER_HAPPY7, BTN_TRIGGER_HAPPY8, /* paddle upper left, lower left */
+ -1 /* terminating entry */
+};
+
/*
* Xbox 360 has a vendor-specific class, so we cannot match it with only
* USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we
@@ -419,6 +457,7 @@ static const signed short xpad_abs_triggers[] = {
static const struct usb_device_id xpad_table[] = {
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 Controller */
+ XPAD_XBOX360_VENDOR(0x03eb), /* Wooting Keyboards (Legacy) */
XPAD_XBOX360_VENDOR(0x044f), /* Thrustmaster X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */
XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */
@@ -429,6 +468,7 @@ static const struct usb_device_id xpad_table[] = {
{ USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */
XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */
XPAD_XBOX360_VENDOR(0x07ff), /* Mad Catz GamePad */
+ XPAD_XBOX360_VENDOR(0x0c12), /* Zeroplus X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */
XPAD_XBOXONE_VENDOR(0x0e6f), /* 0x0e6f X-Box One controllers */
XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */
@@ -450,8 +490,12 @@ static const struct usb_device_id xpad_table[] = {
XPAD_XBOXONE_VENDOR(0x20d6), /* PowerA Controllers */
XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */
XPAD_XBOXONE_VENDOR(0x24c6), /* PowerA Controllers */
+ XPAD_XBOX360_VENDOR(0x2563), /* OneXPlayer Gamepad */
+ XPAD_XBOX360_VENDOR(0x260d), /* Dareu H101 */
+ XPAD_XBOXONE_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller for Xbox */
XPAD_XBOXONE_VENDOR(0x2e24), /* Hyperkin Duke X-Box One pad */
XPAD_XBOX360_VENDOR(0x2f24), /* GameSir Controllers */
+ XPAD_XBOX360_VENDOR(0x31e3), /* Wooting Keyboards */
XPAD_XBOX360_VENDOR(0x3285), /* Nacon GC-100 */
{ }
};
@@ -473,13 +517,52 @@ struct xboxone_init_packet {
.len = ARRAY_SIZE(_data), \
}
+/*
+ * starting with xbox one, the game input protocol is used
+ * magic numbers are taken from
+ * - https://github.com/xpadneo/gip-dissector/blob/main/src/gip-dissector.lua
+ * - https://github.com/medusalix/xone/blob/master/bus/protocol.c
+ */
+#define GIP_CMD_ACK 0x01
+#define GIP_CMD_IDENTIFY 0x04
+#define GIP_CMD_POWER 0x05
+#define GIP_CMD_AUTHENTICATE 0x06
+#define GIP_CMD_VIRTUAL_KEY 0x07
+#define GIP_CMD_RUMBLE 0x09
+#define GIP_CMD_LED 0x0a
+#define GIP_CMD_FIRMWARE 0x0c
+#define GIP_CMD_INPUT 0x20
+
+#define GIP_SEQ0 0x00
+
+#define GIP_OPT_ACK 0x10
+#define GIP_OPT_INTERNAL 0x20
+
+/*
+ * length of the command payload encoded with
+ * https://en.wikipedia.org/wiki/LEB128
+ * which is a no-op for N < 128
+ */
+#define GIP_PL_LEN(N) (N)
+
+/*
+ * payload specific defines
+ */
+#define GIP_PWR_ON 0x00
+#define GIP_LED_ON 0x01
+
+#define GIP_MOTOR_R BIT(0)
+#define GIP_MOTOR_L BIT(1)
+#define GIP_MOTOR_RT BIT(2)
+#define GIP_MOTOR_LT BIT(3)
+#define GIP_MOTOR_ALL (GIP_MOTOR_R | GIP_MOTOR_L | GIP_MOTOR_RT | GIP_MOTOR_LT)
/*
* This packet is required for all Xbox One pads with 2015
* or later firmware installed (or present from the factory).
*/
-static const u8 xboxone_fw2015_init[] = {
- 0x05, 0x20, 0x00, 0x01, 0x00
+static const u8 xboxone_power_on[] = {
+ GIP_CMD_POWER, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(1), GIP_PWR_ON
};
/*
@@ -489,7 +572,16 @@ static const u8 xboxone_fw2015_init[] = {
* Bluetooth mode.
*/
static const u8 xboxone_s_init[] = {
- 0x05, 0x20, 0x00, 0x0f, 0x06
+ GIP_CMD_POWER, GIP_OPT_INTERNAL, GIP_SEQ0, 0x0f, 0x06
+};
+
+/*
+ * This packet is required to get additional input data
+ * from Xbox One Elite Series 2 (0x045e:0x0b00) pads.
+ * We mostly do this right now to get paddle data
+ */
+static const u8 extra_input_packet_init[] = {
+ 0x4d, 0x10, 0x01, 0x02, 0x07, 0x00
};
/*
@@ -497,9 +589,9 @@ static const u8 xboxone_s_init[] = {
* (0x0e6f:0x0165) to finish initialization and for Hori pads
* (0x0f0d:0x0067) to make the analog sticks work.
*/
-static const u8 xboxone_hori_init[] = {
- 0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a,
- 0x00, 0x00, 0x00, 0x80, 0x00
+static const u8 xboxone_hori_ack_id[] = {
+ GIP_CMD_ACK, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(9),
+ 0x00, GIP_CMD_IDENTIFY, GIP_OPT_INTERNAL, 0x3a, 0x00, 0x00, 0x00, 0x80, 0x00
};
/*
@@ -507,8 +599,8 @@ static const u8 xboxone_hori_init[] = {
* sending input reports. These pads include: (0x0e6f:0x02ab),
* (0x0e6f:0x02a4), (0x0e6f:0x02a6).
*/
-static const u8 xboxone_pdp_init1[] = {
- 0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
+static const u8 xboxone_pdp_led_on[] = {
+ GIP_CMD_LED, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(3), 0x00, GIP_LED_ON, 0x14
};
/*
@@ -516,8 +608,8 @@ static const u8 xboxone_pdp_init1[] = {
* sending input reports. These pads include: (0x0e6f:0x02ab),
* (0x0e6f:0x02a4), (0x0e6f:0x02a6).
*/
-static const u8 xboxone_pdp_init2[] = {
- 0x06, 0x20, 0x00, 0x02, 0x01, 0x00
+static const u8 xboxone_pdp_auth[] = {
+ GIP_CMD_AUTHENTICATE, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(2), 0x01, 0x00
};
/*
@@ -525,8 +617,8 @@ static const u8 xboxone_pdp_init2[] = {
* sending input reports. One of those pads is (0x24c6:0x543a).
*/
static const u8 xboxone_rumblebegin_init[] = {
- 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
- 0x1D, 0x1D, 0xFF, 0x00, 0x00
+ GIP_CMD_RUMBLE, 0x00, GIP_SEQ0, GIP_PL_LEN(9),
+ 0x00, GIP_MOTOR_ALL, 0x00, 0x00, 0x1D, 0x1D, 0xFF, 0x00, 0x00
};
/*
@@ -536,8 +628,8 @@ static const u8 xboxone_rumblebegin_init[] = {
* spin up to enough speed to actually vibrate the gamepad.
*/
static const u8 xboxone_rumbleend_init[] = {
- 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00
+ GIP_CMD_RUMBLE, 0x00, GIP_SEQ0, GIP_PL_LEN(9),
+ 0x00, GIP_MOTOR_ALL, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/*
@@ -547,13 +639,14 @@ static const u8 xboxone_rumbleend_init[] = {
* packet is going to be sent.
*/
static const struct xboxone_init_packet xboxone_init_packets[] = {
- XBOXONE_INIT_PKT(0x0e6f, 0x0165, xboxone_hori_init),
- XBOXONE_INIT_PKT(0x0f0d, 0x0067, xboxone_hori_init),
- XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_fw2015_init),
+ XBOXONE_INIT_PKT(0x0e6f, 0x0165, xboxone_hori_ack_id),
+ XBOXONE_INIT_PKT(0x0f0d, 0x0067, xboxone_hori_ack_id),
+ XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_power_on),
XBOXONE_INIT_PKT(0x045e, 0x02ea, xboxone_s_init),
XBOXONE_INIT_PKT(0x045e, 0x0b00, xboxone_s_init),
- XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init1),
- XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init2),
+ XBOXONE_INIT_PKT(0x045e, 0x0b00, extra_input_packet_init),
+ XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_led_on),
+ XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_auth),
XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumblebegin_init),
XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init),
XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumblebegin_init),
@@ -608,14 +701,17 @@ struct usb_xpad {
int mapping; /* map d-pad to buttons or to axes */
int xtype; /* type of xbox device */
+ int packet_type; /* type of the extended packet */
int pad_nr; /* the order x360 pads were attached */
const char *name; /* name of the device */
struct work_struct work; /* init/remove device from callback */
+ time64_t mode_btn_down_ts;
};
static int xpad_init_input(struct usb_xpad *xpad);
static void xpad_deinit_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);
/*
* xpad_process_packet
@@ -656,10 +752,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
/* digital pad */
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
/* dpad as buttons (left, right, up, down) */
- input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04);
- input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08);
- input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01);
- input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);
+ input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & BIT(2));
+ input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & BIT(3));
+ input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & BIT(0));
+ input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & BIT(1));
} else {
input_report_abs(dev, ABS_HAT0X,
!!(data[2] & 0x08) - !!(data[2] & 0x04));
@@ -668,10 +764,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
}
/* start/back buttons and stick press left/right */
- input_report_key(dev, BTN_START, data[2] & 0x10);
- input_report_key(dev, BTN_SELECT, data[2] & 0x20);
- input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
- input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
+ input_report_key(dev, BTN_START, data[2] & BIT(4));
+ input_report_key(dev, BTN_SELECT, data[2] & BIT(5));
+ input_report_key(dev, BTN_THUMBL, data[2] & BIT(6));
+ input_report_key(dev, BTN_THUMBR, data[2] & BIT(7));
/* "analog" buttons A, B, X, Y */
input_report_key(dev, BTN_A, data[4]);
@@ -683,6 +779,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
input_report_key(dev, BTN_C, data[8]);
input_report_key(dev, BTN_Z, data[9]);
+ /* Profile button has a value of 0-3, so it is reported as an axis */
+ if (xpad->mapping & MAP_PROFILE_BUTTON)
+ input_report_abs(dev, ABS_PROFILE, data[34]);
+
input_sync(dev);
}
@@ -706,10 +806,10 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
/* digital pad */
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
/* dpad as buttons (left, right, up, down) */
- input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04);
- input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08);
- input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01);
- input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);
+ input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & BIT(2));
+ input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & BIT(3));
+ input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & BIT(0));
+ input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & BIT(1));
}
/*
@@ -727,21 +827,21 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
}
/* start/back buttons */
- input_report_key(dev, BTN_START, data[2] & 0x10);
- input_report_key(dev, BTN_SELECT, data[2] & 0x20);
+ input_report_key(dev, BTN_START, data[2] & BIT(4));
+ input_report_key(dev, BTN_SELECT, data[2] & BIT(5));
/* stick press left/right */
- input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
- input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
+ input_report_key(dev, BTN_THUMBL, data[2] & BIT(6));
+ input_report_key(dev, BTN_THUMBR, data[2] & BIT(7));
/* buttons A,B,X,Y,TL,TR and MODE */
- input_report_key(dev, BTN_A, data[3] & 0x10);
- input_report_key(dev, BTN_B, data[3] & 0x20);
- input_report_key(dev, BTN_X, data[3] & 0x40);
- input_report_key(dev, BTN_Y, data[3] & 0x80);
- input_report_key(dev, BTN_TL, data[3] & 0x01);
- input_report_key(dev, BTN_TR, data[3] & 0x02);
- input_report_key(dev, BTN_MODE, data[3] & 0x04);
+ input_report_key(dev, BTN_A, data[3] & BIT(4));
+ input_report_key(dev, BTN_B, data[3] & BIT(5));
+ input_report_key(dev, BTN_X, data[3] & BIT(6));
+ input_report_key(dev, BTN_Y, data[3] & BIT(7));
+ input_report_key(dev, BTN_TL, data[3] & BIT(0));
+ input_report_key(dev, BTN_TR, data[3] & BIT(1));
+ input_report_key(dev, BTN_MODE, data[3] & BIT(2));
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
/* left stick */
@@ -767,6 +867,23 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
}
input_sync(dev);
+
+ /* XBOX360W controllers can't be turned off without driver assistance */
+ if (xpad->xtype == XTYPE_XBOX360W) {
+ if (xpad->mode_btn_down_ts > 0 && xpad->pad_present &&
+ ((ktime_get_seconds() - xpad->mode_btn_down_ts) >=
+ XPAD360W_POWEROFF_TIMEOUT)) {
+ xpad360w_poweroff_controller(xpad);
+ xpad->mode_btn_down_ts = 0;
+ return;
+ }
+
+ /* mode button down/up */
+ if (data[3] & BIT(2))
+ xpad->mode_btn_down_ts = ktime_get_seconds();
+ else
+ xpad->mode_btn_down_ts = 0;
+ }
}
static void xpad_presence_work(struct work_struct *work)
@@ -846,87 +963,154 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
{
struct input_dev *dev = xpad->dev;
+ bool do_sync = false;
/* the xbox button has its own special report */
- if (data[0] == 0X07) {
+ if (data[0] == GIP_CMD_VIRTUAL_KEY) {
/*
* The Xbox One S controller requires these reports to be
* acked otherwise it continues sending them forever and
* won't report further mode button events.
*/
- if (data[1] == 0x30)
+ if (data[1] == (GIP_OPT_ACK | GIP_OPT_INTERNAL))
xpadone_ack_mode_report(xpad, data[2]);
- input_report_key(dev, BTN_MODE, data[4] & 0x01);
+ input_report_key(dev, BTN_MODE, data[4] & GENMASK(1, 0));
input_sync(dev);
- return;
- }
- /* check invalid packet */
- else if (data[0] != 0X20)
- return;
- /* menu/view buttons */
- input_report_key(dev, BTN_START, data[4] & 0x04);
- input_report_key(dev, BTN_SELECT, data[4] & 0x08);
- if (xpad->mapping & MAP_SELECT_BUTTON)
- input_report_key(dev, KEY_RECORD, data[22] & 0x01);
+ do_sync = true;
+ } else if (data[0] == GIP_CMD_FIRMWARE) {
+ /* Some packet formats force us to use this separate to poll paddle inputs */
+ if (xpad->packet_type == PKT_XBE2_FW_5_11) {
+ /* Mute paddles if controller is in a custom profile slot
+ * Checked by looking at the active profile slot to
+ * verify it's the default slot
+ */
+ if (data[19] != 0)
+ data[18] = 0;
- /* buttons A,B,X,Y */
- input_report_key(dev, BTN_A, data[4] & 0x10);
- input_report_key(dev, BTN_B, data[4] & 0x20);
- input_report_key(dev, BTN_X, data[4] & 0x40);
- input_report_key(dev, BTN_Y, data[4] & 0x80);
+ /* Elite Series 2 split packet paddle bits */
+ input_report_key(dev, BTN_TRIGGER_HAPPY5, data[18] & BIT(0));
+ input_report_key(dev, BTN_TRIGGER_HAPPY6, data[18] & BIT(1));
+ input_report_key(dev, BTN_TRIGGER_HAPPY7, data[18] & BIT(2));
+ input_report_key(dev, BTN_TRIGGER_HAPPY8, data[18] & BIT(3));
- /* digital pad */
- if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
- /* dpad as buttons (left, right, up, down) */
- input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & 0x04);
- input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & 0x08);
- input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & 0x01);
- input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & 0x02);
- } else {
- input_report_abs(dev, ABS_HAT0X,
- !!(data[5] & 0x08) - !!(data[5] & 0x04));
- input_report_abs(dev, ABS_HAT0Y,
- !!(data[5] & 0x02) - !!(data[5] & 0x01));
- }
-
- /* TL/TR */
- input_report_key(dev, BTN_TL, data[5] & 0x10);
- input_report_key(dev, BTN_TR, data[5] & 0x20);
+ do_sync = true;
+ }
+ } else if (data[0] == GIP_CMD_INPUT) { /* The main valid packet type for inputs */
+ /* menu/view buttons */
+ input_report_key(dev, BTN_START, data[4] & BIT(2));
+ input_report_key(dev, BTN_SELECT, data[4] & BIT(3));
+ if (xpad->mapping & MAP_SELECT_BUTTON)
+ input_report_key(dev, KEY_RECORD, data[22] & BIT(0));
+
+ /* buttons A,B,X,Y */
+ input_report_key(dev, BTN_A, data[4] & BIT(4));
+ input_report_key(dev, BTN_B, data[4] & BIT(5));
+ input_report_key(dev, BTN_X, data[4] & BIT(6));
+ input_report_key(dev, BTN_Y, data[4] & BIT(7));
+
+ /* digital pad */
+ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
+ /* dpad as buttons (left, right, up, down) */
+ input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & BIT(2));
+ input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & BIT(3));
+ input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & BIT(0));
+ input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & BIT(1));
+ } else {
+ input_report_abs(dev, ABS_HAT0X,
+ !!(data[5] & 0x08) - !!(data[5] & 0x04));
+ input_report_abs(dev, ABS_HAT0Y,
+ !!(data[5] & 0x02) - !!(data[5] & 0x01));
+ }
- /* stick press left/right */
- input_report_key(dev, BTN_THUMBL, data[5] & 0x40);
- input_report_key(dev, BTN_THUMBR, data[5] & 0x80);
+ /* TL/TR */
+ input_report_key(dev, BTN_TL, data[5] & BIT(4));
+ input_report_key(dev, BTN_TR, data[5] & BIT(5));
+
+ /* stick press left/right */
+ input_report_key(dev, BTN_THUMBL, data[5] & BIT(6));
+ input_report_key(dev, BTN_THUMBR, data[5] & BIT(7));
+
+ if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
+ /* left stick */
+ input_report_abs(dev, ABS_X,
+ (__s16) le16_to_cpup((__le16 *)(data + 10)));
+ input_report_abs(dev, ABS_Y,
+ ~(__s16) le16_to_cpup((__le16 *)(data + 12)));
+
+ /* right stick */
+ input_report_abs(dev, ABS_RX,
+ (__s16) le16_to_cpup((__le16 *)(data + 14)));
+ input_report_abs(dev, ABS_RY,
+ ~(__s16) le16_to_cpup((__le16 *)(data + 16)));
+ }
- if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
- /* left stick */
- input_report_abs(dev, ABS_X,
- (__s16) le16_to_cpup((__le16 *)(data + 10)));
- input_report_abs(dev, ABS_Y,
- ~(__s16) le16_to_cpup((__le16 *)(data + 12)));
+ /* triggers left/right */
+ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
+ input_report_key(dev, BTN_TL2,
+ (__u16) le16_to_cpup((__le16 *)(data + 6)));
+ input_report_key(dev, BTN_TR2,
+ (__u16) le16_to_cpup((__le16 *)(data + 8)));
+ } else {
+ input_report_abs(dev, ABS_Z,
+ (__u16) le16_to_cpup((__le16 *)(data + 6)));
+ input_report_abs(dev, ABS_RZ,
+ (__u16) le16_to_cpup((__le16 *)(data + 8)));
+ }
- /* right stick */
- input_report_abs(dev, ABS_RX,
- (__s16) le16_to_cpup((__le16 *)(data + 14)));
- input_report_abs(dev, ABS_RY,
- ~(__s16) le16_to_cpup((__le16 *)(data + 16)));
- }
+ /* paddle handling */
+ /* based on SDL's SDL_hidapi_xboxone.c */
+ if (xpad->mapping & MAP_PADDLES) {
+ if (xpad->packet_type == PKT_XBE1) {
+ /* Mute paddles if controller has a custom mapping applied.
+ * Checked by comparing the current mapping
+ * config against the factory mapping config
+ */
+ if (memcmp(&data[4], &data[18], 2) != 0)
+ data[32] = 0;
+
+ /* OG Elite Series Controller paddle bits */
+ input_report_key(dev, BTN_TRIGGER_HAPPY5, data[32] & BIT(1));
+ input_report_key(dev, BTN_TRIGGER_HAPPY6, data[32] & BIT(3));
+ input_report_key(dev, BTN_TRIGGER_HAPPY7, data[32] & BIT(0));
+ input_report_key(dev, BTN_TRIGGER_HAPPY8, data[32] & BIT(2));
+ } else if (xpad->packet_type == PKT_XBE2_FW_OLD) {
+ /* Mute paddles if controller has a custom mapping applied.
+ * Checked by comparing the current mapping
+ * config against the factory mapping config
+ */
+ if (data[19] != 0)
+ data[18] = 0;
+
+ /* Elite Series 2 4.x firmware paddle bits */
+ input_report_key(dev, BTN_TRIGGER_HAPPY5, data[18] & BIT(0));
+ input_report_key(dev, BTN_TRIGGER_HAPPY6, data[18] & BIT(1));
+ input_report_key(dev, BTN_TRIGGER_HAPPY7, data[18] & BIT(2));
+ input_report_key(dev, BTN_TRIGGER_HAPPY8, data[18] & BIT(3));
+ } else if (xpad->packet_type == PKT_XBE2_FW_5_EARLY) {
+ /* Mute paddles if controller has a custom mapping applied.
+ * Checked by comparing the current mapping
+ * config against the factory mapping config
+ */
+ if (data[23] != 0)
+ data[22] = 0;
+
+ /* Elite Series 2 5.x firmware paddle bits
+ * (before the packet was split)
+ */
+ input_report_key(dev, BTN_TRIGGER_HAPPY5, data[22] & BIT(0));
+ input_report_key(dev, BTN_TRIGGER_HAPPY6, data[22] & BIT(1));
+ input_report_key(dev, BTN_TRIGGER_HAPPY7, data[22] & BIT(2));
+ input_report_key(dev, BTN_TRIGGER_HAPPY8, data[22] & BIT(3));
+ }
+ }
- /* triggers left/right */
- if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
- input_report_key(dev, BTN_TL2,
- (__u16) le16_to_cpup((__le16 *)(data + 6)));
- input_report_key(dev, BTN_TR2,
- (__u16) le16_to_cpup((__le16 *)(data + 8)));
- } else {
- input_report_abs(dev, ABS_Z,
- (__u16) le16_to_cpup((__le16 *)(data + 6)));
- input_report_abs(dev, ABS_RZ,
- (__u16) le16_to_cpup((__le16 *)(data + 8)));
+ do_sync = true;
}
- input_sync(dev);
+ if (do_sync)
+ input_sync(dev);
}
static void xpad_irq_in(struct urb *urb)
@@ -1226,8 +1410,8 @@ static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num)
struct xpad_output_packet *packet =
&xpad->out_packets[XPAD_OUT_CMD_IDX];
static const u8 mode_report_ack[] = {
- 0x01, 0x20, 0x00, 0x09, 0x00, 0x07, 0x20, 0x02,
- 0x00, 0x00, 0x00, 0x00, 0x00
+ GIP_CMD_ACK, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(9),
+ 0x00, GIP_CMD_VIRTUAL_KEY, GIP_OPT_INTERNAL, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00
};
spin_lock_irqsave(&xpad->odata_lock, flags);
@@ -1305,14 +1489,14 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect
break;
case XTYPE_XBOXONE:
- packet->data[0] = 0x09; /* activate rumble */
+ packet->data[0] = GIP_CMD_RUMBLE; /* activate rumble */
packet->data[1] = 0x00;
packet->data[2] = xpad->odata_serial++;
- packet->data[3] = 0x09;
+ packet->data[3] = GIP_PL_LEN(9);
packet->data[4] = 0x00;
- packet->data[5] = 0x0F;
- packet->data[6] = 0x00;
- packet->data[7] = 0x00;
+ packet->data[5] = GIP_MOTOR_ALL;
+ packet->data[6] = 0x00; /* left trigger */
+ packet->data[7] = 0x00; /* right trigger */
packet->data[8] = strong / 512; /* left actuator */
packet->data[9] = weak / 512; /* right actuator */
packet->data[10] = 0xFF; /* on period */
@@ -1622,6 +1806,9 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */
input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
break;
+ case ABS_PROFILE: /* 4 value profile button (such as on XAC) */
+ input_set_abs_params(input_dev, abs, 0, 4, 0, 0);
+ break;
default:
input_set_abs_params(input_dev, abs, 0, 0, 0, 0);
break;
@@ -1693,6 +1880,12 @@ static int xpad_init_input(struct usb_xpad *xpad)
xpad_btn_pad[i]);
}
+ /* set up paddles if the controller has them */
+ if (xpad->mapping & MAP_PADDLES) {
+ for (i = 0; xpad_btn_paddles[i] >= 0; i++)
+ input_set_capability(input_dev, EV_KEY, xpad_btn_paddles[i]);
+ }
+
/*
* This should be a simple else block. However historically
* xbox360w has mapped DPAD to buttons while xbox360 did not. This
@@ -1714,6 +1907,10 @@ static int xpad_init_input(struct usb_xpad *xpad)
xpad_set_up_abs(input_dev, xpad_abs_triggers[i]);
}
+ /* setup profile button as an axis with 4 possible values */
+ if (xpad->mapping & MAP_PROFILE_BUTTON)
+ xpad_set_up_abs(input_dev, ABS_PROFILE);
+
error = xpad_init_ff(xpad);
if (error)
goto err_free_input;
@@ -1779,6 +1976,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->mapping = xpad_device[i].mapping;
xpad->xtype = xpad_device[i].xtype;
xpad->name = xpad_device[i].name;
+ xpad->packet_type = PKT_XB;
INIT_WORK(&xpad->work, xpad_presence_work);
if (xpad->xtype == XTYPE_UNKNOWN) {
@@ -1844,6 +2042,38 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
usb_set_intfdata(intf, xpad);
+ /* Packet type detection */
+ if (le16_to_cpu(udev->descriptor.idVendor) == 0x045e) { /* Microsoft controllers */
+ if (le16_to_cpu(udev->descriptor.idProduct) == 0x02e3) {
+ /* The original elite controller always uses the oldest
+ * type of extended packet
+ */
+ xpad->packet_type = PKT_XBE1;
+ } else if (le16_to_cpu(udev->descriptor.idProduct) == 0x0b00) {
+ /* The elite 2 controller has seen multiple packet
+ * revisions. These are tied to specific firmware
+ * versions
+ */
+ if (le16_to_cpu(udev->descriptor.bcdDevice) < 0x0500) {
+ /* This is the format that the Elite 2 used
+ * prior to the BLE update
+ */
+ xpad->packet_type = PKT_XBE2_FW_OLD;
+ } else if (le16_to_cpu(udev->descriptor.bcdDevice) <
+ 0x050b) {
+ /* This is the format that the Elite 2 used
+ * prior to the update that split the packet
+ */
+ xpad->packet_type = PKT_XBE2_FW_5_EARLY;
+ } else {
+ /* The split packet format that was introduced
+ * in firmware v5.11
+ */
+ xpad->packet_type = PKT_XBE2_FW_5_11;
+ }
+ }
+ }
+
if (xpad->xtype == XTYPE_XBOX360W) {
/*
* Submit the int URB immediately rather than waiting for open
@@ -1972,7 +2202,6 @@ static struct usb_driver xpad_driver = {
.disconnect = xpad_disconnect,
.suspend = xpad_suspend,
.resume = xpad_resume,
- .reset_resume = xpad_resume,
.id_table = xpad_table,
};