Browse Source

XBox360 Wireless controller

WIP - So far i am getting the basic values for 1 XBox 360 controller.  The device should handle up to 4 devices, but so far hard coded to only handle 1...
main
Kurt Eckhardt 6 years ago
parent
commit
349e18a1b8
6 changed files with 309 additions and 27 deletions
  1. +29
    -2
      USBHost_t36.h
  2. +108
    -0
      bluetooth.cpp
  3. +1
    -1
      enumeration.cpp
  4. +2
    -0
      examples/Mouse/Mouse.ino
  5. +167
    -23
      joystick.cpp
  6. +2
    -1
      keywords.txt

+ 29
- 2
USBHost_t36.h View File

const uint8_t *manufacturer(); const uint8_t *manufacturer();
const uint8_t *product(); const uint8_t *product();
const uint8_t *serialNumber(); const uint8_t *serialNumber();
operator bool() { return ((device != nullptr) || (mydevice != nullptr)); } // override as in both USBDriver and in USBHIDInput
operator bool() { return (((device != nullptr) || (mydevice != nullptr)) && connected_); } // override as in both USBDriver and in USBHIDInput


bool available() { return joystickEvent; } bool available() { return joystickEvent; }
void joystickDataClear(); void joystickDataClear();
// setLEDs on PS4(RGB), PS3 simple LED setting (only uses lr) // 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, 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) }; 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} joytype_t;
typedef enum { UNKNOWN=0, PS3, PS4, XBOXONE, XBOX360} joytype_t;
joytype_t joystickType = UNKNOWN; joytype_t joystickType = UNKNOWN;
protected: protected:
// From USBDriver // From USBDriver
uint8_t rumble_rValue_ = 0; uint8_t rumble_rValue_ = 0;
uint8_t rumble_timeout_ = 0; uint8_t rumble_timeout_ = 0;
uint8_t leds_[3] = {0,0,0}; uint8_t leds_[3] = {0,0,0};
uint8_t connected_ = 0; // what type of device if any is connected xbox 360...




// Used by HID code // Used by HID code


}; };


//--------------------------------------------------------------------------

class BluetoothController: public USBDriver {
public:
BluetoothController(USBHost &host) { init(); }
enum {MAX_ENDPOINTS=4, NUM_SERVICES=4}; // Max number of Bluetooth services - if you need more than 4 simply increase this number

protected:
virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len);
virtual void control(const Transfer_t *transfer);
virtual void disconnect();
//virtual void timer_event(USBDriverTimer *whichTimer);
private:
static void rx_callback(const Transfer_t *transfer);
static void tx_callback(const Transfer_t *transfer);
void rx_data(const Transfer_t *transfer);
void tx_data(const Transfer_t *transfer);

void init();

Pipe_t mypipes[3] __attribute__ ((aligned(32)));
Transfer_t mytransfers[7] __attribute__ ((aligned(32)));
strbuf_t mystring_bufs[1];

};

#endif #endif

+ 108
- 0
bluetooth.cpp View File

/* USB EHCI Host for Teensy 3.6
* Copyright 2017 Paul Stoffregen (paul@pjrc.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Note: special thanks to the Linux kernel for the CH341's method of operation, particularly how the baud rate is encoded.
*/

#include <Arduino.h>
#include "USBHost_t36.h" // Read this header first for key info

#define print USBHost::print_
#define println USBHost::println_

/************************************************************/
// Define mapping VID/PID - to Serial Device type.
/************************************************************/

/************************************************************/
// Initialization and claiming of devices & interfaces
/************************************************************/

void BluetoothController::init()
{
contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t));
contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t));
contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t));
driver_ready_for_device(this);
}

bool BluetoothController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len)
{
// only claim at device level
println("BluetoothController claim this=", (uint32_t)this, HEX);
print("vid=", dev->idVendor, HEX);
print(", pid=", dev->idProduct, HEX);
print(", bDeviceClass = ", dev->bDeviceClass, HEX);
print(", bDeviceSubClass = ", dev->bDeviceSubClass, HEX);
println(", bDeviceProtocol = ", dev->bDeviceProtocol, HEX);
print_hexbytes(descriptors, len);

// Lets try to support the main USB Bluetooth class...
// http://www.usb.org/developers/defined_class/#BaseClassE0h
if (dev->bDeviceClass != 0xe0) return false; // not base class wireless controller
if ((dev->bDeviceSubClass != 1) || (dev->bDeviceProtocol != 1)) return false; // Bluetooth Programming Interface

if (type == 0) {
}

return false;
}


