Updated with a better understanding of the Xbox One controller protocol
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
old mode 100644
new mode 100755
diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c
index 3f89ac6..90b60e4 100644
--- a/src/joystick/hidapi/SDL_hidapi_xboxone.c
+++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c
@@ -47,31 +47,26 @@
static const Uint8 xboxone_init0[] = {
0x04, 0x20, 0x00, 0x00
};
-/* Initial ack */
-static const Uint8 xboxone_init1[] = {
- 0x01, 0x20, 0x01, 0x09, 0x00, 0x04, 0x20, 0x3a,
- 0x00, 0x00, 0x00, 0x80, 0x00
-};
/* Start controller - extended? */
-static const Uint8 xboxone_init2[] = {
+static const Uint8 xboxone_init1[] = {
0x05, 0x20, 0x00, 0x0F, 0x06, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x55, 0x53, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00
};
/* Start controller with input */
-static const Uint8 xboxone_init3[] = {
+static const Uint8 xboxone_init2[] = {
0x05, 0x20, 0x03, 0x01, 0x00
};
/* Enable LED */
-static const Uint8 xboxone_init4[] = {
+static const Uint8 xboxone_init3[] = {
0x0A, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
};
/* Start input reports? */
-static const Uint8 xboxone_init5[] = {
+static const Uint8 xboxone_init4[] = {
0x06, 0x20, 0x00, 0x02, 0x01, 0x00
};
/* Start rumble? */
-static const Uint8 xboxone_init6[] = {
+static const Uint8 xboxone_init5[] = {
0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
0x00, 0x00, 0xFF, 0x00, 0xEB
};
@@ -94,17 +89,16 @@ typedef struct {
static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = {
- { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init0, sizeof(xboxone_init0), { 0x04, 0xf0 } },
- { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init1, sizeof(xboxone_init1), { 0x04, 0xb0 } },
+ { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init0, sizeof(xboxone_init0), { 0x04, 0xb0 } },
+ { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init1, sizeof(xboxone_init1), { 0x00, 0x00 } },
{ 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init2, sizeof(xboxone_init2), { 0x00, 0x00 } },
{ 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init3, sizeof(xboxone_init3), { 0x00, 0x00 } },
- { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init4, sizeof(xboxone_init4), { 0x00, 0x00 } },
/* These next packets are required for third party controllers (PowerA, PDP, HORI),
but aren't the correct protocol for Microsoft Xbox controllers.
*/
+ { 0x0000, 0x0000, 0x045e, 0x0000, xboxone_init4, sizeof(xboxone_init4), { 0x00, 0x00 } },
{ 0x0000, 0x0000, 0x045e, 0x0000, xboxone_init5, sizeof(xboxone_init5), { 0x00, 0x00 } },
- { 0x0000, 0x0000, 0x045e, 0x0000, xboxone_init6, sizeof(xboxone_init6), { 0x00, 0x00 } },
};
typedef enum {
@@ -187,6 +181,18 @@ ControllerSendsWaitingForInit(Uint16 vendor_id, Uint16 product_id)
return SDL_FALSE;
}
+static void
+SendAckIfNeeded(SDL_HIDAPI_Device *device, Uint8 *data, int size)
+{
+ if ((data[1] & 0x30) == 0x30) {
+ Uint8 ack_packet[] = { 0x01, 0x20, data[2], 0x09, 0x00, data[0], 0x20, data[3], 0x00, 0x00, 0x00, 0x00, 0x00 };
+#ifdef DEBUG_XBOX_PROTOCOL
+ HIDAPI_DumpPacket("Xbox One sending ACK packet: size = %d", ack_packet, sizeof(ack_packet));
+#endif
+ hid_write(device->dev, ack_packet, sizeof(ack_packet));
+ }
+}
+
static SDL_bool
SendControllerInit(SDL_HIDAPI_Device *device, SDL_DriverXboxOne_Context *ctx)
{
@@ -218,6 +224,9 @@ SendControllerInit(SDL_HIDAPI_Device *device, SDL_DriverXboxOne_Context *ctx)
if (init_packet[0] != 0x01) {
init_packet[2] = ctx->sequence++;
}
+#ifdef DEBUG_XBOX_PROTOCOL
+ HIDAPI_DumpPacket("Xbox One sending INIT packet: size = %d", init_packet, packet->size);
+#endif
if (hid_write(device->dev, init_packet, packet->size) != packet->size) {
SDL_SetError("Couldn't write Xbox One initialization packet");
return SDL_FALSE;
@@ -239,6 +248,7 @@ SendControllerInit(SDL_HIDAPI_Device *device, SDL_DriverXboxOne_Context *ctx)
if (size >= 2 && data[0] == packet->response[0] && data[1] == packet->response[1]) {
got_response = SDL_TRUE;
}
+ SendAckIfNeeded(device, data, size);
}
}
#ifdef DEBUG_XBOX_PROTOCOL
@@ -443,8 +453,16 @@ HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev,
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[5] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
}
- if (ctx->has_share_button && ctx->last_state[18] != data[18]) {
- SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MISC1, (data[18] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ if (ctx->has_share_button) {
+ /* Version 1 of the firmware for Xbox One Series X */
+ if (ctx->last_state[18] != data[18]) {
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MISC1, (data[18] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ }
+
+ /* Version 2 of the firmware for Xbox One Series X */
+ if (ctx->last_state[22] != data[22]) {
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MISC1, (data[22] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ }
}
/* Xbox One S report is 18 bytes
@@ -542,13 +560,6 @@ HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev,
static void
HIDAPI_DriverXboxOne_HandleModePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
{
- if (data[1] == 0x30) {
- /* The Xbox One S controller needs acks for mode reports */
- const Uint8 seqnum = data[2];
- const Uint8 ack[] = { 0x01, 0x20, seqnum, 0x09, 0x00, 0x07, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
- hid_write(dev, ack, sizeof(ack));
- }
-
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[4] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
}
@@ -861,8 +872,30 @@ HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device)
}
} else {
switch (data[0]) {
+ case 0x01:
+ /* ACK packet */
+ /* The data bytes are:
+ 0x01 0x20 NN 0x09, where NN is the packet sequence
+ then 0x00
+ then a byte of the sequence being acked
+ then 0x20
+ then 16-bit LE value, the size of the previous packet payload when it's a single packet
+ then 4 bytes of unknown data, often all zero
+ */
+ break;
case 0x02:
/* Controller is connected and waiting for initialization */
+ /* The data bytes are:
+ 0x02 0x20 NN 0x1c, where NN is the packet sequence
+ then 6 bytes of wireless MAC address
+ then 2 bytes padding
+ then 16-bit VID
+ then 16-bit PID
+ then 16-bit firmware version quartet AA.BB.CC.DD
+ e.g. 0x05 0x00 0x05 0x00 0x51 0x0a 0x00 0x00
+ is firmware version 5.5.2641.0, and product version 0x0505 = 1285
+ then 8 bytes of unknown data
+ */
if (!ctx->initialized) {
#ifdef DEBUG_XBOX_PROTOCOL
SDL_Log("Delay after init: %ums\n", SDL_GetTicks() - ctx->start_time);
@@ -879,6 +912,26 @@ HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device)
case 0x03:
/* Controller heartbeat */
break;
+ case 0x04:
+ /* Unknown chatty controller information, sent by both sides */
+ break;
+ case 0x06:
+ /* Unknown chatty controller information, sent by both sides */
+ break;
+ case 0x07:
+ HIDAPI_DriverXboxOne_HandleModePacket(joystick, device->dev, ctx, data, size);
+ break;
+ case 0x1E:
+ /* If the packet starts with this:
+ 0x1E 0x30 0x07 0x10 0x04 0x00
+ then the next 14 bytes are the controller serial number
+ e.g. 0x30 0x39 0x37 0x31 0x32 0x33 0x33 0x32 0x33 0x35 0x34 0x30 0x33 0x36
+ is serial number "3039373132333332333534303336"
+
+ The controller sends that in response to this request:
+ 0x1E 0x30 0x07 0x01 0x04
+ */
+ break;
case 0x20:
if (!ctx->input_ready) {
if (!SDL_TICKS_PASSED(SDL_GetTicks(), ctx->initialized_time + CONTROLLER_INPUT_DELAY_MS)) {
@@ -891,15 +944,13 @@ HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device)
}
HIDAPI_DriverXboxOne_HandleStatePacket(joystick, device->dev, ctx, data, size);
break;
- case 0x07:
- HIDAPI_DriverXboxOne_HandleModePacket(joystick, device->dev, ctx, data, size);
- break;
default:
#ifdef DEBUG_JOYSTICK
SDL_Log("Unknown Xbox One packet: 0x%.2x\n", data[0]);
#endif
break;
}
+ SendAckIfNeeded(device, data, size);
}
}