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); } }
usbhid.cpp
- Committer:
- innocopter
- Date:
- 2012-05-11
- Revision:
- 0:237d5ef643e9
File content as of revision 0:237d5ef643e9:
/* 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(); }