void BluetoothController::disconnect()
{
}



void BluetoothController::control(const Transfer_t *transfer)
{
//println("control callback (bluetooth) ", pending_control, HEX);
}

/************************************************************/
// Interrupt-based Data Movement
/************************************************************/

void BluetoothController::rx_callback(const Transfer_t *transfer)
{
if (!transfer->driver) return;
((BluetoothController *)(transfer->driver))->rx_data(transfer);
}

void BluetoothController::tx_callback(const Transfer_t *transfer)
{
if (!transfer->driver) return;
((BluetoothController *)(transfer->driver))->tx_data(transfer);
}


void BluetoothController::rx_data(const Transfer_t *transfer)
{
//uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF);
}


void BluetoothController::tx_data(const Transfer_t *transfer)
{
}


+ 1
- 1
enumeration.cpp View File

// Static buffers used during enumeration. One a single USB device // Static buffers used during enumeration. One a single USB device
// may enumerate at once, because USB address zero is used, and // may enumerate at once, because USB address zero is used, and
// because this static buffer & state info can't be shared. // because this static buffer & state info can't be shared.
static uint8_t enumbuf[256] __attribute__ ((aligned(16)));
static uint8_t enumbuf[512] __attribute__ ((aligned(16)));
static setup_t enumsetup __attribute__ ((aligned(16))); static setup_t enumsetup __attribute__ ((aligned(16)));
static uint16_t enumlen; static uint16_t enumlen;



+ 2
- 0
examples/Mouse/Mouse.ino View File

USBHIDParser hid5(myusb); USBHIDParser hid5(myusb);
MouseController mouse1(myusb); MouseController mouse1(myusb);
JoystickController joystick1(myusb); JoystickController joystick1(myusb);
BluetoothController bluet(myusb);
int user_axis[64]; int user_axis[64];
uint32_t buttons_prev = 0; uint32_t buttons_prev = 0;
RawHIDController rawhid1(myusb); RawHIDController rawhid1(myusb);
break; break;


