A fully-Android-compatible two joysticks USB driver for LPC1768. The joysticks have 1 hat, 6 buttons, and there are 1P, 2P buttons.

Dependencies:   mbed

Fork of app-board-Joystick by Chris Styles

Files at this revision

API Documentation at this revision

Comitter:
Alberto_Wino
Date:
Fri Dec 16 18:17:42 2016 +0000
Parent:
0:0e4db18afd77
Child:
2:84ea6e2fb4b6
Commit message:
Import for dual joystick support, suitable for an arcade machine.; Based on code from Phil Wright.

Changed in this revision

main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
usbdc.cpp Show annotated file Show diff for this revision Revisions of this file
usbdc.h Show annotated file Show diff for this revision Revisions of this file
usbdevice.cpp Show annotated file Show diff for this revision Revisions of this file
usbdevice.h Show annotated file Show diff for this revision Revisions of this file
usbhid.cpp Show annotated file Show diff for this revision Revisions of this file
usbhid.h Show annotated file Show diff for this revision Revisions of this file
--- a/main.cpp	Mon Oct 15 13:37:36 2012 +0000
+++ b/main.cpp	Fri Dec 16 18:17:42 2016 +0000
@@ -1,18 +1,54 @@
 #include "mbed.h"
+#include "usbhid.h"
+
+#define IGN p5 /* ignore */
+
+BusIn buttonsL
+(
+    p17, p16, p15, p20, // 3 top row, bottom-left
+    p19, p18, p14, IGN, // 2 bottom right, 1p
+    IGN, IGN, IGN, IGN
+);
 
-BusIn joy(p15,p12,p13,p16);
-DigitalIn fire(p14);
+BusIn buttonsR
+(
+    p9,  p8,  p7,  p12, // 3 top row, bottom-left
+    p11, p10, p6,  IGN, // 2 bottom right, 2p
+    IGN, IGN, IGN, IGN
+);
 
