A usb interface emulating an Xbox 360 controller

Dependencies:   USBDevice

Support for rumble is not done yet.

Please note this is work in progress.

tested so far with LPC1769 and LPC11u35

Files at this revision

API Documentation at this revision

Comitter:
olyeah
Date:
Fri Sep 16 10:01:59 2016 +0000
Commit message:
initial commit

Changed in this revision

USBDevice.lib Show annotated file Show diff for this revision Revisions of this file
Xinput.cpp Show annotated file Show diff for this revision Revisions of this file
Xinput.h Show annotated file Show diff for this revision Revisions of this file
--- /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_ */