case JoystickController::XBOXONE: case JoystickController::XBOXONE:
case JoystickController::XBOX360:
ltv = joystick1.getAxis(4); ltv = joystick1.getAxis(4);
rtv = joystick1.getAxis(5); rtv = joystick1.getAxis(5);
if ((ltv != joystick_left_trigger_value) || (rtv != joystick_right_trigger_value)) { if ((ltv != joystick_left_trigger_value) || (rtv != joystick_right_trigger_value)) {

+ 167
- 23
joystick.cpp View File

// doing other features. // doing other features.
JoystickController::product_vendor_mapping_t JoystickController::pid_vid_mapping[] = { JoystickController::product_vendor_mapping_t JoystickController::pid_vid_mapping[] = {
{ 0x045e, 0x02ea, XBOXONE, false },{ 0x045e, 0x02dd, XBOXONE, false }, { 0x045e, 0x02ea, XBOXONE, false },{ 0x045e, 0x02dd, XBOXONE, false },
{ 0x045e, 0x0719, XBOX360, false},
{ 0x054C, 0x0268, PS3, true}, { 0x054C, 0x0268, PS3, true},
{ 0x054C, 0x05C4, PS4, true}, {0x054C, 0x09CC, PS4, true } { 0x054C, 0x05C4, PS4, true}, {0x054C, 0x09CC, PS4, true }
}; };
println("XBoxOne rumble transfer fail"); println("XBoxOne rumble transfer fail");
} }
return true; // return true; //
case XBOX360:
txbuf_[0] = 0x00;
txbuf_[1] = 0x01;
txbuf_[2] = 0x0F;
txbuf_[3] = 0xC0;
txbuf_[4] = 0x00;
txbuf_[5] = lValue;
txbuf_[6] = rValue;
txbuf_[7] = 0x00;
txbuf_[8] = 0x00;
txbuf_[9] = 0x00;
txbuf_[10] = 0x00;
txbuf_[11] = 0x00;
if (!queue_Data_Transfer(txpipe_, txbuf_, 12, this)) {
println("XBox360 rumble transfer fail");
}
return true;
} }
return false; return false;
} }
return transmitPS3UserFeedbackMsg(); return transmitPS3UserFeedbackMsg();
case PS4: case PS4:
return transmitPS4UserFeedbackMsg(); return transmitPS4UserFeedbackMsg();
case XBOX360:
// 0: off, 1: all blink then return to before
// 2-5(TL, TR, BL, BR) - blink on then stay on
// 6-9() - On
// ...
txbuf_[1] = 0x00;
txbuf_[2] = 0x08;
txbuf_[3] = 0x40 + lr;
txbuf_[4] = 0x00;
txbuf_[5] = 0x00;
txbuf_[6] = 0x00;
txbuf_[7] = 0x00;
txbuf_[8] = 0x00;
txbuf_[9] = 0x00;
txbuf_[10] = 0x00;
txbuf_[11] = 0x00;
if (!queue_Data_Transfer(txpipe_, txbuf_, 12, this)) {
println("XBox360 set leds fail");
}
return true;
case XBOXONE:
default: default:
return false; return false;
} }
if (topusage != 0x10004 && topusage != 0x10005) return CLAIM_NO; if (topusage != 0x10004 && topusage != 0x10005) return CLAIM_NO;
// only claim from one physical device // only claim from one physical device
if (mydevice != NULL && dev != mydevice) return CLAIM_NO; if (mydevice != NULL && dev != mydevice) return CLAIM_NO;

// Also don't allow us to claim if it is used as a standard usb object (XBox...)
if (device != nullptr) return CLAIM_NO;

mydevice = dev; mydevice = dev;
collections_claimed++; collections_claimed++;
anychange = true; // always report values on first read anychange = true; // always report values on first read
driver_ = driver; // remember the driver. driver_ = driver; // remember the driver.
driver_->setTXBuffers(txbuf_, nullptr, sizeof(txbuf_)); driver_->setTXBuffers(txbuf_, nullptr, sizeof(txbuf_));
connected_ = true; // remember that hardware is actually connected...


// Lets see if we know what type of joystick this is. That is, is it a PS3 or PS4 or ... // Lets see if we know what type of joystick this is. That is, is it a PS3 or PS4 or ...
joystickType = mapVIDPIDtoJoystickType(mydevice->idVendor, mydevice->idProduct, false); joystickType = mapVIDPIDtoJoystickType(mydevice->idVendor, mydevice->idProduct, false);
// Example: XBox One controller. // Example: XBox One controller.
//***************************************************************************** //*****************************************************************************


static uint8_t start_input[] = {0x05, 0x20, 0x00, 0x01, 0x00};
static uint8_t xboxone_start_input[] = {0x05, 0x20, 0x00, 0x01, 0x00};
static uint8_t xbox360w_inquire_present[] = {0x08, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};


