N64 to USB HID interface

Dependencies:   mbed

USBHID/usbhid.cpp

Committer:
igor_m
Date:
2014-12-10
Revision:
5:eb93a4f91396
Parent:
0:547c5459faa6

File content as of revision 5:eb93a4f91396:

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

/* modified by Shinichiro Oba <http://mbed.org/users/bricklife/> */

#include "mbed.h"
#include "usbhid.h"
#include "asciihid.h"

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

/* Class requests */
#define GET_REPORT (0x1)
#define GET_IDLE   (0x2)
#define SET_REPORT (0x9)
#define SET_IDLE   (0xa)

/* Descriptors */
unsigned char deviceDescriptor[] = {
    0x12,                    /* bLength */
    DEVICE_DESCRIPTOR,       /* bDescriptorType */
    0x00,                    /* bcdUSB (LSB) */
    0x02,                    /* bcdUSB (MSB) */
    0x00,                    /* bDeviceClass */
    0x00,                    /* bDeviceSubClass */
    0x00,                    /* bDeviceprotocol */
    MAX_PACKET_SIZE_EP0,     /* bMaxPacketSize0 */
    0x28,                    /* idVendor (LSB) */
    0x0d,                    /* idVendor (MSB) */
    0x05,                    /* idProduct (LSB) */
    0x02,                    /* idProduct (MSB) */
    0x00,                    /* bcdDevice (LSB) */
    0x00,                    /* bcdDevice (MSB) */
    0x00,                    /* iManufacturer */
    0x00,                    /* iProduct */
    0x00,                    /* iSerialNumber */
    0x01                     /* bNumConfigurations */
};

