A usb interface emulating an Xbox 360 controller
Support for rumble is not done yet.
Please note this is work in progress.
tested so far with LPC1769 and LPC11u35
Revision 0:430276835afe, committed 2016-09-16
- Comitter:
- olyeah
- Date:
- Fri Sep 16 10:01:59 2016 +0000
- Commit message:
- initial commit
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice.lib Fri Sep 16 10:01:59 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/USBDevice/#01321bd6ff89
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Xinput.cpp Fri Sep 16 10:01:59 2016 +0000 @@ -0,0 +1,302 @@ + + +#include <Xinput.h> + + + +Xinput::~Xinput() { + // TODO Auto-generated destructor stub +} + +void Xinput::send_controls(void){ + uint8_t TXData[20] = {0x00, // must be 0x00 + 0x14, // must be 0x14 + LSB(buttons), + MSB(buttons), + left_trigger, + right_trigger, + LSB(left_stick_x), + MSB(left_stick_x), + LSB(left_stick_y), + MSB(left_stick_y), + LSB(right_stick_x), + MSB(right_stick_x), + LSB(right_stick_y), + MSB(right_stick_y), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + USBDevice::write(XINPUT_TX_ENDPOINT, TXData, 20, 20); +} + +void Xinput::update_button(uint8_t button, uint8_t status){ + if(status){ + buttons |= 1UL << button; + } + /*else{ + buttons &= ~(1UL << button); + }*/ +} + +void Xinput::clear(void){ + buttons = 0; + left_stick_x = 0; + left_stick_y = 0; + left_trigger = 0; + right_stick_x = 0; + right_stick_y = 0; + right_trigger = 0; +} + +void Xinput::update_analog(uint8_t stick, int16_t value){ + switch(stick){ + case STICK_LEFT_X: + left_stick_x = value; + break; + case STICK_LEFT_Y: + left_stick_y = value; + break; + case STICK_RIGHT_X: + right_stick_x = value; + break; + case STICK_RIGHT_Y: + right_stick_y = value; + break; + default: + break; + } +} + +void Xinput::update_analog(uint8_t stick, uint8_t value){ + switch(stick){ + case TRIGGER_LEFT: + left_trigger = value; + break; + case TRIGGER_RIGHT: + right_trigger = value; + break; + default: + break; + } +} +/* + * +Process the LED Pattern +0x00 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 (1-2-4-3) +0x0B Blinking* +0x0C Slow Blinking* +0x0D Alternating (1+4-2+3)* + */ +bool Xinput::read_leds(uint8_t * data){ + uint32_t bytesRead = 0; + bool result; + result = USBDevice::readEP_NB(XINPUT_RX_ENDPOINT, data, &bytesRead, XINPUT_RX_SIZE); + // if readEP_NB did not succeed, does not issue a readStart + if (!result) + return false; + if(!readStart(XINPUT_RX_ENDPOINT, XINPUT_RX_SIZE)) + return false; + return result; +} + + + + + +#define DEFAULT_CONFIGURATION (1) +bool Xinput::USBCallback_setConfiguration(uint8_t configuration){ + addEndpoint(XINPUT_TX_ENDPOINT, XINPUT_TX_SIZE); + addEndpoint(XINPUT_RX_ENDPOINT, 20); + + readStart(XINPUT_RX_ENDPOINT, XINPUT_RX_SIZE); + return true; +} + + +uint8_t * Xinput::configurationDesc() { + static uint8_t configurationDescriptor[] = { + CONFIGURATION_DESCRIPTOR_LENGTH,// bLength + CONFIGURATION_DESCRIPTOR, // bDescriptorType + LSB(CONFIG_DESC_SIZE), // wTotalLength (LSB) + MSB(CONFIG_DESC_SIZE), // wTotalLength (MSB) + NUM_INTERFACE, // bNumInterfaces + DEFAULT_CONFIGURATION, // bConfigurationValue + 0x00, // iConfiguration + DEVICE_ATTRIBUTES, // bmAttributes + DEVICE_POWER, // bMaxPower + + //Interface 0 + 9, //bLength (length of interface descriptor 9 bytes) + 4, //bDescriptorType (4 is interface) + 0, //bInterfaceNumber (This is interface 0) + 0, //bAlternateSetting (used to select alternate setting. notused) + 2, //bNumEndpoints (this interface has 2 endpoints) + 0xFF, //bInterfaceClass (Vendor Defined is 255) + 0x5D, //bInterfaceSubClass + 0x01, //bInterfaceProtocol + 0, //iInterface (Index of string descriptor for describing this notused) + //Some sort of common descriptor? I pulled this from Message Analyzer dumps of an actual controller + 17,33,0,1,1,37,129,20,0,0,0,0,19,2,8,0,0, + //Endpoint 1 IN + 7, //bLength (length of ep1in in descriptor 7 bytes) + 5, //bDescriptorType (5 is endpoint) + 0x81, //bEndpointAddress (0x81 is IN1) + 0x03, //bmAttributes (0x03 is interrupt no synch, usage type data) + 0x20, 0x00, //wMaxPacketSize (0x0020 is 1x32 bytes) + 4, //bInterval (polling interval in frames 4 frames) + //Endpoint 2 OUT + 7, //bLength (length of ep2out in descriptor 7 bytes) + 5, //bDescriptorType (5 is endpoint) + 0x02, //bEndpointAddress (0x02 is OUT2) + 0x03, //bmAttributes (0x03 is interrupt no synch, usage type data) + 0x20, 0x00, //wMaxPacketSize (0x0020 is 1x32 bytes) + 8, //bInterval (polling interval in frames 8 frames) + //Interface 1 + 9, //bLength (length of interface descriptor 9 bytes) + 4, //bDescriptorType (4 is interface) + 1, //bInterfaceNumber (This is interface 1) + 0, //bAlternateSetting (used to select alternate setting. notused) + 4, //bNumEndpoints (this interface has 4 endpoints) + 0xFF, //bInterfaceClass (Vendor Defined is 255) + 0x5D, //bInterfaceSubClass (93) + 0x03, //bInterfaceProtocol (3) + 0, //iInterface (Index of string descriptor for describing this notused) + //A different common descriptor? I pulled this from Message Analyzer dumps of an actual controller + 27,33,0,1,1,1,131,64,1,4,32,22,133,0,0,0,0,0,0,22,5,0,0,0,0,0,0, + //Endpoint 3 IN + 7, //bLength (length of ep3in descriptor 7 bytes) + 5, //bDescriptorType (5 is endpoint) + 0x83, //bEndpointAddress (0x83 is IN3) + 0x03, //bmAttributes (0x03 is interrupt no synch, usage type data) + 0x20, 0x00, //wMaxPacketSize (0x0020 is 1x32 bytes) + 2, //bInterval (polling interval in frames 2 frames) + //Endpoint 4 OUT + 7, //bLength (length of ep4out descriptor 7 bytes) + 5, //bDescriptorType (5 is endpoint) + 0x04, //bEndpointAddress (0x04 is OUT4) + 0x03, //bmAttributes (0x03 is interrupt no synch, usage type data) + 0x20, 0x00, //wMaxPacketSize (0x0020 is 1x32 bytes) + 4, //bInterval (polling interval in frames 4 frames) + //Endpoint 5 IN + 7, //bLength (length of ep5in descriptor 7 bytes) + 5, //bDescriptorType (5 is endpoint) + 0x85, //bEndpointAddress (0x85 is IN5) + 0x03, //bmAttributes (0x03 is interrupt no synch, usage type data) + 0x20, 0x00, //wMaxPacketSize (0x0020 is 1x32 bytes) + 64, //bInterval (polling interval in frames 64 frames) + //Endpoint 5 OUT (shares endpoint number with previous) + 7, //bLength (length of ep5out descriptor 7 bytes) + 5, //bDescriptorType (5 is endpoint) + 0x05, //bEndpointAddress (0x05 is OUT5) + 0x03, //bmAttributes (0x03 is interrupt no synch, usage type data) + 0x20, 0x00, //wMaxPacketSize (0x0020 is 1x32 bytes) + 16, //bInterval (polling interval in frames 16 frames) + //Interface 2 + 9, //bLength (length of interface descriptor 9 bytes) + 4, //bDescriptorType (4 is interface) + 2, //bInterfaceNumber (This is interface 2) + 0, //bAlternateSetting (used to select alternate setting. notused) + 1, //bNumEndpoints (this interface has 4 endpoints) + 0xFF, //bInterfaceClass (Vendor Defined is 255) + 0x5D, //bInterfaceSubClass (93) + 0x02, //bInterfaceProtocol (3) + 0, //iInterface (Index of string descriptor for describing this notused) + //Common Descriptor. Seems that these come after every interface description? + 9,33,0,1,1,34,134,7,0, + //Endpoint 6 IN + 7, //bLength (length of ep6in descriptor 7 bytes) + 5, //bDescriptorType (5 is endpoint) + 0x86, //bEndpointAddress (0x86 is IN6) + 0x03, //bmAttributes (0x03 is interrupt no synch, usage type data) + 0x20, 0x00, //wMaxPacketSize (0x0020 is 1x32 bytes) + 16, //bInterval (polling interval in frames 64 frames)+ + //Interface 3 + //This is the interface on which all the security handshaking takes place + //We don't use this but it could be used for man-in-the-middle stuff + 9, //bLength (length of interface descriptor 9 bytes) + 4, //bDescriptorType (4 is interface) + 3, //bInterfaceNumber (This is interface 3) + 0, //bAlternateSetting (used to select alternate setting. notused) + 0, //bNumEndpoints (this interface has 0 endpoints ???) + 0xFF, //bInterfaceClass (Vendor Defined is 255) + 0xFD, //bInterfaceSubClass (253) + 0x13, //bInterfaceProtocol (19) + 4, //iInterface (Computer never asks for this, but an x360 would. so include one day?) + //Another interface another Common Descriptor + 6,65,0,1,1,3 + }; + return configurationDescriptor; +} + + +uint8_t * Xinput::deviceDesc() { + static uint8_t deviceDescriptor[] = { + DEVICE_DESCRIPTOR_LENGTH, /* bLength */ + DEVICE_DESCRIPTOR, /* bDescriptorType */ + LSB(USB_VERSION_2_0), /* bcdUSB (LSB) */ + MSB(USB_VERSION_2_0), /* bcdUSB (MSB) */ + DEVICE_CLASS, /* bDeviceClass */ + DEVICE_SUBCLASS, /* bDeviceSubClass */ + DEVICE_PROTOCOL, /* bDeviceprotocol */ + MAX_PACKET_SIZE_EP0, /* bMaxPacketSize0 */ + (uint8_t)(LSB(VENDOR_ID)), /* idVendor (LSB) */ + (uint8_t)(MSB(VENDOR_ID)), /* idVendor (MSB) */ + (uint8_t)(LSB(PRODUCT_ID)), /* idProduct (LSB) */ + (uint8_t)(MSB(PRODUCT_ID)), /* idProduct (MSB) */ + (uint8_t)(LSB(DEVICE_VERSION)), /* bcdDevice (LSB) */ + (uint8_t)(MSB(DEVICE_VERSION)), /* bcdDevice (MSB) */ + STRING_OFFSET_IMANUFACTURER, /* iManufacturer */ + STRING_OFFSET_IPRODUCT, /* iProduct */ + STRING_OFFSET_ISERIAL, /* iSerialNumber */ + 0x01 /* bNumConfigurations */ + }; + return deviceDescriptor; +} + +uint8_t * Xinput::stringImanufacturerDesc() { + static uint8_t stringImanufacturerDescriptor[] = { + 0x16, /*bLength*/ + STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ + '©',0,'M',0,'i',0,'c',0,'r',0,'o',0,'s',0,'o',0,'f',0,'t',0 + }; + return stringImanufacturerDescriptor; +} + + +uint8_t * Xinput::stringIproductDesc() { + static uint8_t stringIproductDescriptor[] = { + 0x16, /*bLength*/ + STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ + 'C',0,'o',0,'n',0,'t',0,'r',0,'o',0,'l',0,'l',0,'e',0,'r',0 /*bString iProduct - USB DEVICE*/ + }; + return stringIproductDescriptor; +} + + +uint8_t * Xinput::stringIConfigurationDesc() { + + static uint8_t stringIconfigurationDescriptor[] = { + 0xB2, /*bLength*/ + STRING_DESCRIPTOR, /*bDescriptorType 0x03*/ + 0x58, 0x00, 0x62, 0x00, 0x6F, 0x00, 0x78, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, 0x63, 0x00, + 0x75, 0x00, 0x72, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x65, 0x00, + 0x74, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x20, 0x00, 0x33, 0x00, 0x2C, 0x00, 0x20, 0x00, + 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x20, 0x00, + 0x31, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x30, 0x00, 0x2C, 0x00, 0x20, 0x00, 0xA9, 0x00, 0x20, 0x00, + 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x35, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x69, 0x00, 0x63, 0x00, + 0x72, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x43, 0x00, + 0x6F, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, + 0x6F, 0x00, 0x6E, 0x00, 0x2E, 0x00, 0x20, 0x00, 0x41, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x20, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x73, 0x00, 0x20, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x64, 0x00, 0x2E, 0x00 + }; + return stringIconfigurationDescriptor; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Xinput.h Fri Sep 16 10:01:59 2016 +0000 @@ -0,0 +1,153 @@ +/* + * Xinput.h + * + * Created on: 8 sept. 2016 + * Author: Olivier Chabloz + */ + +#ifndef XINPUT_H_ +#define XINPUT_H_ + +#include "USBDevice.h" +#include "USBHID.h" + +#define DEVICE_CLASS 0xFF +#define DEVICE_SUBCLASS 0xFF +#define DEVICE_PROTOCOL 0xFF +#define DEVICE_VERSION 0x0114 +#define DEVICE_ATTRIBUTES 0xA0 +#define DEVICE_POWER 0xFA +#define VENDOR_ID 0x045e +#define PRODUCT_ID 0x028e +#define MANUFACTURER_NAME {'©','M','i','c','r','o','s','o','f','t'} +#define MANUFACTURER_NAME_LEN 10 +#define PRODUCT_NAME {'C','o','n','t','r','o','l','l','e','r'} +#define PRODUCT_NAME_LEN 10 +#define EP0_SIZE 8 +#define NUM_ENDPOINTS 6 +#define NUM_USB_BUFFERS 24 +#define NUM_INTERFACE 4 +#define XINPUT_INTERFACE 0 +#define XINPUT_RX_ENDPOINT EP2OUT +#define XINPUT_RX_SIZE 3 +#define XINPUT_TX_ENDPOINT EP1IN +#define XINPUT_TX_SIZE 20 +#define CONFIG_DESC_SIZE 153 +#define ENDPOINT1_CONFIG ENDPOINT_TRANSIMIT_ONLY +#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_ONLY +#define ENDPOINT3_CONFIG ENDPOINT_TRANSIMIT_ONLY +#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_ONLY +#define ENDPOINT5_CONFIG ENDPOINT_TRANSMIT_AND_RECEIVE +#define ENDPOINT6_CONFIG ENDPOINT_TRANSIMIT_ONLY + + + +#define BUTTON_Y 15 +#define BUTTON_X 14 +#define BUTTON_B 13 +#define BUTTON_A 12 + +#define BUTTON_LOGO 10 +#define BUTTON_RB 9 +#define BUTTON_LB 8 +#define BUTTON_R3 7 +#define BUTTON_L3 6 +#define BUTTON_Back 5 +#define BUTTON_Start 4 +#define BUTTON_Right 3 +#define BUTTON_Left 2 +#define BUTTON_Down 1 +#define BUTTON_Up 0 + + +#define STICK_LEFT_X 1 +#define STICK_LEFT_Y 2 +#define STICK_RIGHT_X 3 +#define STICK_RIGHT_Y 4 +#define TRIGGER_LEFT 5 +#define TRIGGER_RIGHT 6 + + + +class Xinput: public USBDevice { +public: + Xinput(): + USBDevice( VENDOR_ID, PRODUCT_ID, 0x0001), buttons(0), left_stick_x(0), left_stick_y(0), right_stick_x(0), right_stick_y(0), left_trigger(0), right_trigger(0){ + connect(); + }; + + virtual ~Xinput(); + + + /* + * Get device descriptor. Warning: this method has to store the length of the report descriptor in reportLength. + * + * @returns pointer to the device descriptor + */ + virtual uint8_t * deviceDesc(); + + /* + * Get string manufacturer descriptor + * + * @returns pointer to the string manufacturer descriptor + */ + virtual uint8_t * stringImanufacturerDesc(); + + /* + * Get string product descriptor + * + * @returns pointer to the string product descriptor + */ + virtual uint8_t * stringIproductDesc(); + + /* + * Get string configuration descriptor + * + * @returns pointer to the string configuration descriptor + */ + virtual uint8_t * stringIConfigurationDesc(); + + /* + * Called by USBDevice layer. Set configuration of the device. + * For instance, you can add all endpoints that you need on this function. + * + * @param configuration Number of the configuration + * @returns true if class handles this request + */ + virtual bool USBCallback_setConfiguration(uint8_t configuration); + + + + bool read_leds(uint8_t * data); + void send_controls(void); + void clear(void); + + /* + * Update Xinput buttons + * + * @param button The button number to update + * @param status 1 to activate, 0 to desactivate + */ + void update_button(uint8_t button, uint8_t status); + + void update_analog(uint8_t stick, int16_t value); + void update_analog(uint8_t stick, uint8_t value); +protected: + /* + * Get configuration descriptor + * + * @returns pointer to the configuration descriptor + */ + virtual uint8_t * configurationDesc(); + +private: + uint16_t buttons; + int16_t left_stick_x; + int16_t left_stick_y; + int16_t right_stick_x; + int16_t right_stick_y; + uint8_t left_trigger; + uint8_t right_trigger; +}; + +#endif /* XINPUT_H_ */