bool JoystickController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) bool JoystickController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len)
{ {
println("JoystickController claim this=", (uint32_t)this, HEX); println("JoystickController claim this=", (uint32_t)this, HEX);


// Don't try to claim if it is used as USB device or HID device
if (mydevice != NULL) return false;
if (device != nullptr) return false;
// only claim at device level // only claim at device level
if (type != 0) return false; if (type != 0) return false;
print_hexbytes(descriptors, len); print_hexbytes(descriptors, len);
if (jtype == UNKNOWN) if (jtype == UNKNOWN)
return false; return false;


// 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 9 30 1...
// XBOX One
// 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 9 30 1...
// 09 04 00 00 02 FF 47 D0 00 07 05 02 03 40 00 04 07 05 82 03 40 00 04 09 04 01 00 00 FF 47 D0 00 // 09 04 00 00 02 FF 47 D0 00 07 05 02 03 40 00 04 07 05 82 03 40 00 04 09 04 01 00 00 FF 47 D0 00
// Lets do some verifications to make sure. // Lets do some verifications to make sure.


// XBOX 360 wireless... Has 8 interfaces. 4 joysticks (1, 3, 5, 7) and 4 headphones assume 2,4,6, 8...
// 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 81 00 14 22 00 01 13 81 1D 00 17 01 02 08 13 01 0C 00 0C 01 02 08

// 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

if (len < 9+7+7) return false; if (len < 9+7+7) return false;


// Some common stuff for both XBoxs
uint32_t count_end_points = descriptors[4]; uint32_t count_end_points = descriptors[4];
if (count_end_points < 2) return false; if (count_end_points < 2) return false;
if (descriptors[5] != 0xff) return false; // bInterfaceClass, 3 = HID if (descriptors[5] != 0xff) return false; // bInterfaceClass, 3 = HID
uint32_t rxep = 0; uint32_t rxep = 0;
uint32_t txep = 0; uint32_t txep = 0;
uint8_t rx_interval = 0;
uint8_t tx_interval = 0;
rx_size_ = 0; rx_size_ = 0;
tx_size_ = 0; tx_size_ = 0;
uint32_t descriptor_index = 9; uint32_t descriptor_index = 9;
if (descriptors[descriptor_index+1] == 0x22) descriptor_index += descriptors[descriptor_index]; // XBox360w ignore this unknown setup...
while (count_end_points-- && ((rxep == 0) || txep == 0)) { while (count_end_points-- && ((rxep == 0) || txep == 0)) {
if (descriptors[descriptor_index] != 7) return false; // length 7 if (descriptors[descriptor_index] != 7) return false; // length 7
if (descriptors[descriptor_index+1] != 5) return false; // ep desc if (descriptors[descriptor_index+1] != 5) return false; // ep desc
if (descriptors[descriptor_index+2] & 0x80 ) { if (descriptors[descriptor_index+2] & 0x80 ) {
rxep = descriptors[descriptor_index+2]; rxep = descriptors[descriptor_index+2];
rx_size_ = descriptors[descriptor_index+4]; rx_size_ = descriptors[descriptor_index+4];
rx_interval = descriptors[descriptor_index+6];
} else { } else {
txep = descriptors[descriptor_index+2]; txep = descriptors[descriptor_index+2];
tx_size_ = descriptors[descriptor_index+4]; tx_size_ = descriptors[descriptor_index+4];
tx_interval = descriptors[descriptor_index+6];
} }
} }
descriptor_index += 7; // setup to look at next one... descriptor_index += 7; // setup to look at next one...
print("), txep=", txep); print("), txep=", txep);
print("(", tx_size_); print("(", tx_size_);
println(")"); println(")");
rxpipe_ = new_Pipe(dev, 2, rxep & 15, 1, rx_size_);
rxpipe_ = new_Pipe(dev, 3, rxep & 15, 1, rx_size_, rx_interval);
if (!rxpipe_) return false; if (!rxpipe_) return false;
txpipe_ = new_Pipe(dev, 2, txep, 0, tx_size_);
txpipe_ = new_Pipe(dev, 3, txep, 0, tx_size_, tx_interval);
if (!txpipe_) { if (!txpipe_) {
//free_Pipe(rxpipe_); //free_Pipe(rxpipe_);
return false; return false;


txpipe_->callback_function = tx_callback; txpipe_->callback_function = tx_callback;


queue_Data_Transfer(txpipe_, start_input, sizeof(start_input), this);
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) {
queue_Data_Transfer(txpipe_, xbox360w_inquire_present, sizeof(xbox360w_inquire_present), this);
connected_ = 0; // remember that hardware is actually connected...
}
memset(axis, 0, sizeof(axis)); // clear out any data. memset(axis, 0, sizeof(axis)); // clear out any data.
joystickType = jtype; // remember we are an XBox One. joystickType = jtype; // remember we are an XBox One.
return true; return true;
// dpad up, down left, right // dpad up, down left, right
// lb, rb, left stick, right stick // lb, rb, left stick, right stick
// Axis: // Axis:
// lt, rt, lx, xy, rx, ry
// lt, rt, lx, ly, rx, ry
// //
uint16_t buttons; uint16_t buttons;
int16_t axis[6]; int16_t axis[6];
} xbox1data20_t; } 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}; static const uint8_t xbox_axis_order_mapping[] = {4, 5, 0, 1, 2, 3};