unsigned char configurationDescriptor[] = {
    0x09,                        /* bLength */
    CONFIGURATION_DESCRIPTOR,    /* bDescriptorType */
    0x09+0x09+0x09+0x07,         /* wTotalLength (LSB) */
    0x00,                        /* wTotalLength (MSB) */
    0x01,                        /* bNumInterfaces */
    0x01,                        /* bConfigurationValue */
    0x00,                        /* iConfiguration */
    0xc0,                        /* bmAttributes */
    0x00,                        /* bMaxPower */

    0x09,                        /* bLength */
    INTERFACE_DESCRIPTOR,        /* bDescriptorType */
    0x00,                        /* 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 */
    0x79,                        /* wDescriptorLength (LSB) */
    0x00,                        /* wDescriptorLength (MSB) */

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

/* HID Class Report Descriptor */
/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes of data as per HID Class standard */

/* Main items */
#define INPUT(size)             (0x80 | size)
#define OUTPUT(size)            (0x90 | size)
#define FEATURE(size)           (0xb0 | size)
#define COLLECTION(size)        (0xa0 | size)
#define END_COLLECTION(size)    (0xc0 | size)

/* Global items */
#define USAGE_PAGE(size)        (0x04 | size)
#define LOGICAL_MIN(size)       (0x14 | size)
#define LOGICAL_MAX(size)       (0x24 | size)
#define PHYSICAL_MIN(size)      (0x34 | size)
#define PHYSICAL_MAX(size)      (0x44 | size)
#define UNIT_EXPONENT(size)     (0x54 | size)
#define UNIT(size)              (0x64 | size)
#define REPORT_SIZE(size)       (0x74 | size)
#define REPORT_ID(size)         (0x84 | size)
#define REPORT_COUNT(size)      (0x94 | size)
#define PUSH(size)              (0xa4 | size)
#define POP(size)               (0xb4 | size)

/* Local items */
#define USAGE(size)             (0x08 | size)
#define USAGE_MIN(size)         (0x18 | size)
#define USAGE_MAX(size)         (0x28 | size)
#define DESIGNATOR_INDEX(size)  (0x38 | size)
#define DESIGNATOR_MIN(size)    (0x48 | size)
#define DESIGNATOR_MAX(size)    (0x58 | size)
#define STRING_INDEX(size)      (0x78 | size)
#define STRING_MIN(size)        (0x88 | size)
#define STRING_MAX(size)        (0x98 | size)
#define DELIMITER(size)         (0xa8 | size)

#define REPORT_ID_KEYBOARD      (1)
#define REPORT_ID_MOUSE         (2)

#define MAX_REPORT_SIZE         (8)

unsigned char reportDescriptor[] = {
    /* Keyboard */
    USAGE_PAGE(1),      0x01,
    USAGE(1),           0x06,
    COLLECTION(1),      0x01,
    REPORT_ID(1),       REPORT_ID_KEYBOARD,
    USAGE_PAGE(1),      0x07,
    USAGE_MIN(1),       0xE0,
    USAGE_MAX(1),       0xE7,
    LOGICAL_MIN(1),     0x00,
    LOGICAL_MAX(1),     0x01,
    REPORT_SIZE(1),     0x01,
    REPORT_COUNT(1),    0x08,
    INPUT(1),           0x02,
    REPORT_COUNT(1),    0x01,
    REPORT_SIZE(1),     0x08,
    INPUT(1),           0x01,
    REPORT_COUNT(1),    0x05,
    REPORT_SIZE(1),     0x01,
    USAGE_PAGE(1),      0x08,
    USAGE_MIN(1),       0x01,
    USAGE_MAX(1),       0x05,
    OUTPUT(1),          0x02,
    REPORT_COUNT(1),    0x01,
    REPORT_SIZE(1),     0x03,
    OUTPUT(1),          0x01,
    REPORT_COUNT(1),    0x06,
    REPORT_SIZE(1),     0x08,
    LOGICAL_MIN(1),     0x00,
    LOGICAL_MAX(2),     0xff, 0x00,
    USAGE_PAGE(1),      0x07,
    USAGE_MIN(1),       0x00,
    USAGE_MAX(2),       0xff, 0x00,
    INPUT(1),           0x00,
    END_COLLECTION(0),

    /* Mouse */
    USAGE_PAGE(1),      0x01,
    USAGE(1),           0x02,
    COLLECTION(1),      0x01,
    USAGE(1),           0x01,
    COLLECTION(1),      0x00,
    REPORT_ID(1),       REPORT_ID_MOUSE,
    REPORT_COUNT(1),    0x03,
    REPORT_SIZE(1),     0x01,
    USAGE_PAGE(1),      0x09,
    USAGE_MIN(1),       0x1,
    USAGE_MAX(1),       0x3,
    LOGICAL_MIN(1),     0x00,
    LOGICAL_MAX(1),     0x01,
    INPUT(1),           0x02,
    REPORT_COUNT(1),    0x01,
    REPORT_SIZE(1),     0x05,
    INPUT(1),           0x01,
    REPORT_COUNT(1),    0x03,
    REPORT_SIZE(1),     0x08,
    USAGE_PAGE(1),      0x01,
    USAGE(1),           0x30,
    USAGE(1),           0x31,
    USAGE(1),           0x38,
    LOGICAL_MIN(1),     0x81,
    LOGICAL_MAX(1),     0x7f,
    INPUT(1),           0x06,
    END_COLLECTION(0),
    END_COLLECTION(0),
};

volatile bool complete;
volatile bool configured;
unsigned char outputReport[MAX_REPORT_SIZE];

usbhid::usbhid() {
    configured = false;
    connect();
}

void usbhid::deviceEventReset() {
    configured = false;

    /* Must call base class */
    usbdevice::deviceEventReset();
}

bool usbhid::requestSetConfiguration(void) {
    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(void) {
    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 = 0x09; /* TODO: Fix hard coded size/offset */
            transfer.ptr = &configurationDescriptor[18];
            transfer.direction = DEVICE_TO_HOST;
            success = true;
            break;
        case REPORT_DESCRIPTOR:
            transfer.remaining = sizeof(reportDescriptor);
            transfer.ptr = reportDescriptor;
            transfer.direction = DEVICE_TO_HOST;
            success = true;
            break;
        default:
            break;
    }

    return success;
}

bool usbhid::requestSetup(void) {
    /* Process class requests */
    bool success = false;

    if (transfer.setup.bmRequestType.Type == CLASS_TYPE) {
        switch (transfer.setup.bRequest) {
            case SET_REPORT:
                switch (transfer.setup.wValue & 0xff) {
                    case REPORT_ID_KEYBOARD:
                        /* TODO: LED state */
                        transfer.remaining = sizeof(outputReport);
                        transfer.ptr = outputReport;
                        transfer.direction = HOST_TO_DEVICE;
                        success = true;
                        break;
                    default:
                        break;
                }
                break;
            default:
                break;
        }
    }

    if (success) {
        /* We've handled this request */
        return true;
    }

    return usbdevice::requestSetup();
}

bool usbhid::sendInputReport(unsigned char id, unsigned char *data, unsigned char size) {
    /* Send an Input Report */
    /* If data is NULL an all zero report is sent */

    static unsigned char report[MAX_REPORT_SIZE+1]; /* +1 for report ID */
    unsigned char i;

    if (size > MAX_REPORT_SIZE) {
        return false;
    }

    /* Add report ID */
    report[0]=id;

    /* Add report data */
    if (data != NULL) {
        for (i=0; i<size; i++) {
            report[i+1] = *data++;
        }
    } else {
        for (i=0; i<size; i++) {
            report[i+1] = 0;
        }
    }

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

    /* Send report */
    complete = false;
    disableEvents();
    endpointWrite(EP1IN, report, size+1); /* +1 for report ID */
    enableEvents();

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

void usbhid::endpointEventEP1In(void) {
    complete = true;
}

bool usbhid::keyboard(char c) {
    /* Send a simulated keyboard keypress. Returns true if successful. */
    unsigned char report[8]={0,0,0,0,0,0,0,0};

    report[0] = keymap[c].modifier;
    report[2] = keymap[c].usage;

    /* Key down */
    if (!sendInputReport(REPORT_ID_KEYBOARD, report, 8)) {
        return false;
    }

    /* Key up */
    if (!sendInputReport(REPORT_ID_KEYBOARD, NULL, 8)) {
        return false;
    }

    return true;
}

bool usbhid::keyboard(char *string) {
    /* Send a string of characters. Returns true if successful. */
    do {
        if (!keyboard(*string++)) {
            return false;
        }
    } while (*string != '\0');

    return true;
}

bool usbhid::mouse(signed char x, signed char y, unsigned char buttons, signed char wheel) {
    /* Send a simulated mouse event. Returns true if successful. */
    unsigned char report[4]={0,0,0,0};

    report[0] = buttons;
    report[1] = x;
    report[2] = y;
    report[3] = wheel;

    if (!sendInputReport(REPORT_ID_MOUSE, report, 4)) {
        return false;
    }

    return true;
}


//
// USBJoystick
//

unsigned char reportDescriptorJoystick[] = {
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x05,                    // USAGE (Game Pad)
    0xa1, 0x01,                    // COLLECTION (Application)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x05,                    //     USAGE_MAXIMUM (Button 5)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x05,                    //     REPORT_COUNT (5)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x75, 0x03,                    //     REPORT_SIZE (3)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
    0x19, 0x06,                    //     USAGE_MINIMUM (Button 6)
    0x29, 0x0c,                    //     USAGE_MAXIMUM (Button 12)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x07,                    //     REPORT_COUNT (7)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x09, 0x33,                    //     USAGE (Rx)
    0x09, 0x34,                    //     USAGE (Ry)
    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x04,                    //     REPORT_COUNT (4)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x36,                    //     USAGE (Slider)
    0x09, 0x37,                    //     USAGE (Dial)
    0x26, 0xff, 0x00,              //     LOGICAL_MAXIMUM (255)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x02,                    //     REPORT_COUNT (2)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0xc0,                           //   END_COLLECTION
    0xc0                           // END_COLLECTION
};

USBJoystick::USBJoystick() {
    int reportDescriptorSize = sizeof(reportDescriptorJoystick);
    configurationDescriptor[25] = reportDescriptorSize & 0xff;
    configurationDescriptor[26] = (reportDescriptorSize >> 8) & 0xff;
}

bool USBJoystick::requestGetDescriptor(void) {
    bool success = false;

    switch (DESCRIPTOR_TYPE(transfer.setup.wValue)) {
        case REPORT_DESCRIPTOR:
            transfer.remaining = sizeof(reportDescriptorJoystick);
            transfer.ptr = reportDescriptorJoystick;
            transfer.direction = DEVICE_TO_HOST;
            success = true;
            break;
        default:
            success = usbhid::requestGetDescriptor();
            break;
    }

    return success;
}

bool USBJoystick::sendInputReport(unsigned char *data, unsigned char size) {
    /* Send an Input Report */
    /* If data is NULL an all zero report is sent */

    static unsigned char report[MAX_REPORT_SIZE];
    unsigned char i;

    if (size > MAX_REPORT_SIZE) {
        return false;
    }

    /* Add report data */
    if (data != NULL) {
        for (i=0; i<size; i++) {
            report[i] = *data++;
        }
    } else {
        for (i=0; i<size; i++) {
            report[i] = 0;
        }
    }

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

    /* Send report */
    complete = false;
    disableEvents();
    endpointWrite(EP1IN, report, size);
    enableEvents();

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

bool USBJoystick::joystick(unsigned char buttons1, unsigned short buttons2, signed char x, signed char y, signed char cx, signed char cy, unsigned char L, unsigned char R) {
    unsigned char report[9] = {0, 0, 0, 0, 0, 0, 0, 0};
    report[0] = buttons1;
    report[1] = buttons2;
    report[2] = x ;
    report[3] = -y ;
    report[4] = cx ;
    report[5] = cy ;
    report[6] = L ;
    report[7] = R ;


    if (!sendInputReport(report, 8)) {
        return false;
    }

    return true;
}