-BusOut leds(LED1,LED2,LED3,LED4);
+BusIn stickL(p21, p22, p23, p24);
+BusIn stickR(p28, p26, p27, p29);
+
+USBJoystick joysticks; // left and right pads
 
 int main()
 {
-    while(1) {
-        if (fire) {
-            leds=0xf;
-        } else {
-            leds=joy;
+    unsigned char stickL_old = 0;
+    unsigned char stickR_old = 0;
+    unsigned char buttonsL_old = 0;
+    unsigned char buttonsR_old = 0;
+
+    while(1)
+    {
+        const unsigned char stickL_new = stickL.read();
+        const unsigned char stickR_new = stickR.read();
+        const unsigned long buttonsL_new = buttonsL.read();
+        const unsigned long buttonsR_new = buttonsR.read();
+
+        if ((stickL_old != stickL_new) || (buttonsL_old != buttonsL_new))
+        {
+            stickL_old = stickL_new;
+            buttonsL_old = buttonsL_new;
+            joysticks.update(1, stickL_old, buttonsL_old);
         }
-        wait(0.1);
+
+        if ((stickR_old != stickR_new) || (buttonsR_old != buttonsR_new))
+        {
+            stickR_old = stickR_new;
+            buttonsR_old = buttonsR_new;
+            joysticks.update(2, stickR_old, buttonsR_old);
+        }
+        wait(0.016666); /* 60 Hz */
     }
-}
+}
\ No newline at end of file
--- a/mbed.bld	Mon Oct 15 13:37:36 2012 +0000
+++ b/mbed.bld	Fri Dec 16 18:17:42 2016 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/mbed_official/code/mbed/builds/cd19af002ccc
\ No newline at end of file
+http://mbed.org/users/mbed_official/code/mbed/builds/d75b3fe1f5cb
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbdc.cpp	Fri Dec 16 18:17:42 2016 +0000
@@ -0,0 +1,545 @@
+/* usbdc.cpp */
+/* USB device controller */
+/* Copyright (c) Phil Wright 2008 */
+
+#include "mbed.h"
+#include "usbdc.h"
+#include "cmsis.h"
+
+#ifdef  TARGET_LPC2368
+#undef  LPC_USB_BASE
+#define LPC_USB_BASE (0xFFE0C000) /* TODO */
+#endif
+
+/* Power Control for Peripherals register */
+#define PCUSB      ((unsigned long)1<<31)
+
+/* USB Clock Control register */
+#define DEV_CLK_EN ((unsigned long)1<<1)
+#define AHB_CLK_EN ((unsigned long)1<<4)
+
+/* USB Clock Status register */
+#define DEV_CLK_ON ((unsigned long)1<<1)
+#define AHB_CLK_ON ((unsigned long)1<<4)
+
+/* USB Device Interupt registers */
+#define FRAME      ((unsigned long)1<<0)
+#define EP_FAST    ((unsigned long)1<<1)
+#define EP_SLOW    ((unsigned long)1<<2)
+#define DEV_STAT   ((unsigned long)1<<3)
+#define CCEMPTY    ((unsigned long)1<<4)
+#define CDFULL     ((unsigned long)1<<5)
+#define RxENDPKT   ((unsigned long)1<<6)
+#define TxENDPKT   ((unsigned long)1<<7)
+#define EP_RLZED   ((unsigned long)1<<8)
+#define ERR_INT    ((unsigned long)1<<9)
+
+/* Endpoint Interrupt Registers */
+#define EP(endpoint) (1<<endpoint)
+
+/* USB Control register */
+#define RD_EN (1<<0)
+#define WR_EN (1<<1)
+#define LOG_ENDPOINT(endpoint) ((endpoint>>1)<<2)
+
+/* USB Receive Packet Length register */
+#define DV      ((unsigned long)1<<10)
+#define PKT_RDY ((unsigned long)1<<11)
+#define PKT_LNGTH_MASK (0x3ff)
+
+/* Serial Interface Engine (SIE) */
+#define SIE_WRITE   (0x01)
+#define SIE_READ    (0x02)
+#define SIE_COMMAND (0x05)
+#define SIE_CMD_CODE(phase, data) ((phase<<8)|(data<<16))
+
+/* SIE Command codes */
+#define SIE_CMD_SET_ADDRESS        (0xD0)
+#define SIE_CMD_CONFIGURE_DEVICE   (0xD8)
+#define SIE_CMD_SET_MODE           (0xF3)
+#define SIE_CMD_READ_FRAME_NUMBER  (0xF5)
+#define SIE_CMD_READ_TEST_REGISTER (0xFD)
+#define SIE_CMD_SET_DEVICE_STATUS  (0xFE)
+#define SIE_CMD_GET_DEVICE_STATUS  (0xFE)
+#define SIE_CMD_GET_ERROR_CODE     (0xFF)
+#define SIE_CMD_READ_ERROR_STATUS  (0xFB)
+
+#define SIE_CMD_SELECT_ENDPOINT(endpoint)                 (0x00+endpoint)
+#define SIE_CMD_SELECT_ENDPOINT_CLEAR_INTERRUPT(endpoint) (0x40+endpoint)
+#define SIE_CMD_SET_ENDPOINT_STATUS(endpoint)             (0x40+endpoint)
+
+#define SIE_CMD_CLEAR_BUFFER    (0xF2)
+#define SIE_CMD_VALIDATE_BUFFER (0xFA)
+
+/* SIE Device Status register */
+#define SIE_DS_CON    (1<<0)
+#define SIE_DS_CON_CH (1<<1)
+#define SIE_DS_SUS    (1<<2)
+#define SIE_DS_SUS_CH (1<<3)
+#define SIE_DS_RST    (1<<4)
+
+/* SIE Device Set Address register */
+#define SIE_DSA_DEV_EN  (1<<7)
+
+/* SIE Configue Device register */
+#define SIE_CONF_DEVICE (1<<0)
+
+/* Select Endpoint register */
+#define SIE_SE_FE       (1<<0)
+#define SIE_SE_ST       (1<<1)
+#define SIE_SE_STP      (1<<2)
+#define SIE_SE_PO       (1<<3)
+#define SIE_SE_EPN      (1<<4)
+#define SIE_SE_B_1_FULL (1<<5)
+#define SIE_SE_B_2_FULL (1<<6)
+
+/* Set Endpoint Status command */
+#define SIE_SES_ST      (1<<0)
+#define SIE_SES_DA      (1<<5)
+#define SIE_SES_RF_MO   (1<<6)
+#define SIE_SES_CND_ST  (1<<7)
+
+usbdc *usbdc::instance;
+
+usbdc::usbdc()
+{
+#ifdef TARGET_LPC1768
+    LPC_SC->USBCLKCFG = 5; /* TODO */
+#endif
+
+    /* Enable power to USB device controller */
+    LPC_SC->PCONP |= PCUSB;
+
+    /* Enable USB clocks */
+    LPC_USB->USBClkCtrl |= DEV_CLK_EN | AHB_CLK_EN;
+    while (LPC_USB->USBClkSt != (DEV_CLK_ON | AHB_CLK_ON));
+
+    /* Configure pins P0.29 and P0.30 to be USB D+ and USB D- */
+    LPC_PINCON->PINSEL1 &= 0xc3ffffff;
+    LPC_PINCON->PINSEL1 |= 0x14000000;
+
+#ifdef ENABLE_VBUS
+    /* Configure pin P1.30 to be VBUS */
+    LPC_PINCON->PINSEL3 &= 0xcfffffff;
+    LPC_PINCON->PINSEL3 |= 0x20000000;
+
+    /* Configure pin P1.30 to have pull-down */
+    LPC_PINCON->PINMODE3 |= 0x30000000;
+#endif
+
+    /* Configure pin P2.9 to be Connect */
+    LPC_PINCON->PINSEL4 &= 0xfffcffff;
+    LPC_PINCON->PINSEL4 |= 0x00040000;
+
+    /* Connect must be low for at least 2.5uS */
+    wait_ms(1);
+
+    /* Attach IRQ */
+    instance = this;
+    NVIC_SetVector(USB_IRQn, (uint32_t)&_usbisr);
+    NVIC_EnableIRQ(USB_IRQn);
+
+    /* Enable device interrupts */
+    enableEvents();
+}
+
+void usbdc::connect(void)
+{
+    /* Connect USB device */
+    unsigned char status;
+
+    status = getDeviceStatus();
+    setDeviceStatus(status | SIE_DS_CON);
+}
+
+void usbdc::disconnect(void)
+{
+    /* Disconnect USB device */
+    unsigned char status;
+
+    status = getDeviceStatus();
+    setDeviceStatus(status & ~SIE_DS_CON);
+}
+
+void usbdc::SIECommand(unsigned long command)
+{
+    /* The command phase of a SIE transaction */
+    LPC_USB->USBDevIntClr = CCEMPTY;
+    LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_COMMAND, command);
+    while (!(LPC_USB->USBDevIntSt & CCEMPTY));
+}
+
+void usbdc::SIEWriteData(unsigned char data)
+{
+    /* The data write phase of a SIE transaction */
+    LPC_USB->USBDevIntClr = CCEMPTY;
+    LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_WRITE, data);
+    while (!(LPC_USB->USBDevIntSt & CCEMPTY));
+}
+
+unsigned char usbdc::SIEReadData(unsigned long command)
+{
+    /* The data read phase of a SIE transaction */
+    LPC_USB->USBDevIntClr = CDFULL;
+    LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_READ, command);
+    while (!(LPC_USB->USBDevIntSt & CDFULL));
+    return (unsigned char)LPC_USB->USBCmdData;
+}
+
+void usbdc::setDeviceStatus(unsigned char status)
+{
+    /* Write SIE device status register */
+    SIECommand(SIE_CMD_SET_DEVICE_STATUS);
+    SIEWriteData(status);
+}
+
+unsigned char usbdc::getDeviceStatus(void)
+{
+    /* Read SIE device status register */
+    SIECommand(SIE_CMD_GET_DEVICE_STATUS);
+    return SIEReadData(SIE_CMD_GET_DEVICE_STATUS);
+}
+
+void usbdc::setAddress(unsigned char address)
+{
+    /* Write SIE device address register */
+    SIECommand(SIE_CMD_SET_ADDRESS);
+    SIEWriteData((address & 0x7f) | SIE_DSA_DEV_EN);
+}
+
+unsigned char usbdc::selectEndpoint(unsigned char endpoint)
+{
+    /* SIE select endpoint command */
+    SIECommand(SIE_CMD_SELECT_ENDPOINT(endpoint));
+    return SIEReadData(SIE_CMD_SELECT_ENDPOINT(endpoint));
+}
+
+#if 1
+unsigned char usbdc::selectEndpointClearInterrupt(unsigned char endpoint)
+{
+    /* SIE select endpoint and clear interrupt command */
+    /* Using the Select Endpoint / Clear Interrupt SIE command does not seem   */
+    /* to clear the appropriate bit in EP_INT_STAT? - using EP_INT_CLR instead */
+    LPC_USB->USBEpIntClr = EP(endpoint);
+    while (!(LPC_USB->USBDevIntSt & CDFULL));
+    return (unsigned char)LPC_USB->USBCmdData;
+}
+#else
+unsigned char usbdc::selectEndpointClearInterrupt(unsigned char endpoint)
+{
+    /* SIE select endpoint and clear interrupt command */
+    SIECommand(SIE_CMD_SELECT_ENDPOINT_CLEAR_INTERRUPT(endpoint));
+    return SIEReadData(SIE_CMD_SELECT_ENDPOINT_CLEAR_INTERRUPT(endpoint));
+}
+#endif
+
+unsigned char usbdc::clearBuffer(void)
+{
+    /* SIE clear buffer command */
+    SIECommand(SIE_CMD_CLEAR_BUFFER);
+    return SIEReadData(SIE_CMD_CLEAR_BUFFER);
+}
+
+void usbdc::validateBuffer(void)
+{
+    /* SIE validate buffer command */
+    SIECommand(SIE_CMD_VALIDATE_BUFFER);
+}
+
+void usbdc::setEndpointStatus(unsigned char endpoint, unsigned char status)
+{
+    /* SIE set endpoint status command */
+    SIECommand(SIE_CMD_SET_ENDPOINT_STATUS(endpoint));
+    SIEWriteData(status);
+}
+
+void usbdc::realiseEndpoint(unsigned char endpoint, unsigned long maxPacket)
+{
+    /* Realise an endpoint */
+    LPC_USB->USBDevIntClr = EP_RLZED;
+    LPC_USB->USBReEp |= EP(endpoint);
+    LPC_USB->USBEpInd = endpoint;
+    LPC_USB->USBMaxPSize = maxPacket;
+
+    while (!(LPC_USB->USBDevIntSt & EP_RLZED));
+    LPC_USB->USBDevIntClr = EP_RLZED;
+
+    /* Clear stall state */
+    endpointStallState &= ~EP(endpoint);
+}
+
+void usbdc::enableEndpointEvent(unsigned char endpoint)
+{
+    /* Enable an endpoint interrupt */
+    LPC_USB->USBEpIntEn |= EP(endpoint);
+}
+
+void usbdc::disableEndpointEvent(unsigned char endpoint)
+{
+    /* Disable an endpoint interrupt */
+    LPC_USB->USBEpIntEn &= ~EP(endpoint);
+}
+
+void usbdc::stallEndpoint(unsigned char endpoint)
+{
+    /* Stall an endpoint */
+    if ( (endpoint==EP0IN) || (endpoint==EP0OUT) )
+    {
+        /* Conditionally stall both control endpoints */
+        setEndpointStatus(EP0OUT, SIE_SES_CND_ST);
+    }
+    else
+    {
+        setEndpointStatus(endpoint, SIE_SES_ST);
+
+        /* Update stall state */
+        endpointStallState |= EP(endpoint);
+    }
+}
+
+void usbdc::unstallEndpoint(unsigned char endpoint)
+{
+    /* Unstall an endpoint. The endpoint will also be reinitialised */
+    setEndpointStatus(endpoint, 0);
+
+    /* Update stall state */
+    endpointStallState &= ~EP(endpoint);
+}
+
+bool usbdc::getEndpointStallState(unsigned char endpoint)
+{
+    /* Returns true if endpoint stalled */
+    return endpointStallState & EP(endpoint);
+}
+
+void usbdc::configureDevice(void)
+{
+    /* SIE Configure device command */
+    SIECommand(SIE_CMD_CONFIGURE_DEVICE);
+    SIEWriteData(SIE_CONF_DEVICE);
+}
+
+void usbdc::unconfigureDevice(void)
+{
+    /* SIE Configure device command */
+    SIECommand(SIE_CMD_CONFIGURE_DEVICE);
+    SIEWriteData(0);
+}
+
+unsigned long usbdc::endpointRead(unsigned char endpoint, unsigned char *buffer)
+{
+    /* Read from an OUT endpoint */
+    unsigned long size;
+    unsigned long i;
+    unsigned long data;
+    unsigned char offset;
+
+    LPC_USB->USBCtrl = LOG_ENDPOINT(endpoint) | RD_EN;
+    while (!(LPC_USB->USBRxPLen & PKT_RDY));
+
+    size = LPC_USB->USBRxPLen & PKT_LNGTH_MASK;
+
+    offset = 0;
+
+    for (i=0; i<size; i++)
+    {
+        if (offset==0)
+        {
+            /* Fetch up to four bytes of data as a word */
+            data = LPC_USB->USBRxData;
+        }
+
+        /* extract a byte */
+        *buffer++ = data>>offset;
+
+        /* move on to the next byte */
+        offset = (offset + 8) % 32;
+    }
+
+    /* Clear RD_EN to cover zero length packet case */
+    LPC_USB->USBCtrl=0;
+
+    selectEndpoint(endpoint);
+    clearBuffer();
+
+    return size;
+}
+
+void usbdc::endpointWrite(unsigned char endpoint, unsigned char *buffer, unsigned long size)
+{
+    /* Write to an IN endpoint */
+    unsigned long temp, data;
+    unsigned char offset;
+
+    LPC_USB->USBCtrl = LOG_ENDPOINT(endpoint) | WR_EN;
+
+    LPC_USB->USBTxPLen = size;
+    offset = 0;
+    data = 0;
+
+    if (size>0)
+    {
+        do
+        {
+            /* Fetch next data byte into a word-sized temporary variable */
+            temp = *buffer++;
+
+            /* Add to current data word */
+            temp = temp << offset;
+            data = data | temp;
+
+            /* move on to the next byte */
+            offset = (offset + 8) % 32;
+            size--;
+
+            if ((offset==0) || (size==0))
+            {
+                /* Write the word to the endpoint */
+                LPC_USB->USBTxData = data;
+                data = 0;
+            }
+        } while (size>0);
+    }
+
+    /* Clear WR_EN to cover zero length packet case */
+    LPC_USB->USBCtrl=0;
+
+    selectEndpoint(endpoint);
+    validateBuffer();
+}
+
+void usbdc::enableEvents(void)
+{
+    /* Enable interrupt sources */
+    LPC_USB->USBDevIntEn = EP_SLOW | DEV_STAT;
+}
+
+void usbdc::disableEvents(void)
+{
+    /* Disable interrupt sources */
+    LPC_USB->USBDevIntClr = EP_SLOW | DEV_STAT;
+}
+
+void usbdc::usbisr(void)
+{
+    unsigned char devStat;
+
+    if (LPC_USB->USBDevIntSt & FRAME)
+    {
+        /* Frame event */
+        deviceEventFrame();
+        /* Clear interrupt status flag */
+        LPC_USB->USBDevIntClr = FRAME;
+    }
+
+    if (LPC_USB->USBDevIntSt & DEV_STAT)
+    {
+        /* Device Status interrupt */
+        /* Must clear the interrupt status flag before reading the device status from the SIE */
+        LPC_USB->USBDevIntClr = DEV_STAT;
+
+        /* Read device status from SIE */
+        devStat = getDeviceStatus();
+
+        if (devStat & SIE_DS_RST)
+        {
+            /* Bus reset */
+            deviceEventReset();
+        }
+    }
+
+    if (LPC_USB->USBDevIntSt & EP_SLOW)
+    {
+        /* (Slow) Endpoint Interrupt */
+
+        /* Process each endpoint interrupt */
+        if (LPC_USB->USBEpIntSt & EP(EP0OUT))
+        {
+            if (selectEndpointClearInterrupt(EP0OUT) & SIE_SE_STP)
+            {
+                /* this is a setup packet */
+                endpointEventEP0Setup();
+            }
+            else
+            {
+                endpointEventEP0Out();
+            }
+        }
+
+        if (LPC_USB->USBEpIntSt & EP(EP0IN))
+        {
+            selectEndpointClearInterrupt(EP0IN);
+            endpointEventEP0In();
+        }
+
+        if (LPC_USB->USBEpIntSt & EP(EP1OUT))
+        {
+            selectEndpointClearInterrupt(EP1OUT);
+            endpointEventEP1Out();
+        }
+
+        if (LPC_USB->USBEpIntSt & EP(EP1IN))
+        {
+            selectEndpointClearInterrupt(EP1IN);
+            endpointEventEP1In();
+        }
+
+        if (LPC_USB->USBEpIntSt & EP(EP2OUT))
+        {
+            selectEndpointClearInterrupt(EP2OUT);
+            endpointEventEP2Out();
+        }
+
+        if (LPC_USB->USBEpIntSt & EP(EP2IN))
+        {
+            selectEndpointClearInterrupt(EP2IN);
+            endpointEventEP2In();
+        }
+
+        /* Clear interrupt status flag */
+        /* EP_SLOW and EP_FAST interrupt bits should be cleared after the corresponding endpoint interrupts are cleared. */
+        LPC_USB->USBDevIntClr = EP_SLOW;
+    }
+}
+
+
+void usbdc::_usbisr(void)
+{
+    instance->usbisr();
+}
+
+void usbdc::deviceEventReset(void)
+{
+}
+
+void usbdc::deviceEventFrame(void)
+{
+}
+
+void usbdc::endpointEventEP0Setup(void)
+{
+}
+
+void usbdc::endpointEventEP0In(void)
+{
+}
+
+void usbdc::endpointEventEP0Out(void)
+{
+}
+
+void usbdc::endpointEventEP1In(void)
+{
+}
+
+void usbdc::endpointEventEP1Out(void)
+{
+}
+
+void usbdc::endpointEventEP2In(void)
+{
+}
+
+void usbdc::endpointEventEP2Out(void)
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbdc.h	Fri Dec 16 18:17:42 2016 +0000
@@ -0,0 +1,64 @@
+/* usbdc.h */
+/* USB device controller */
+/* Copyright (c) Phil Wright 2008 */
+
+#ifndef USBDC_H
+#define USBDC_H
+
+/* Endpoints */
+#define EP0OUT  (0) /* Control */
+#define EP0IN   (1) /* Control */
+#define EP1OUT  (2) /* Interrupt */
+#define EP1IN   (3) /* Interrupt */
+#define EP2OUT  (4) /* Bulk */
+#define EP2IN   (5) /* Bulk */
+
+#include "mbed.h"
+
+class usbdc //: public Base
+{
+public:
+    usbdc();
+    void connect(void);
+    void disconnect(void);
+protected:
+    void setAddress(unsigned char address);
+    void realiseEndpoint(unsigned char endpoint, unsigned long maxPacket);
+    void enableEndpointEvent(unsigned char endpoint);
+    void disableEndpointEvent(unsigned char endpoint);
+    void stallEndpoint(unsigned char endpoint);
+    void unstallEndpoint(unsigned char endpoint);
+    bool getEndpointStallState(unsigned char endpoint);
+    void configureDevice(void);
+    void unconfigureDevice(void);
+    unsigned long endpointRead(unsigned char endpoint, unsigned char *buffer);
+    void endpointWrite(unsigned char endpoint, unsigned char *buffer, unsigned long size);
+    void enableEvents(void);
+    void disableEvents(void);
+    virtual void deviceEventReset(void);
+    virtual void deviceEventFrame(void);
+    virtual void endpointEventEP0Setup(void);
+    virtual void endpointEventEP0In(void);
+    virtual void endpointEventEP0Out(void);
+    virtual void endpointEventEP1In(void);
+    virtual void endpointEventEP1Out(void);
+    virtual void endpointEventEP2In(void);
+    virtual void endpointEventEP2Out(void);
+private:
+    void SIECommand(unsigned long command);
+    void SIEWriteData(unsigned char data);
+    unsigned char SIEReadData(unsigned long command);
+    void setDeviceStatus(unsigned char status);
+    void setEndpointStatus(unsigned char endpoint, unsigned char status);
+    unsigned char getDeviceStatus(void);
+    unsigned char selectEndpoint(unsigned char endpoint);
+    unsigned char selectEndpointClearInterrupt(unsigned char endpoint);
+    unsigned char clearBuffer(void);
+    void validateBuffer(void);
+    void usbisr(void);
+    unsigned long endpointStallState;
+    static void _usbisr(void);
+    static usbdc *instance;
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbdevice.cpp	Fri Dec 16 18:17:42 2016 +0000
@@ -0,0 +1,495 @@
+/* usbdevice.cpp */
+/* Generic USB device */
+/* Copyright (c) Phil Wright 2008 */
+
+#include "mbed.h"
+#include "usbdevice.h"
+
+/* Standard requests */
+#define GET_STATUS        (0)
+#define CLEAR_FEATURE     (1)
+#define SET_FEATURE       (3)
+#define SET_ADDRESS       (5)
+#define GET_DESCRIPTOR    (6)
+#define SET_DESCRIPTOR    (7)
+#define GET_CONFIGURATION (8)
+#define SET_CONFIGURATION (9)
+#define GET_INTERFACE     (10)
+#define SET_INTERFACE     (11)
+
+/* Device status */
+#define DEVICE_STATUS_SELF_POWERED  (1<<0)
+#define DEVICE_STATUS_REMOTE_WAKEUP (1<<1)
+
+/* Endpoint status */
+#define ENDPOINT_STATUS_HALT        (1<<0)
+
+/* Standard feature selectors */
+#define DEVICE_REMOTE_WAKEUP        (1)
+#define ENDPOINT_HALT               (0)
+
+/* Macro to convert wIndex endpoint number to physical endpoint number */
+#define WINDEX_TO_PHYSICAL(endpoint) (((endpoint & 0x0f) << 1) + ((endpoint & 0x80) ? 1 : 0))
+
+CONTROL_TRANSFER transfer;
+USB_DEVICE device;
+
+usbdevice::usbdevice()
+{
+    /* Set initial device state */
+    device.state = POWERED;
+    device.configuration = 0;
+    device.suspended = false;
+
+    /* Set the maximum packet size for the control endpoints */
+    realiseEndpoint(EP0IN, MAX_PACKET_SIZE_EP0);
+    realiseEndpoint(EP0OUT, MAX_PACKET_SIZE_EP0);
+
+    /* Enable endpoint events for EP0 */
+    enableEndpointEvent(EP0IN);
+    enableEndpointEvent(EP0OUT);
+}
+
+void usbdevice::endpointEventEP0Setup()
+{
+    /* Endpoint 0 setup event */
+    if (!controlSetup())
+    {
+        /* Protocol stall; this will stall both endpoints */
+        stallEndpoint(EP0OUT);
+    }
+}
+
+void usbdevice::endpointEventEP0Out()
+{
+    /* Endpoint 0 OUT data event */
+    if (!controlOut())
+    {
+        /* Protocol stall; this will stall both endpoints */
+        stallEndpoint(EP0OUT);
+    }
+}
+
+void usbdevice::endpointEventEP0In()
+{
+    /* Endpoint 0 IN data event */
+    if (!controlIn())
+    {
+        /* Protocol stall; this will stall both endpoints */
+        stallEndpoint(EP0OUT);
+    }
+}
+
+void usbdevice::deviceEventReset()
+{
+    device.state = DEFAULT;
+    device.configuration = 0;
+    device.suspended = false;
+}
+
+void usbdevice::decodeSetupPacket(unsigned char *data, SETUP_PACKET *packet)
+{
+    /* Fill in the elements of a SETUP_PACKET structure from raw data */
+    packet->bmRequestType.dataTransferDirection = (data[0] & 0x80) >> 7;
+    packet->bmRequestType.Type = (data[0] & 0x60) >> 5;
+    packet->bmRequestType.Recipient = data[0] & 0x1f;
+    packet->bRequest = data[1];
+    packet->wValue = (data[2] | (unsigned short)data[3] << 8);
+    packet->wIndex = (data[4] | (unsigned short)data[5] << 8);
+    packet->wLength = (data[6] | (unsigned short)data[7] << 8);
+}
+
+bool usbdevice::controlSetup()
+{
+    /* Control transfer setup stage */
+    unsigned char buffer[MAX_PACKET_SIZE_EP0];
+    unsigned long count = endpointRead(EP0OUT, buffer);
+
+    /* Must be 8 bytes of data */
+    if (count != 8)
+        return false;
+
+    /* Initialise control transfer state */
+    decodeSetupPacket(buffer, &transfer.setup);
+    transfer.ptr = NULL;
+    transfer.remaining = 0;
+    transfer.direction = 0;
+    transfer.zlp = false;
+
+    /* Process request */
+    if (!requestSetup())
+        return false;
+
+    /* Check transfer size and direction  */
+    if (transfer.setup.wLength > 0)
+    {
+        if (transfer.setup.bmRequestType.dataTransferDirection==DEVICE_TO_HOST)
+        {
+            /* IN data stage is required */
+            if (transfer.direction != DEVICE_TO_HOST)
+                return false;
+
+            /* Transfer must be less than or equal to the size requested by the host */
+            if (transfer.remaining > transfer.setup.wLength)
+                transfer.remaining = transfer.setup.wLength;
+        }
+        else
+        {
+            /* OUT data stage is required */
+            if (transfer.direction != HOST_TO_DEVICE)
+                return false;
+
+            /* Transfer must be equal to the size requested by the host */
+            if (transfer.remaining != transfer.setup.wLength)
+                return false;
+        }
+    }
+    else
+    {
+        /* No data stage; transfer size must be zero */
+        if (transfer.remaining != 0)
+            return false;
+    }
+
+    /* Data or status stage if applicable */
+    if (transfer.setup.wLength > 0)
+    {
+        if (transfer.setup.bmRequestType.dataTransferDirection == DEVICE_TO_HOST)
+        {
+            /* Check if we'll need to send a zero length packet at the end of this transfer */
+            if (transfer.setup.wLength > transfer.remaining)
+            {
+                /* Device wishes to transfer less than host requested */
+                if ((transfer.remaining % MAX_PACKET_SIZE_EP0) == 0)
+                {
+                    /* Transfer is a multiple of EP0 max packet size */
+                    transfer.zlp = true;
+                }
+            }
+
+            /* IN stage */
+            controlIn();
+        }
+    }
+    else
+    {
+        /* Status stage */
+        endpointWrite(EP0IN, NULL, 0);
+    }
+
+    return true;
+}
+
+bool usbdevice::controlOut()
+{
+    /* Control transfer data OUT stage */
+    unsigned char buffer[MAX_PACKET_SIZE_EP0];
+    unsigned long packetSize;
+
+    /* Check we should be transferring data OUT */
+    if (transfer.direction != HOST_TO_DEVICE)
+        return false;
+
+    /* Read from endpoint */
+    packetSize = endpointRead(EP0OUT, buffer);
+
+    /* Check if transfer size is valid */
+    if (packetSize > transfer.remaining)
+    {
+        /* Too big */
+        return false;
+    }
+
+    /* Update transfer */
+    transfer.ptr += packetSize;
+    transfer.remaining -= packetSize;
+
+    /* Check if transfer has completed */
+    if (transfer.remaining == 0)
+    {
+        /* Process request */
+        if (!requestOut())
+            return false;
+
+        /* Status stage */
+        endpointWrite(EP0IN, NULL, 0);
+    }
+
+    return true;
+}
+
+bool usbdevice::controlIn()
+{
+    /* Control transfer data IN stage */
+    unsigned packetSize;
+
+    /* Check if transfer has completed (status stage transactions also have transfer.remaining == 0) */
+    if (transfer.remaining == 0)
+    {
+        if (transfer.zlp)
+        {
+            /* Send zero length packet */
+            endpointWrite(EP0IN, NULL, 0);
+            transfer.zlp = false;
+        }
+
+        /* Completed */
+        return true;
+    }
+
+    /* Check we should be transferring data IN */
+    if (transfer.direction != DEVICE_TO_HOST)
+        return false;
+
+    packetSize = transfer.remaining;
+    if (packetSize > MAX_PACKET_SIZE_EP0)
+        packetSize = MAX_PACKET_SIZE_EP0;
+
+    /* Write to endpoint */
+    endpointWrite(EP0IN, transfer.ptr, packetSize);
+
+    /* Update transfer */
+    transfer.ptr += packetSize;
+    transfer.remaining -= packetSize;
+
+    return true;
+}
+
+bool usbdevice::requestSetup()
+{
+    bool success = false;
+
+    /* Process standard requests */
+    if ((transfer.setup.bmRequestType.Type == STANDARD_TYPE))
+    {
+        switch (transfer.setup.bRequest)
+        {
+             case GET_STATUS:
+                 success = requestGetStatus();
+                 break;
+             case CLEAR_FEATURE:
+                 success = requestClearFeature();
+                 break;
+             case SET_FEATURE:
+                 success = requestSetFeature();
+                 break;
+             case SET_ADDRESS:
+                success = requestSetAddress();
+                 break;
+             case GET_DESCRIPTOR:
+                 success = requestGetDescriptor();
+                 break;
+             case SET_DESCRIPTOR:
+                 /* TODO: Support is optional, not implemented here */
+                 success = false;
+                 break;
+             case GET_CONFIGURATION:
+                 success = requestGetConfiguration();
+                 break;
+             case SET_CONFIGURATION:
+                 success = requestSetConfiguration();
+                 break;
+             case GET_INTERFACE:
+                 success = requestGetInterface();
+                 break;
+             case SET_INTERFACE:
+                 success = requestSetInterface();
+                 break;
+             default:
+                 break;
+        }
+    }
+
+    return success;
+}
+
+bool usbdevice::requestOut()
+{
+    return true;
+}
+
+bool usbdevice::requestSetAddress()
+{
+    /* Set the device address */
+    setAddress(transfer.setup.wValue);
+
+    if (transfer.setup.wValue == 0)
+        device.state = DEFAULT;
+    else
+        device.state = ADDRESS;
+
+    return true;
+}
+
+bool usbdevice::requestSetConfiguration()
+{
+    /* Set the device configuration */
+    if (transfer.setup.wValue == 0)
+    {
+        /* Not configured */
+        unconfigureDevice();
+        device.state = ADDRESS;
+    }
+    else
+    {
+        configureDevice();
+        device.state = CONFIGURED;
+    }
+
+    /* TODO: We do not currently support multiple configurations, just keep a record of the configuration value */
+    device.configuration = transfer.setup.wValue;
+
+    return true;
+}
+
+bool usbdevice::requestGetConfiguration()
+{
+    /* Send the device configuration */
+    transfer.ptr = &device.configuration;
+    transfer.remaining = sizeof(device.configuration);
+    transfer.direction = DEVICE_TO_HOST;
+    return true;
+}
+
+bool usbdevice::requestGetInterface()
+{
+    static unsigned char alternateSetting;
+
+    /* Return the selected alternate setting for an interface */
+
+    if (device.state != CONFIGURED)
+    {
+        return false;
+    }
+
+    /* TODO: We currently do not support alternate settings so always return zero */
+    /* TODO: Should check that the interface number is valid */
+    alternateSetting = 0;
+
+    /* Send the alternate setting */
+    transfer.ptr = &alternateSetting;
+    transfer.remaining = sizeof(alternateSetting);
+    transfer.direction = DEVICE_TO_HOST;
+    return true;
+}
+
+bool usbdevice::requestSetInterface()
+{
+    /* TODO: We currently do not support alternate settings, return false */
+    return false;
+}
+
+bool usbdevice::requestSetFeature()
+{
+    bool success = false;
+
+    if (device.state != CONFIGURED)
+    {
+        /* Endpoint or interface must be zero */
+        if (transfer.setup.wIndex != 0)
+            return false;
+    }
+
+    switch (transfer.setup.bmRequestType.Recipient)
+    {
+        case DEVICE_RECIPIENT:
+            /* TODO: Remote wakeup feature not supported */
+            break;
+        case ENDPOINT_RECIPIENT:
+            if (transfer.setup.wValue == ENDPOINT_HALT)
+            {
+                /* TODO: We should check that the endpoint number is valid */
+                stallEndpoint(WINDEX_TO_PHYSICAL(transfer.setup.wIndex));
+                success = true;
+            }
+            break;
+        default:
+            break;
+    }
+
+    return success;
+}
+
+bool usbdevice::requestClearFeature()
+{
+    bool success = false;
+
+    if (device.state != CONFIGURED)
+    {
+        /* Endpoint or interface must be zero */
+        if (transfer.setup.wIndex != 0)
+            return false;
+    }
+
+    switch (transfer.setup.bmRequestType.Recipient)
+    {
+        case DEVICE_RECIPIENT:
+            /* TODO: Remote wakeup feature not supported */
+            break;
+        case ENDPOINT_RECIPIENT:
+            /* TODO: We should check that the endpoint number is valid */
+            if (transfer.setup.wValue == ENDPOINT_HALT)
+            {
+                unstallEndpoint(WINDEX_TO_PHYSICAL(transfer.setup.wIndex));
+                success = true;
+            }
+            break;
+        default:
+            break;
+    }
+
+    return success;
+}
+
+bool usbdevice::requestGetStatus()
+{
+    static unsigned short status;
+    bool success = false;
+
+    if (device.state != CONFIGURED)
+    {
+        /* Endpoint or interface must be zero */
+        if (transfer.setup.wIndex != 0)
+        {
+            return false;
+        }
+    }
+
+    switch (transfer.setup.bmRequestType.Recipient)
+    {
+        case DEVICE_RECIPIENT:
+            /* TODO: Currently only supports self powered devices */
+            status = DEVICE_STATUS_SELF_POWERED;
+            success = true;
+            break;
+        case INTERFACE_RECIPIENT:
+            status = 0;
+            success = true;
+            break;
+        case ENDPOINT_RECIPIENT:
+            /* TODO: We should check that the endpoint number is valid */
+            if (getEndpointStallState(WINDEX_TO_PHYSICAL(transfer.setup.wIndex)))
+                status = ENDPOINT_STATUS_HALT;
+            else
+                status = 0;
+            success = true;
+            break;
+        default:
+            break;
+    }
+
+    if (success)
+    {
+        /* Send the status */
+        transfer.ptr = (unsigned char *)&status; /* Assumes little endian */
+        transfer.remaining = sizeof(status);
+        transfer.direction = DEVICE_TO_HOST;
+    }
+
+    return success;
+}
+
+bool usbdevice::requestGetDescriptor()
+{
+    return false;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbdevice.h	Fri Dec 16 18:17:42 2016 +0000
@@ -0,0 +1,101 @@
+/* usbdevice.h */
+/* Generic USB device */
+/* Copyright (c) Phil Wright 2008 */
+
+#ifndef USBDEVICE_H
+#define USBDEVICE_H
+
+#include "usbdc.h"
+
+/* Endpoint packet sizes */
+#define MAX_PACKET_SIZE_EP0 (64)
+
+/* bmRequestType.dataTransferDirection */
+#define HOST_TO_DEVICE (0)
+#define DEVICE_TO_HOST (1)
+
+/* bmRequestType.Type*/
+#define STANDARD_TYPE  (0)
+#define CLASS_TYPE     (1)
+#define VENDOR_TYPE    (2)
+#define RESERVED_TYPE  (3)
+
+/* bmRequestType.Recipient */
+#define DEVICE_RECIPIENT    (0)
+#define INTERFACE_RECIPIENT (1)
+#define ENDPOINT_RECIPIENT  (2)
+#define OTHER_RECIPIENT     (3)
+
+/* Descriptors */
+#define DESCRIPTOR_TYPE(wValue)  (wValue >> 8)
+#define DESCRIPTOR_INDEX(wValue) (wValue & 0xf)
+
+/* Descriptor type */
+#define DEVICE_DESCRIPTOR        (1)
+#define CONFIGURATION_DESCRIPTOR (2)
+#define STRING_DESCRIPTOR        (3)
+#define INTERFACE_DESCRIPTOR     (4)
+#define ENDPOINT_DESCRIPTOR      (5)
+
+typedef struct
+{
+    struct
+    {
+        unsigned char dataTransferDirection;
+        unsigned char Type;
+        unsigned char Recipient;
+    } bmRequestType;
+    unsigned char  bRequest;
+    unsigned short wValue;
+    unsigned short wIndex;
+    unsigned short wLength;
+} SETUP_PACKET;
+
+typedef struct
+{
+    SETUP_PACKET  setup;
+    unsigned char *ptr;
+    unsigned long remaining;
+    unsigned char direction;
+    bool          zlp;
+} CONTROL_TRANSFER;
+
+typedef enum { ATTACHED, POWERED, DEFAULT, ADDRESS, CONFIGURED } DEVICE_STATE;
+
+typedef struct
+{
+    DEVICE_STATE  state;
+    unsigned char configuration;
+    bool          suspended;
+} USB_DEVICE;
+
+class usbdevice : public usbdc
+{
+public:
+    usbdevice();
+protected:
+    virtual void endpointEventEP0Setup();
+    virtual void endpointEventEP0In();
+    virtual void endpointEventEP0Out();
+    virtual bool requestSetup();
+    virtual bool requestOut();
+    virtual void deviceEventReset();
+    virtual bool requestGetDescriptor();
+            bool requestSetAddress();
+    virtual bool requestSetConfiguration();
+    virtual bool requestGetConfiguration();
+            bool requestGetStatus();
+    virtual bool requestSetInterface();
+    virtual bool requestGetInterface();
+            bool requestSetFeature();
+            bool requestClearFeature();
+    CONTROL_TRANSFER transfer;
+    USB_DEVICE device;
+private:
+    bool controlIn();
+    bool controlOut();
+    bool controlSetup();
+    void decodeSetupPacket(unsigned char *data, SETUP_PACKET *packet);
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbhid.cpp	Fri Dec 16 18:17:42 2016 +0000
@@ -0,0 +1,314 @@
+/* usbhid.cpp */
+/* USB HID class device */
+/* Copyright (c) Phil Wright 2008 */
+
+/* Modified by yours truly */
+
+#include "mbed.h"
+#include "usbhid.h"
+
+/* Report for 2 arcade joysticks */
+static uint8_t report_descriptor_joystick[] =
+{
+0x05, 0x01, // USAGE_PAGE (Generic Desktop)
+0x09, 0x05, // USAGE (Gamepad)
+0xa1, 0x01, // COLLECTION (Application)
+
+0xa1, 0x00, // COLLECTION (Physical)
+0x85, 0x01, // REPORT ID 1
+0x15, 0x00, //   LOGICAL_MINIMUM (0)
+0x25, 0x01, //   LOGICAL_MAXIMUM (1)
+0x35, 0x00, //   PHYSICAL_MINIMUM (0)
+0x45, 0x01, //   PHYSICAL_MAXIMUM (1)
+0x75, 0x01, //   REPORT_SIZE (1)
+0x95, 0x08, //   REPORT_COUNT (8)
+0x05, 0x09, //   USAGE_PAGE (Button)
+0x19, 0x01, //   USAGE_MINIMUM (Button 1)
+0x29, 0x08, //   USAGE_MAXIMUM (Button 8)
+0x81, 0x02, //   INPUT (Data,Var,Abs)
+0x05, 0x01, //   USAGE_PAGE (Generic Desktop)
+0x25, 0x07, //   LOGICAL_MAXIMUM (7)
+0x46, 0x3b, 0x01, //   PHYSICAL_MAXIMUM (315)
+0x75, 0x04, //   REPORT_SIZE (4)
+0x95, 0x01, //   REPORT_COUNT (1)
+0x65, 0x14, //   UNIT (Eng Rot:Angular Pos)
+0x09, 0x39, //   USAGE (Hat switch)
+0x81, 0x42, //   INPUT (Data,Var,Abs,Null)
+0x65, 0x00, //   UNIT (None)
+0x95, 0x01, //   REPORT_COUNT (1)
+0x81, 0x01, //   INPUT (Cnst,Ary,Abs)
+0x26, 0xff, 0x00, //   LOGICAL_MAXIMUM (255)
+0x46, 0xff, 0x00, //   PHYSICAL_MAXIMUM (255)
+
+/* For some reason, this is necessary (though we never put it in the report). */
+0x09, 0x30, //   USAGE (X)
+0x09, 0x31, //   USAGE (Y)
+0x75, 0x04, //   REPORT_SIZE (4)
+0x95, 0x02, //   REPORT_COUNT (2)
+0x81, 0x02, //   INPUT (Data,Var,Abs)
+
+0xc0, // END_COLLECTION -- physical
+
+/* second report */
+0xa1, 0x00, // COLLECTION (Physical)
+0x85, 0x02, // REPORT ID 2
+0x15, 0x00, //   LOGICAL_MINIMUM (0)
+0x25, 0x01, //   LOGICAL_MAXIMUM (1)
+0x35, 0x00, //   PHYSICAL_MINIMUM (0)
+0x45, 0x01, //   PHYSICAL_MAXIMUM (1)
+
+0x75, 0x01, //   REPORT_SIZE (1)
+0x95, 0x08, //   REPORT_COUNT (8)
+0x05, 0x09, //   USAGE_PAGE (Button)
+0x19, 0x01, //   USAGE_MINIMUM (Button 1)
+0x29, 0x08, //   USAGE_MAXIMUM (Button 8)
+0x81, 0x02, //   INPUT (Data,Var,Abs)
+0x05, 0x01, //   USAGE_PAGE (Generic Desktop)
+0x25, 0x07, //   LOGICAL_MAXIMUM (7)
+
+0x46, 0x3b, 0x01, //   PHYSICAL_MAXIMUM (315)
+
+0x75, 0x04, //   REPORT_SIZE (4)
+0x95, 0x01, //   REPORT_COUNT (1)
+0x65, 0x14, //   UNIT (Eng Rot:Angular Pos)
+0x09, 0x39, //   USAGE (Hat switch)
+0x81, 0x42, //   INPUT (Data,Var,Abs,Null)
+0x65, 0x00, //   UNIT (None)
+#if 0 /* For some reason, this is not necessary! */
+0x95, 0x01, //   REPORT_COUNT (1)
+0x81, 0x01, //   INPUT (Cnst,Ary,Abs)
+0x26, 0xff, 0x00, //   LOGICAL_MAXIMUM (255)
+0x46, 0xff, 0x00, //   PHYSICAL_MAXIMUM (255)
+0x09, 0x30, //   USAGE (X)
+0x09, 0x31, //   USAGE (Y)
+0x75, 0x04, //   REPORT_SIZE (4)
+0x95, 0x02, //   REPORT_COUNT (2)
+0x81, 0x02, //   INPUT (Data,Var,Abs)
+#endif
+0xc0, // END_COLLECTION -- physical
+
+0xc0, // END_COLLECTION -- application
+};
+
+/* Endpoint packet sizes */
+#define MAX_PACKET_SIZE_EP1 64
+
+/* HID Class */
+#define HID_CLASS          3
+#define HID_SUBCLASS_NONE  0
+#define HID_PROTOCOL_NONE  0
+#define HID_DESCRIPTOR    33
+#define REPORT_DESCRIPTOR 34
+
+/* Descriptors */
+unsigned char deviceDescriptor[] =
+{
+    0x12,                    /* bLength */
+    DEVICE_DESCRIPTOR,       /* bDescriptorType */
+    0x10,                    /* bcdUSB (LSB) */
+    0x01,                    /* bcdUSB (MSB) */ // USB release 1.1
+    0x00,                    /* bDeviceClass */
+    0x00,                    /* bDeviceSubClass */
+    0x00,                    /* bDeviceprotocol */
+    MAX_PACKET_SIZE_EP0,     /* bMaxPacketSize0 */
+    0xFE,                    /* idVendor (LSB) */
+    0xCA,                    /* idVendor (MSB) */
+    0xBE,                    /* idProduct (LSB) */
+    0xBA,                    /* idProduct (MSB) */
+    0x01,                    /* bcdDevice (LSB) */
+    0x00,                    /* bcdDevice (MSB) */ // device version number
+    0x01,                    /* iManufacturer */ // string index (0 = none)
+    0x02,                    /* iProduct */ // string index
+    0x00,                    /* iSerialNumber */ // string index
+    0x01                     /* bNumConfigurations */
+};
+    
+unsigned char configurationDescriptor[] =
+{
+    0x09,                        /* bLength */
+    CONFIGURATION_DESCRIPTOR,    /* bDescriptorType */
+    0x00,                        /* wTotalLength (LSB), set later */
+    0x00,                        /* wTotalLength (MSB), set later */
+    0x01,                        /* bNumInterfaces */
+    0x01,                        /* bConfigurationValue */
+    0x00,                        /* iConfiguration */
+    0x80,                        /* bmAttributes */
+    0x32,                        /* bMaxPower */
+/*9*/
+    0x09,                        /* bLength */
+    INTERFACE_DESCRIPTOR,        /* bDescriptorType */
+    0x00,                        /* bInterfaceNumber */
+    0x00,                        /* bAlternateSetting */
+    0x01,                        /* bNumEndpoints */
+    HID_CLASS,                   /* bInterfaceClass */
+    HID_SUBCLASS_NONE,           /* bInterfaceSubClass */
+    HID_PROTOCOL_NONE,           /* bInterfaceProtocol */
+    0x00,                        /* iInterface */
+/*18*/
+    0x09,                        /* bLength */
+    HID_DESCRIPTOR,              /* bDescriptorType */
+    0x11,                        /* bcdHID (LSB) */
+    0x01,                        /* bcdHID (MSB) */
+    0x00,                        /* bCountryCode */
+    0x01,                        /* bNumDescriptors */
+    REPORT_DESCRIPTOR,           /* bDescriptorType */
+    sizeof(report_descriptor_joystick) & 0xff, /* wDescriptorLength (LSB) */
+    (sizeof(report_descriptor_joystick) >> 8) & 0xff, /* wDescriptorLength (MSB) */
+/*27*/
+    0x07,                        /* bLength */
+    ENDPOINT_DESCRIPTOR,         /* bDescriptorType */
+    0x81,                        /* bEndpointAddress */
+    0x03,                        /* bmAttributes */
+    MAX_PACKET_SIZE_EP1,         /* wMaxPacketSize (LSB) */
+    0x00,                        /* wMaxPacketSize (MSB) */
+    0x05                         /* bInterval */
+};
+
+static volatile bool complete;
+static volatile bool configured;
+
+/* */
+usbhid::usbhid()
+{
+    configured = false;
+    connect();
+}
+
+void usbhid::deviceEventReset()
+{
+    configured = false;
+
+    /* Must call base class */
+    usbdevice::deviceEventReset();
+}
+
+bool usbhid::requestSetConfiguration()
+{
+    bool result;
+    
+    /* Configure IN interrupt endpoint */
+    realiseEndpoint(EP1IN, MAX_PACKET_SIZE_EP1);
+    enableEndpointEvent(EP1IN);
+
+    /* Must call base class */
+    result = usbdevice::requestSetConfiguration();
+
+    if (result)
+    {
+        /* Now configured */
+        configured = true;
+    }
+
+    return result;
+}
+
+bool usbhid::requestGetDescriptor()
+{
+    bool success = false;
+
+    switch (DESCRIPTOR_TYPE(transfer.setup.wValue))
+    {
+        case DEVICE_DESCRIPTOR:
+            transfer.remaining = sizeof(deviceDescriptor);
+            transfer.ptr = deviceDescriptor;
+            transfer.direction = DEVICE_TO_HOST;
+            success = true;
+            break;
+        case CONFIGURATION_DESCRIPTOR:
+            transfer.remaining = sizeof(configurationDescriptor);
+            transfer.ptr = configurationDescriptor;
+            transfer.direction = DEVICE_TO_HOST;
+            success = true;
+            break;
+        case STRING_DESCRIPTOR:
+        case INTERFACE_DESCRIPTOR:
+        case ENDPOINT_DESCRIPTOR:
+            /* TODO: Support is optional, not implemented here */
+            break;
+        case HID_DESCRIPTOR:
+            transfer.remaining = configurationDescriptor[18];
+            transfer.ptr = configurationDescriptor + 18;
+            transfer.direction = DEVICE_TO_HOST;
+            success = true;
+            break;
+        // case REPORT_DESCRIPTOR: /* Handled in the derived class */
+        default:
+            break;
+    }
+
+    return success;
+}
+
+void usbhid::endpointEventEP1In()
+{
+    complete = true;
+}
+
+/* */
+USBJoystick::USBJoystick()
+{
+    const unsigned config_descriptor_size = sizeof(configurationDescriptor);
+    configurationDescriptor[2] = config_descriptor_size & 0xff;
+    configurationDescriptor[3] = (config_descriptor_size >> 8) & 0xff;
+}
+
+bool USBJoystick::requestGetDescriptor()
+{
+    switch(DESCRIPTOR_TYPE(transfer.setup.wValue))
+    {
+        case REPORT_DESCRIPTOR:
+            transfer.remaining = sizeof(report_descriptor_joystick);
+            transfer.ptr = report_descriptor_joystick;
+            transfer.direction = DEVICE_TO_HOST;
+            return true;
+        default:
+            return usbhid::requestGetDescriptor();
+    }
+}
+
+bool USBJoystick::update(unsigned char gamepad_id, unsigned char stick, unsigned char buttons)
+{
+    unsigned char hatswitch;
+    if (stick & JOYSTICK_UP)
+    {
+        hatswitch = 0;
+        if (stick & JOYSTICK_RIGHT) 
+            hatswitch = 1;
+        if (stick & JOYSTICK_LEFT) 
+            hatswitch = 7;
+    }
+    else
+    if (stick & JOYSTICK_RIGHT)
+    {
+        hatswitch = (stick & JOYSTICK_DOWN) ? 3 : 2;
+    }
+    else
+    if (stick & JOYSTICK_DOWN)
+    {
+        hatswitch = (stick & JOYSTICK_LEFT) ? 5 : 4;
+    }
+    else
+    if (stick & JOYSTICK_LEFT)
+        hatswitch = 6;
+    else
+        hatswitch = 0xf;
+
+    /* Prepare report */
+    unsigned char report[3];
+    report[0] = gamepad_id;
+    report[1] = buttons;
+    report[2] = hatswitch;
+
+    /* Block if not configured */
+    while(!configured) ;
+
+    /* Send report */
+    complete = false;
+    disableEvents();
+    endpointWrite(EP1IN, report, 3);
+    enableEvents();
+
+    /* Wait for completion */
+    while(!complete && configured) ;
+    return true;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbhid.h	Fri Dec 16 18:17:42 2016 +0000
@@ -0,0 +1,36 @@
+/* usbhid.h */
+/* USB HID class device */
+/* Copyright (c) Phil Wright 2008 */
+
+#ifndef USBHID_H
+#define USBHID_H
+
+#include "usbdevice.h"
+
+class usbhid : public usbdevice
+{
+public:
+    usbhid();
+
+protected:
+    virtual void deviceEventReset();
+    virtual void endpointEventEP1In();
+    virtual bool requestGetDescriptor();
+    virtual bool requestSetConfiguration();
+};
+
+#define JOYSTICK_UP    (1<<0)
+#define JOYSTICK_DOWN  (1<<1)
+#define JOYSTICK_LEFT  (1<<2)
+#define JOYSTICK_RIGHT (1<<3)
+
+class USBJoystick : public usbhid
+{
+public:
+    USBJoystick();
+    bool update(unsigned char gamepad_id, unsigned char stick, unsigned char buttons);
+protected:
+    virtual bool requestGetDescriptor();
+};
+
+#endif /* USBHID_H */
\ No newline at end of file