From a7ff79e584df5a9f78a9431397faab18393fdafb Mon Sep 17 00:00:00 2001 From: gdsports Date: Sat, 3 Nov 2018 15:58:33 -1000 Subject: [PATCH] Add support for Xbox 360 USB controller --- USBHost_t36.h | 77 +++++++++++++- examples/Joystick/Joystick.ino | 1 + joystick.cpp | 187 ++++++++++++++++++++------------- keywords.txt | 1 + 4 files changed, 191 insertions(+), 75 deletions(-) diff --git a/USBHost_t36.h b/USBHost_t36.h index e5a139e..2ccac5f 100644 --- a/USBHost_t36.h +++ b/USBHost_t36.h @@ -833,7 +833,7 @@ public: // setLEDs on PS4(RGB), PS3 simple LED setting (only uses lr) bool setLEDs(uint8_t lr, uint8_t lg=0, uint8_t lb=0); // sets Leds, enum { STANDARD_AXIS_COUNT = 10, ADDITIONAL_AXIS_COUNT = 54, TOTAL_AXIS_COUNT = (STANDARD_AXIS_COUNT+ADDITIONAL_AXIS_COUNT) }; - typedef enum { UNKNOWN=0, PS3, PS4, XBOXONE, XBOX360} joytype_t; + typedef enum { UNKNOWN=0, PS3, PS4, XBOXONE, XBOX360, XBOX360USB} joytype_t; joytype_t joystickType = UNKNOWN; protected: // From USBDriver @@ -851,8 +851,83 @@ protected: private: // Class specific + /************************************************************/ + // Interrupt-based Data Movement + // XBox one input data when type == 0x20 + // Information came from several places on the web including: + // https://github.com/quantus/xbox-one-controller-protocol + /************************************************************/ + typedef struct { + uint8_t type; + uint8_t const_0; + uint16_t id; + // From online references button order: + // sync, dummy, start, back, a, b, x, y + // dpad up, down left, right + // lb, rb, left stick, right stick + // Axis: + // lt, rt, lx, ly, rx, ry + // + uint16_t buttons; + int16_t axis[6]; + } xbox1data20_t; + + typedef struct { + uint16_t buttons; + uint8_t lt; + uint8_t rt; + int16_t axis[4]; + } xbox360controls_t; + + typedef struct { + uint8_t state; + uint8_t id_or_type; + uint16_t controller_status; + uint16_t unknown; + // From online references button order: + // sync, dummy, start, back, a, b, x, y + // dpad up, down left, right + // lb, rb, left stick, right stick + // Axis: + // lt, rt, lx, ly, rx, ry + // + xbox360controls_t controls; + } xbox360data_t; + + /* Xbox 360 wired USB report + http://www.tattiebogle.net/index.php/ProjectRoot/Xbox360Controller/UsbInfo + + Reports are type 0x00, and seem to be 20 bytes long: + + 0014ttttxxyyaaaaaaaabbbbbbbb000000000000 + Where x is the left trigger, y is the right trigger, a is the left hat, b is the right hat and t is the buttons: + + 0x0001 Left shoulder + 0x0002 Right shoulder + 0x0004 XBox button + 0x0008 + 0x0010 A + 0x0020 B + 0x0040 X + 0x0080 Y + 0x0100 D-pad up + 0x0200 D-pad down + 0x0400 D-pad left + 0x0800 D-pad right + 0x1000 Start + 0x2000 Back + 0x4000 Left hat button + 0x8000 Right hat button + */ + typedef struct { + uint8_t rType; + uint8_t rLen; + xbox360controls_t controls; + } xbox360udata_t; + void init(); USBHIDParser *driver_ = nullptr; + void extract_xbox360controls(const xbox360controls_t *cont); joytype_t mapVIDPIDtoJoystickType(uint16_t idVendor, uint16_t idProduct, bool exclude_hid_devices); bool transmitPS4UserFeedbackMsg(); bool transmitPS3UserFeedbackMsg(); diff --git a/examples/Joystick/Joystick.ino b/examples/Joystick/Joystick.ino index 0eb0327..d4589b3 100644 --- a/examples/Joystick/Joystick.ino +++ b/examples/Joystick/Joystick.ino @@ -122,6 +122,7 @@ void loop() case JoystickController::XBOXONE: case JoystickController::XBOX360: + case JoystickController::XBOX360USB: ltv = joysticks[joystick_index].getAxis(4); rtv = joysticks[joystick_index].getAxis(5); if ((ltv != joystick_left_trigger_value[joystick_index]) || (rtv != joystick_right_trigger_value[joystick_index])) { diff --git a/joystick.cpp b/joystick.cpp index 747c6c6..1862704 100644 --- a/joystick.cpp +++ b/joystick.cpp @@ -33,6 +33,7 @@ JoystickController::product_vendor_mapping_t JoystickController::pid_vid_mapping[] = { { 0x045e, 0x02ea, XBOXONE, false },{ 0x045e, 0x02dd, XBOXONE, false }, { 0x045e, 0x0719, XBOX360, false}, + { 0x045e, 0x028e, XBOX360USB, false}, { 0x054C, 0x0268, PS3, true}, { 0x054C, 0x05C4, PS4, true}, {0x054C, 0x09CC, PS4, true } }; @@ -153,6 +154,19 @@ bool JoystickController::setRumble(uint8_t lValue, uint8_t rValue, uint8_t timeo println("XBox360 rumble transfer fail"); } return true; + case XBOX360USB: + txbuf_[0] = 0x00; + txbuf_[1] = 0x08; + txbuf_[2] = 0x00; + txbuf_[3] = lValue; + txbuf_[4] = rValue; + txbuf_[5] = 0x00; + txbuf_[6] = 0x00; + txbuf_[7] = 0x00; + if (!queue_Data_Transfer(txpipe_, txbuf_, 8, this)) { + println("XBox360USB rumble transfer fail"); + } + return true; } return false; } @@ -191,6 +205,45 @@ bool JoystickController::setLEDs(uint8_t lr, uint8_t lg, uint8_t lb) println("XBox360 set leds fail"); } return true; + case XBOX360USB: + /* http://www.tattiebogle.net/index.php/ProjectRoot/Xbox360Controller/UsbInfo + + Some control over the LEDs surrounding the XBox button is + provided, corresponding to the markings 1, 2, 3 and 4. This + is controlled using message type 0x01. + + To select a new pattern for the LEDs, send a message of the following form: + + 0103xx + Where xx is the desired pattern: + + 0x00 All off + 0x01 All blinking + 0x02 1 flashes, then on + 0x03 2 flashes, then on + 0x04 3 flashes, then on + 0x05 4 flashes, then on + 0x06 1 on + 0x07 2 on + 0x08 3 on + 0x09 4 on + 0x0A Rotating (e.g. 1-2-4-3) + 0x0B Blinking* + 0x0C Slow blinking* + 0x0D Alternating (e.g. 1+4-2+3), then back to previous* + * The previous setting will be used for these (all blinking, or 1, 2, 3 or 4 on). + + At startup, the device seems to report 01030E. I believe + this to indicate that there are 14 options (e.g. 0 to D hex) + for the LEDs. + */ + txbuf_[0] = 0x01; + txbuf_[1] = 0x03; + txbuf_[2] = lr; + if (!queue_Data_Transfer(txpipe_, txbuf_, 3, this)) { + println("XBox360USB set leds fail"); + } + return true; case XBOXONE: default: return false; @@ -417,11 +470,21 @@ bool JoystickController::claim(Device_t *dev, int type, const uint8_t *descripto // 29 30 1 2 3 4 5 6 7 8 9 40 41 42 // 07 05 81 03 20 00 01 07 05 01 03 20 00 08 + // XBOX 360 wired USB... Has 4 interfaces and extra descriptors + // Shows data for #1 only... + // Also they have some unknown data type we need to ignore between interface and end points. + // 0 1 2 3 4 5 6 7 8 *9 10 1 2 3 4 5 6 7 8 9 20 1 2 3 4 5 *6 7 8 + // 09 04 00 00 02 FF 5D 01 00 11 21 10 01 01 25 81 14 03 03 03 04 13 02 08 03 03 07 05 81 + + // 29 30 1 2 *3 4 5 6 7 8 9 40 41 42 + // 03 20 00 04 07 05 02 03 20 00 08 + if (len < 9+7+7) return false; // Some common stuff for both XBoxs uint32_t count_end_points = descriptors[4]; if (count_end_points < 2) return false; + if ((jtype == XBOX360USB) && (count_end_points != 2)) return false; if (descriptors[5] != 0xff) return false; // bInterfaceClass, 3 = HID rx_ep_ = 0; uint32_t txep = 0; @@ -434,6 +497,9 @@ bool JoystickController::claim(Device_t *dev, int type, const uint8_t *descripto if (descriptors[descriptor_index] != 0x14) return false; // only support specific versions... descriptor_index += descriptors[descriptor_index]; // XBox360w ignore this unknown setup... } + else if (descriptors[descriptor_index+1] == 0x21) { + descriptor_index += descriptors[descriptor_index]; // XBox360USB skip this unknown setup... + } while (count_end_points-- && ((rx_ep_ == 0) || txep == 0)) { if (descriptors[descriptor_index] != 7) return false; // length 7 if (descriptors[descriptor_index+1] != 5) return false; // ep desc @@ -474,7 +540,7 @@ bool JoystickController::claim(Device_t *dev, int type, const uint8_t *descripto if (jtype == XBOXONE) { queue_Data_Transfer(txpipe_, xboxone_start_input, sizeof(xboxone_start_input), this); connected_ = true; // remember that hardware is actually connected... - } else if (jtype == XBOX360) { + } else if ((jtype == XBOX360) || (jtype == XBOX360USB)) { queue_Data_Transfer(txpipe_, xbox360w_inquire_present, sizeof(xbox360w_inquire_present), this); connected_ = 0; // remember that hardware is actually connected... } @@ -506,47 +572,40 @@ void JoystickController::tx_callback(const Transfer_t *transfer) -/************************************************************/ -// Interrupt-based Data Movement -// XBox one input data when type == 0x20 -// Information came from several places on the web including: -// https://github.com/quantus/xbox-one-controller-protocol -/************************************************************/ -typedef struct { - uint8_t type; - uint8_t const_0; - uint16_t id; - // From online references button order: - // sync, dummy, start, back, a, b, x, y - // dpad up, down left, right - // lb, rb, left stick, right stick - // Axis: - // lt, rt, lx, ly, rx, ry - // - uint16_t buttons; - int16_t axis[6]; -} xbox1data20_t; - -typedef struct { - uint8_t state; - uint8_t id_or_type; - uint16_t controller_status; - uint16_t unknown; - // From online references button order: - // sync, dummy, start, back, a, b, x, y - // dpad up, down left, right - // lb, rb, left stick, right stick - // Axis: - // lt, rt, lx, ly, rx, ry - // - uint16_t buttons; - uint8_t lt; - uint8_t rt; - int16_t axis[4]; -} xbox360data_t; - static const uint8_t xbox_axis_order_mapping[] = {4, 5, 0, 1, 2, 3}; +void JoystickController::extract_xbox360controls(const xbox360controls_t *cont) +{ + if (buttons != cont->buttons) { + buttons = cont->buttons; + anychange = true; + } + axis_mask_ = 0x3f; + axis_changed_mask_ = 0; // assume none for now + + for (uint8_t i = 0; i < 4; i++) { + if (axis[i] != cont->axis[i]) { + axis[i] = cont->axis[i]; + axis_changed_mask_ |= (1 << i); + anychange = true; + } + } + // the two triggers show up as 4 and 5 + if (axis[4] != cont->lt) { + axis[4] = cont->lt; + axis_changed_mask_ |= (1 << 4); + anychange = true; + } + + if (axis[5] != cont->rt) { + axis[5] = cont->rt; + axis_changed_mask_ |= (1 << 5); + anychange = true; + } + + if (anychange) joystickEvent = true; +} + void JoystickController::rx_data(const Transfer_t *transfer) { print("JoystickController::rx_data: "); @@ -591,41 +650,21 @@ void JoystickController::rx_data(const Transfer_t *transfer) } } } else if((xb360d->id_or_type == 0x00) && (xb360d->controller_status & 0x1300)) { - // Controller status report - Maybe we should save away and allow the user access? - println("XBox360w - controllerStatus: ", xb360d->controller_status, HEX); - } else if(xb360d->id_or_type == 0x01) { // Lets only process report 1. + // Controller status report - Maybe we should save away and allow the user access? + println("XBox360w - controllerStatus: ", xb360d->controller_status, HEX); + } else if(xb360d->id_or_type == 0x01) { // Lets only process report 1. //const uint8_t *pbuffer = (uint8_t*)transfer->buffer; - //for (uint8_t i = 0; i < transfer->length; i++) Serial.printf("%02x ", pbuffer[i]); - //Serial.printf("\n"); - - if (buttons != xb360d->buttons) { - buttons = xb360d->buttons; - anychange = true; - } - axis_mask_ = 0x3f; - axis_changed_mask_ = 0; // assume none for now - - for (uint8_t i = 0; i < 4; i++) { - if (axis[i] != xb360d->axis[i]) { - axis[i] = xb360d->axis[i]; - axis_changed_mask_ |= (1 << i); - anychange = true; - } - } - // the two triggers show up as 4 and 5 - if (axis[4] != xb360d->lt) { - axis[4] = xb360d->lt; - axis_changed_mask_ |= (1 << 4); - anychange = true; - } - - if (axis[5] != xb360d->rt) { - axis[5] = xb360d->rt; - axis_changed_mask_ |= (1 << 5); - anychange = true; - } - - if (anychange) joystickEvent = true; + //for (uint8_t i = 0; i < transfer->length; i++) Serial.printf("%02x ", pbuffer[i]); + //Serial.printf("\n"); + extract_xbox360controls(&xb360d->controls); + } + } else if (joystickType == XBOX360USB) { + xbox360udata_t *xb360ud = (xbox360udata_t *)transfer->buffer; + if ((xb360ud->rType == 0) && (xb360ud->rLen == 0x14)) { + extract_xbox360controls(&xb360ud->controls); + } + else { + Serial.printf("XBox360u - rType=0x%x rLen=0x%x\n", xb360ud->rType, xb360ud->rLen); } } diff --git a/keywords.txt b/keywords.txt index 25f2630..3e678a7 100644 --- a/keywords.txt +++ b/keywords.txt @@ -91,6 +91,7 @@ PS3 LITERAL1 PS4 LITERAL1 XBOXONE LITERAL1 XBOX360 LITERAL1 +XBOX360USB LITERAL1 # USBSerial USBHOST_SERIAL_7E1 LITERAL1