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

usbhid.cpp

Committer:
Alberto_Wino
Date:
2016-12-17
Revision:
3:f1a8ec4659f8
Parent:
2:84ea6e2fb4b6

File content as of revision 3:f1a8ec4659f8:

/* usbhid.cpp */
/* USB HID class device */
/* Copyright (c) Phil Wright 2008 */

/* Modified by yours truly. */

#include "config.h"
#include "mbed.h"
#include "usbhid.h"

#ifdef CONFIG_TWO_REPORTS
/* 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
};
#endif

#ifdef CONFIG_2ND_PAD_AS_BUTTONS
static uint8_t report_descriptor_joystick[] =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x05, // USAGE (Gamepad)
0xa1, 0x01, // COLLECTION (Application)

0xa1, 0x00, // COLLECTION (Physical)
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, 0x12, //   REPORT_COUNT (18)
0x05, 0x09, //   USAGE_PAGE (Button)
0x19, 0x01, //   USAGE_MINIMUM (Button 1)
0x29, 0x12, //   USAGE_MAXIMUM (Button 18)
0x81, 0x02, //   INPUT (Data,Var,Abs)
0x95, 0x06, //   REPORT_COUNT (6)
0x81, 0x01, //   INPUT (Cnst,Ary,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

0xc0, // END_COLLECTION -- application
};
#endif

#ifdef CONFIG_TWO_INTERFACES
static uint8_t report_descriptor_joystick[] =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x05, // USAGE (Gamepad)
0xa1, 0x01, // COLLECTION (Application)

0xa1, 0x00, // COLLECTION (Physical)
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
0xc0, // END_COLLECTION -- application
};
#endif



/* 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 */
};

#ifdef CONFIG_TWO_INTERFACES
unsigned char configurationDescriptor[] =
{
    0x09,                        /* bLength */
    CONFIGURATION_DESCRIPTOR,    /* bDescriptorType */
    0x00,                        /* wTotalLength (LSB), set later */
    0x00,                        /* wTotalLength (MSB), set later */
    0x02,                        /* 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 */
    
/* second interface */
    0x09,                        /* bLength */
    INTERFACE_DESCRIPTOR,        /* bDescriptorType */
    0x01,                        /* bInterfaceNumber */
    0x00,                        /* bAlternateSetting */
    0x01,                        /* bNumEndpoints */
    HID_CLASS,                   /* bInterfaceClass */
    HID_SUBCLASS_NONE,           /* bInterfaceSubClass */
    HID_PROTOCOL_NONE,           /* bInterfaceProtocol */
    0x00,                        /* iInterface */

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

    0x07,                        /* bLength */
    ENDPOINT_DESCRIPTOR,         /* bDescriptorType */
    0x82,                        /* bEndpointAddress */
    0x03,                        /* bmAttributes */
    MAX_PACKET_SIZE_EP1,         /* wMaxPacketSize (LSB) */
    0x00,                        /* wMaxPacketSize (MSB) */
    0x05                         /* bInterval */
};

#else    
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 */
};
#endif

static volatile bool complete;
#if CONFIG_TWO_INTERFACES
static volatile bool complete_ep2;
#endif
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);
    
#if CONFIG_TWO_INTERFACES
    /* Configure second IN interrupt endpoint */
    realiseEndpoint(EP2IN, MAX_PACKET_SIZE_EP1);
    enableEndpointEvent(EP2IN);
#endif

    /* 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;
}

void usbhid::endpointEventEP2In()
{
    complete_ep2 = 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(uint8_t gamepad_id, uint8_t stick, uint32_t 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 */
#if CONFIG_TWO_INTERFACES
    const uint8_t report_size = 2;
    unsigned char report[2];
    report[0] = buttons;
    report[1] = hatswitch;
#endif
 
#if CONFIG_TWO_REPORTS
    const uint8_t report_size = 3;
    unsigned char report[3];
    report[0] = gamepad_id;
    report[1] = buttons;
    report[2] = hatswitch;
#endif    
    
#if CONFIG_2ND_PAD_AS_BUTTONS
    const uint8_t report_size = 4;
    unsigned char report[4];
    report[0] = buttons & 0xff;
    report[1] = (buttons >> 8) & 0xff;
    report[2] = (buttons >> 16) & 0x03;
    report[3] = hatswitch;
#endif

    /* Block if not configured */
    while(!configured) ;

    /* Send report */
    
#if CONFIG_TWO_INTERFACES
    if (gamepad_id == 2)
        complete_ep2 = false;
    else
#endif
    complete = false;
    
    disableEvents();
#if CONFIG_TWO_INTERFACES
    if (gamepad_id == 2)
    {
        endpointWrite(EP2IN, report, report_size);
        enableEvents();
        while(!complete_ep2 && configured) ;
        return true;
    }
#endif        
    endpointWrite(EP1IN, report, report_size);
    enableEvents();

    /* Wait for completion */
    while(!complete && configured) ;
    return true;
}