Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
USBHID/usbhid.cpp
- Committer:
- igor_m
- Date:
- 2011-09-25
- Revision:
- 0:547c5459faa6
- Child:
- 5:eb93a4f91396
File content as of revision 0:547c5459faa6:
/* 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, 0x04, // USAGE (Joystick)
0xa1, 0x01, // COLLECTION (Application)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x10, // USAGE_MAXIMUM (Button 16)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x10, // REPORT_COUNT (16)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
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) {
unsigned char report[6] = {0, 0, 0, 0, 0, 0};
report[0] = buttons1;
report[1] = buttons2;
report[2] = x ;
report[3] = (-1)*y ;
if (!sendInputReport(report, 4)) {
return false;
}
return true;
}