Joystick enabled version of USBHID -library. Has full Playstation 3 functionality including button pressures and a working home-button implementation, while maintaining full PC/MAC/linux -compatibility. basic operation of the lib: #include "mbed.h" #include "usbhid.h" USBJoystick joystick; int main() { while(1) { char dpad = 0xf; /*only the rightmost 4 bits matter*/ short button = 0xff; /*only the rightmost 13 bits matter*/ /*buttons are square, cross, circle, triangle, l1, r1, l2, r2, l3, r3, home.*/ char stick1x = 0; char stick1y = 0; char stick2x = 0; char stick2y = 0; joystick.joystick(dpad, buttons, stick1x, stick1y, stick2x, stick2y); wait_ms(5); } }
Diff: usbhid.cpp
- Revision:
- 0:237d5ef643e9
diff -r 000000000000 -r 237d5ef643e9 usbhid.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbhid.cpp Fri May 11 13:35:59 2012 +0000 @@ -0,0 +1,660 @@ +/* usbhid.cpp */ +/* USB HID class device */ +/* Copyright (c) Phil Wright 2008 */ + +/* modified by Shinichiro Oba <http://mbed.org/users/bricklife/> */ + +/* additional modification (PS3 home button functionality) by Panu Kauppinen*/ + +#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) + + +/* 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 (64) + +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), +}; + + + + +/* Descriptors */ +unsigned char deviceDescriptor[] = { + 0x12, /* bLength */ + DEVICE_DESCRIPTOR, /* bDescriptorType */ + 0x10, /* bcdUSB (LSB) */ + 0x01, /* bcdUSB (MSB) */ + 0x00, /* bDeviceClass */ + 0x00, /* bDeviceSubClass */ + 0x00, /* bDeviceprotocol */ + MAX_PACKET_SIZE_EP0, /* bMaxPacketSize0 */ + 0xC4, /* idVendor (LSB) */ + 0x10, /* idVendor (MSB) */ + 0xC0, /* idProduct (LSB) */ + 0x82, /* idProduct (MSB) */ + 0x01, /* bcdDevice (LSB) */ + 0x00, /* bcdDevice (MSB) */ + 0x01, /* iManufacturer */ + 0x02, /* 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 */ + 0x80, /* bmAttributes */ + 0x32, /* 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) */ + 0x05, /* bInterval */ + }; + + + +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 magic_init_bytes[] = {0x21, 0x26, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00}; // PS3 will request these bytes as a class type report 0x0300 + +/* Joystick */ +unsigned char reportDescriptorJoystick[] = { +0x05, 0x01, // USAGE_PAGE (Generic Desktop) +0x09, 0x05, // USAGE (Gamepad) +0xa1, 0x01, // COLLECTION (Application) + +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, 0x0d, // REPORT_COUNT (13) +0x05, 0x09, // USAGE_PAGE (Button) +0x19, 0x01, // USAGE_MINIMUM (Button 1) +0x29, 0x0d, // USAGE_MAXIMUM (Button 13) +0x81, 0x02, // INPUT (Data,Var,Abs) +0x95, 0x03, // REPORT_COUNT (3) +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) +0x09, 0x30, // USAGE (X) +0x09, 0x31, // USAGE (Y) +0x09, 0x32, // USAGE (Z) +0x09, 0x35, // USAGE (Rz) +0x75, 0x08, // REPORT_SIZE (8) +0x95, 0x04, // REPORT_COUNT (4) +0x81, 0x02, // INPUT (Data,Var,Abs) + +0x06, 0x00, 0xff, // USAGE_PAGE (Vendor Specific) +0x09, 0x20, // Unknown +0x09, 0x21, // Unknown +0x09, 0x22, // Unknown +0x09, 0x23, // Unknown +0x09, 0x24, // Unknown +0x09, 0x25, // Unknown +0x09, 0x26, // Unknown +0x09, 0x27, // Unknown +0x09, 0x28, // Unknown +0x09, 0x29, // Unknown +0x09, 0x2a, // Unknown +0x09, 0x2b, // Unknown +0x95, 0x0c, // REPORT_COUNT (12) +0x81, 0x02, // INPUT (Data,Var,Abs) +0x0a, 0x21, 0x26, // Unknown +0x95, 0x08, // REPORT_COUNT (8) +0xb1, 0x02, // FEATURE (Data,Var,Abs) + +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 stick, unsigned short buttons, signed char x, signed char y, signed char z, signed char rz) +{ + unsigned char report[8+8+3] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + 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 = 2; + if (stick & JOYSTICK_DOWN) hatswitch = 3; + } else if (stick & JOYSTICK_DOWN) { + hatswitch = 4; + if (stick & JOYSTICK_LEFT) hatswitch = 5; + } else if (stick & JOYSTICK_LEFT) { + hatswitch = 6; + } else { + hatswitch = 0xf; + } + + report[0] = buttons & 0xff; //buttons 1-8 + report[1] = ( buttons >> 8 ) & 0xff;//buttons 9-13 + report[2] = ((hatswitch ) & 0x0f); // d-pad + report[3] = x + 0x80; //sticks + report[4] = y + 0x80; + report[5] = z + 0x80; + report[6] = rz + 0x80; + + report[7] = 0x00; //unknown + report[8] = 0x00; //unknown + report[9] = 0x00; //unknown + report[10] = 0x00; //unknown + + report[11] = 0x00; //button pressures + report[12] = 0x00; //button pressures + report[13] = 0x00; //button pressures + report[14] = 0x00; //button pressures + + report[15] = 0x00; //trigger pressures + report[16] = 0x00; //trigger pressures + report[17] = 0x00; //trigger pressures + report[18] = 0x00; //trigger pressures + + if (!sendInputReport(report, 19)) { + return false; + } + + + return true; +} + +bool USBJoystick::requestSetup(void) +{ + /* Process class requests */ + bool success = false; + + if (transfer.setup.bmRequestType.Type == CLASS_TYPE) + { + switch (transfer.setup.bRequest) + { + case GET_REPORT: + switch (transfer.setup.wValue & 0xffff) + { + case 0x0300: //vendor specific request + + transfer.ptr = (unsigned char*)magic_init_bytes; + transfer.remaining = sizeof(magic_init_bytes); + transfer.direction = DEVICE_TO_HOST; + success = true; + + break; + default: + break; + } + break; + default: + break; + } + } + + if (success) + { + /* We've handled this request */ + return true; + } + + return usbdevice::requestSetup(); +}