void JoystickController::rx_data(const Transfer_t *transfer) void JoystickController::rx_data(const Transfer_t *transfer)
{ {
// print("JoystickController::rx_data: ");
// print_hexbytes((uint8_t*)transfer->buffer, transfer->length);
axis_mask_ = 0x3f;
axis_changed_mask_ = 0; // assume none for now
xbox1data20_t *xb1d = (xbox1data20_t *)transfer->buffer;
if ((xb1d->type == 0x20) && (transfer->length >= sizeof (xbox1data20_t))) {
// We have a data transfer. Lets see what is new...
if (xb1d->buttons != buttons) {
buttons = xb1d->buttons;
anychange = true;
print("JoystickController::rx_data: ");
print_hexbytes((uint8_t*)transfer->buffer, transfer->length);

if (joystickType == XBOXONE) {
// Process XBOX One data
axis_mask_ = 0x3f;
axis_changed_mask_ = 0; // assume none for now
xbox1data20_t *xb1d = (xbox1data20_t *)transfer->buffer;
if ((xb1d->type == 0x20) && (transfer->length >= sizeof (xbox1data20_t))) {
// We have a data transfer. Lets see what is new...
if (xb1d->buttons != buttons) {
buttons = xb1d->buttons;
anychange = true;
}
for (uint8_t i = 0; i < sizeof (xbox_axis_order_mapping); i++) {
// The first two values were unsigned.
int axis_value = (i < 2)? (int)(uint16_t)xb1d->axis[i] : xb1d->axis[i];
if (axis_value != axis[xbox_axis_order_mapping[i]]) {
axis[xbox_axis_order_mapping[i]] = axis_value;
axis_changed_mask_ |= (1 << xbox_axis_order_mapping[i]);
anychange = true;
}
}
joystickEvent = true;
} }
for (uint8_t i = 0; i < sizeof (xbox_axis_order_mapping); i++) {
// The first two values were unsigned.
int axis_value = (i < 2)? (int)(uint16_t)xb1d->axis[i] : xb1d->axis[i];
if (axis_value != axis[xbox_axis_order_mapping[i]]) {
axis[xbox_axis_order_mapping[i]] = axis_value;

} else if (joystickType == XBOX360) {
// First byte appears to status - if the byte is 0x8 it is a connect or disconnect of the controller.
const uint8_t *pbuffer = (uint8_t*)transfer->buffer;
xbox360data_t *xb360d = (xbox360data_t *)transfer->buffer;
if (xb360d->state == 0x08) {
if (xb360d->id_or_type != connected_) {
connected_ = xb360d->id_or_type; // remember it...
if (connected_) {
println("XBox360w - Connected type:", connected_, HEX);
setLEDs(2); // Right now hard coded to first joystick...

} else {
println("XBox360w - disconnected");
}
}
} 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.
//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; anychange = true;
} }

if (anychange) joystickEvent = true;
} }
joystickEvent = true;
} }



queue_Data_Transfer(rxpipe_, rxbuf_, rx_size_, this); queue_Data_Transfer(rxpipe_, rxbuf_, rx_size_, this);
} }



+ 2
- 1
keywords.txt View File

AntPlus KEYWORD1 AntPlus KEYWORD1
JoystickController KEYWORD1 JoystickController KEYWORD1
RawHIDController KEYWORD1 RawHIDController KEYWORD1
BluetoothController KEYWORD1
# Common Functions # Common Functions
Task KEYWORD2 Task KEYWORD2
idVendor KEYWORD2 idVendor KEYWORD2
PS3 LITERAL1 PS3 LITERAL1
PS4 LITERAL1 PS4 LITERAL1
XBOXONE LITERAL1 XBOXONE LITERAL1
XBOX360 LITERAL1


# USBSerial # USBSerial
USBHOST_SERIAL_7E1 LITERAL1 USBHOST_SERIAL_7E1 LITERAL1

Loading…
Cancel
Save