Revision 269
Added by markw almost 11 years ago
firmware/usb/LICENSE | ||
---|---|---|
This part is GPL - in fact this whole firmware should be - just need to check a few things...
|
firmware/usb/debug.h | ||
---|---|---|
#define hid_debugf(IN, ...) {};
|
||
#define hidp_debugf(IN, ...) {};
|
||
#define iprintf(IN, ...) {};
|
firmware/usb/events.h | ||
---|---|---|
void event_keyboard(uint8_t i, uint8_t buf[])
|
||
{
|
||
int j;
|
||
printf("Event keyboard:%d\n", i);
|
||
for (j=0;j!=6;++j)
|
||
{
|
||
printf("Event keyboard:%d = %x\n", j, buf[j]);
|
||
}
|
||
}
|
||
void event_mouse(uint8_t a, uint8_t b, uint8_t c)
|
||
{
|
||
printf("Event mouse:%d %d %d\n",a,b,c);
|
||
}
|
||
void event_digital_joystick(uint8_t idx, uint8_t jmap)
|
||
{
|
||
printf("Event joystick:%d %x\n", idx,jmap);
|
||
}
|
||
void event_analog_joystick(uint8_t idx, int8_t x, int8_t y)
|
||
{
|
||
printf("Event analog joystick:%d %d %d\n", idx,x,y);
|
||
}
|
||
|
||
#define JOY_RIGHT 0x01
|
||
#define JOY_LEFT 0x02
|
||
#define JOY_DOWN 0x04
|
||
#define JOY_UP 0x08
|
||
#define JOY_BTN1 0x10
|
||
#define JOY_BTN2 0x20
|
||
#define JOY_BTN3 0x40
|
||
#define JOY_BTN4 0x80
|
||
#define JOY_MOVE (JOY_RIGHT|JOY_LEFT|JOY_UP|JOY_DOWN)
|
||
|
||
|
firmware/usb/hid.c | ||
---|---|---|
#include <stdio.h>
|
||
|
||
#include "usb.h"
|
||
#include "timer.h"
|
||
#include "hidparser.h"
|
||
#include "events.h"
|
||
#include "debug.h"
|
||
#include "usbhostslave.h"
|
||
|
||
static unsigned char kbd_led_state = 0; // default: all leds off
|
||
static unsigned char joysticks = 0; // number of detected usb joysticks
|
||
|
||
uint8_t hid_get_joysticks(void) {
|
||
return joysticks;
|
||
}
|
||
|
||
//get HID report descriptor
|
||
static uint8_t hid_get_report_descr(usb_device_t *dev, uint8_t i, uint16_t size) {
|
||
// hid_debugf("%s(%x, if=%d, size=%d)", __FUNCTION__, dev->bAddress, iface, size);
|
||
|
||
uint8_t buf[size];
|
||
usb_hid_info_t *info = &(dev->hid_info);
|
||
uint8_t rcode = usb_ctrl_req( dev, HID_REQ_HIDREPORT, USB_REQUEST_GET_DESCRIPTOR, 0x00,
|
||
HID_DESCRIPTOR_REPORT, info->iface[i].iface_idx, size, buf);
|
||
|
||
if(!rcode) {
|
||
hid_debugf("HID report descriptor:");
|
||
//MWW hexdump(buf, size, 0);
|
||
|
||
// we got a report descriptor. Try to parse it
|
||
if(parse_report_descriptor(buf, size, &(info->iface[i].conf))) {
|
||
if(info->iface[i].conf.type == CONFIG_TYPE_JOYSTICK) {
|
||
hid_debugf("Detected USB joystick #%d", joysticks);
|
||
|
||
info->iface[i].device_type = HID_DEVICE_JOYSTICK;
|
||
info->iface[i].jindex = joysticks++;
|
||
}
|
||
}
|
||
}
|
||
|
||
return rcode;
|
||
}
|
||
|
||
static uint8_t hid_set_idle(usb_device_t *dev, uint8_t iface, uint8_t reportID, uint8_t duration ) {
|
||
// hid_debugf("%s(%x, if=%d id=%d, dur=%d)", __FUNCTION__, dev->bAddress, iface, reportID, duration);
|
||
|
||
return( usb_ctrl_req( dev, HID_REQ_HIDOUT, HID_REQUEST_SET_IDLE, reportID,
|
||
duration, iface, 0x0000, NULL));
|
||
}
|
||
|
||
static uint8_t hid_set_protocol(usb_device_t *dev, uint8_t iface, uint8_t protocol) {
|
||
// hid_debugf("%s(%x, if=%d proto=%d)", __FUNCTION__, dev->bAddress, iface, protocol);
|
||
|
||
return( usb_ctrl_req( dev, HID_REQ_HIDOUT, HID_REQUEST_SET_PROTOCOL, protocol,
|
||
0x00, iface, 0x0000, NULL));
|
||
}
|
||
|
||
static uint8_t hid_set_report(usb_device_t *dev, uint8_t iface, uint8_t report_type, uint8_t report_id,
|
||
uint16_t nbytes, uint8_t* dataptr ) {
|
||
// hid_debugf("%s(%x, if=%d data=%x)", __FUNCTION__, dev->bAddress, iface, dataptr[0]);
|
||
|
||
return( usb_ctrl_req(dev, HID_REQ_HIDOUT, HID_REQUEST_SET_REPORT, report_id,
|
||
report_type, iface, nbytes, dataptr));
|
||
}
|
||
|
||
/* todo: handle parsing in chunks */
|
||
static uint8_t usb_hid_parse_conf(usb_device_t *dev, uint8_t conf, uint16_t len) {
|
||
usb_hid_info_t *info = &(dev->hid_info);
|
||
uint8_t rcode;
|
||
bool isGoodInterface = false;
|
||
|
||
union buf_u {
|
||
usb_configuration_descriptor_t conf_desc;
|
||
usb_interface_descriptor_t iface_desc;
|
||
usb_endpoint_descriptor_t ep_desc;
|
||
usb_hid_descriptor_t hid_desc;
|
||
uint8_t raw[len];
|
||
} buf, *p;
|
||
|
||
// usb_interface_descriptor
|
||
|
||
if(rcode = usb_get_conf_descr(dev, len, conf, &buf.conf_desc))
|
||
return rcode;
|
||
|
||
/* scan through all descriptors */
|
||
p = &buf;
|
||
while(len > 0) {
|
||
switch(p->conf_desc.bDescriptorType) {
|
||
case USB_DESCRIPTOR_CONFIGURATION:
|
||
// hid_debugf("conf descriptor size %d", p->conf_desc.bLength);
|
||
// we already had this, so we simply ignore it
|
||
break;
|
||
|
||
case USB_DESCRIPTOR_INTERFACE:
|
||
isGoodInterface = false;
|
||
// hid_debugf("iface descriptor size %d", p->iface_desc.bLength);
|
||
|
||
/* check the interface descriptors for supported class */
|
||
|
||
// only HID interfaces are supported
|
||
if(p->iface_desc.bInterfaceClass == USB_CLASS_HID) {
|
||
// puts("iface is HID");
|
||
|
||
if(info->bNumIfaces < MAX_IFACES) {
|
||
// ok, let's use this interface
|
||
isGoodInterface = true;
|
||
|
||
info->iface[info->bNumIfaces].iface_idx = p->iface_desc.bInterfaceNumber;
|
||
info->iface[info->bNumIfaces].has_boot_mode = false;
|
||
info->iface[info->bNumIfaces].is_5200daptor = false;
|
||
info->iface[info->bNumIfaces].key_state = 0;
|
||
info->iface[info->bNumIfaces].device_type = HID_DEVICE_UNKNOWN;
|
||
info->iface[info->bNumIfaces].conf.type = CONFIG_TYPE_NONE;
|
||
|
||
if(p->iface_desc.bInterfaceSubClass == HID_BOOT_INTF_SUBCLASS) {
|
||
// hid_debugf("Iface %d is Boot sub class", info->bNumIfaces);
|
||
info->iface[info->bNumIfaces].has_boot_mode = true;
|
||
}
|
||
|
||
switch(p->iface_desc.bInterfaceProtocol) {
|
||
case HID_PROTOCOL_NONE:
|
||
hid_debugf("HID protocol is NONE");
|
||
break;
|
||
|
||
case HID_PROTOCOL_KEYBOARD:
|
||
hid_debugf("HID protocol is KEYBOARD");
|
||
info->iface[info->bNumIfaces].device_type = HID_DEVICE_KEYBOARD;
|
||
break;
|
||
|
||
case HID_PROTOCOL_MOUSE:
|
||
hid_debugf("HID protocol is MOUSE");
|
||
info->iface[info->bNumIfaces].device_type = HID_DEVICE_MOUSE;
|
||
break;
|
||
|
||
default:
|
||
hid_debugf("HID protocol is %d", p->iface_desc.bInterfaceProtocol);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case USB_DESCRIPTOR_ENDPOINT:
|
||
// hid_debugf("endpoint descriptor size %d", p->ep_desc.bLength);
|
||
|
||
if(isGoodInterface) {
|
||
|
||
// only interrupt in endpoints are supported
|
||
if ((p->ep_desc.bmAttributes & 0x03) == 3 && (p->ep_desc.bEndpointAddress & 0x80) == 0x80) {
|
||
hid_debugf("endpoint %d, interval = %dms",
|
||
p->ep_desc.bEndpointAddress & 0x0F, p->ep_desc.bInterval);
|
||
|
||
// Fill in the endpoint info structure
|
||
uint8_t epidx = info->bNumIfaces;
|
||
info->iface[epidx].interval = p->ep_desc.bInterval;
|
||
info->iface[epidx].ep.epAddr = (p->ep_desc.bEndpointAddress & 0x0F);
|
||
info->iface[epidx].ep.maxPktSize = p->ep_desc.wMaxPacketSize[0];
|
||
info->iface[epidx].ep.epAttribs = 0;
|
||
info->iface[epidx].ep.bmNakPower = USB_NAK_NOWAIT;
|
||
info->bNumIfaces++;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case HID_DESCRIPTOR_HID:
|
||
hid_debugf("hid descriptor size %d", p->ep_desc.bLength);
|
||
|
||
if(isGoodInterface) {
|
||
// we need a report descriptor
|
||
if(p->hid_desc.bDescrType == HID_DESCRIPTOR_REPORT) {
|
||
uint16_t len = p->hid_desc.wDescriptorLength[0] +
|
||
256 * p->hid_desc.wDescriptorLength[1];
|
||
hid_debugf(" -> report descriptor size = %d", len);
|
||
|
||
info->iface[info->bNumIfaces].report_desc_size = len;
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
hid_debugf("unsupported descriptor type %d size %d", p->raw[1], p->raw[0]);
|
||
}
|
||
|
||
// advance to next descriptor
|
||
len -= p->conf_desc.bLength;
|
||
p = (union buf_u*)(p->raw + p->conf_desc.bLength);
|
||
}
|
||
|
||
if(len != 0) {
|
||
hid_debugf("Config underrun: %d", len);
|
||
return USB_ERROR_CONFIGURAION_SIZE_MISMATCH;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static uint8_t usb_hid_init(usb_device_t *dev) {
|
||
hid_debugf("%s(%x)", __FUNCTION__, dev->bAddress);
|
||
|
||
uint8_t rcode;
|
||
uint8_t i;
|
||
uint16_t vid, pid;
|
||
|
||
usb_hid_info_t *info = &(dev->hid_info);
|
||
|
||
union {
|
||
usb_device_descriptor_t dev_desc;
|
||
usb_configuration_descriptor_t conf_desc;
|
||
} buf;
|
||
|
||
// reset status
|
||
info->bPollEnable = false;
|
||
info->bNumIfaces = 0;
|
||
|
||
for(i=0;i<MAX_IFACES;i++) {
|
||
info->iface[i].qNextPollTime = 0;
|
||
info->iface[i].ep.epAddr = i;
|
||
info->iface[i].ep.maxPktSize = 8;
|
||
info->iface[i].ep.epAttribs = 0;
|
||
info->iface[i].ep.bmNakPower = USB_NAK_MAX_POWER;
|
||
}
|
||
|
||
// try to re-read full device descriptor from newly assigned address
|
||
if(rcode = usb_get_dev_descr( dev, sizeof(usb_device_descriptor_t), &buf.dev_desc ))
|
||
return rcode;
|
||
|
||
// save vid/pid for automatic hack later
|
||
vid = buf.dev_desc.idVendor;
|
||
pid = buf.dev_desc.idProduct;
|
||
|
||
uint8_t num_of_conf = buf.dev_desc.bNumConfigurations;
|
||
// hid_debugf("number of configurations: %d", num_of_conf);
|
||
|
||
for(i=0; i<num_of_conf; i++) {
|
||
if(rcode = usb_get_conf_descr(dev, sizeof(usb_configuration_descriptor_t), i, &buf.conf_desc))
|
||
return rcode;
|
||
|
||
// hid_debugf("conf descriptor %d has total size %d", i, buf.conf_desc.wTotalLength);
|
||
|
||
// parse directly if it already fitted completely into the buffer
|
||
usb_hid_parse_conf(dev, i, buf.conf_desc.wTotalLength);
|
||
}
|
||
|
||
// check if we found valid hid interfaces
|
||
if(!info->bNumIfaces) {
|
||
hid_debugf("no hid interfaces found");
|
||
return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
|
||
}
|
||
|
||
// Set Configuration Value
|
||
rcode = usb_set_conf(dev, buf.conf_desc.bConfigurationValue);
|
||
|
||
// process all supported interfaces
|
||
for(i=0; i<info->bNumIfaces; i++) {
|
||
// no boot mode, try to parse HID report descriptor
|
||
if(!info->iface[i].has_boot_mode) {
|
||
rcode = hid_get_report_descr(dev, i, info->iface[i].report_desc_size);
|
||
if(rcode) return rcode;
|
||
|
||
if(info->iface[i].device_type == CONFIG_TYPE_JOYSTICK) {
|
||
char k;
|
||
|
||
iprintf("Report: type = %d, id = %d, size = %d\n",
|
||
info->iface[i].conf.type,
|
||
info->iface[i].conf.report_id,
|
||
info->iface[i].conf.report_size);
|
||
|
||
for(k=0;k<2;k++)
|
||
iprintf("Axis%d: %d@%d %d->%d\n", k,
|
||
info->iface[i].conf.joystick.axis[k].size,
|
||
info->iface[i].conf.joystick.axis[k].byte_offset,
|
||
info->iface[i].conf.joystick.axis[k].logical.min,
|
||
info->iface[i].conf.joystick.axis[k].logical.max);
|
||
|
||
for(k=0;k<4;k++)
|
||
iprintf("Button%d: @%d/%d\n", k,
|
||
info->iface[i].conf.joystick.button[k].byte_offset,
|
||
info->iface[i].conf.joystick.button[k].bitmask);
|
||
}
|
||
|
||
|
||
// use fixed setup for known interfaces
|
||
if((vid == 0x0079) && (pid == 0x0011) && (i==0)) {
|
||
iprintf("hacking cheap NES pad\n");
|
||
|
||
// fixed setup for nes gamepad
|
||
info->iface[0].conf.joystick.button[0].byte_offset = 5;
|
||
info->iface[0].conf.joystick.button[0].bitmask = 32;
|
||
info->iface[0].conf.joystick.button[1].byte_offset = 5;
|
||
info->iface[0].conf.joystick.button[1].bitmask = 64;
|
||
info->iface[0].conf.joystick.button[2].byte_offset = 6;
|
||
info->iface[0].conf.joystick.button[2].bitmask = 16;
|
||
info->iface[0].conf.joystick.button[3].byte_offset = 6;
|
||
info->iface[0].conf.joystick.button[3].bitmask = 32;
|
||
}
|
||
|
||
if((vid == 0x04d8) && (pid == 0xf6ec) && (i==0)) {
|
||
iprintf("hacking 5200daptor\n");
|
||
|
||
info->iface[0].conf.joystick.button[2].byte_offset = 4;
|
||
info->iface[0].conf.joystick.button[2].bitmask = 0x40; // "Reset"
|
||
info->iface[0].conf.joystick.button[3].byte_offset = 4;
|
||
info->iface[0].conf.joystick.button[3].bitmask = 0x10; // "Start"
|
||
|
||
info->iface[0].is_5200daptor = true;
|
||
}
|
||
}
|
||
|
||
rcode = hid_set_idle(dev, info->iface[i].iface_idx, 0, 0);
|
||
// MWW if (rcode && rcode != hrSTALL)
|
||
if (rcode && rcode != OHS900_STATMASK_STALL_RXED)
|
||
return rcode;
|
||
|
||
// enable boot mode
|
||
if(info->iface[i].has_boot_mode)
|
||
hid_set_protocol(dev, info->iface[i].iface_idx, HID_BOOT_PROTOCOL);
|
||
}
|
||
|
||
puts("HID configured");
|
||
|
||
// update leds
|
||
for(i=0;i<MAX_IFACES;i++)
|
||
if(dev->hid_info.iface[i].device_type == HID_DEVICE_KEYBOARD)
|
||
hid_set_report(dev, dev->hid_info.iface[i].iface_idx, 2, 0, 1, &kbd_led_state);
|
||
|
||
info->bPollEnable = true;
|
||
return 0;
|
||
}
|
||
|
||
static uint8_t usb_hid_release(usb_device_t *dev) {
|
||
usb_hid_info_t *info = &(dev->hid_info);
|
||
|
||
puts(__FUNCTION__);
|
||
|
||
uint8_t i;
|
||
// check if a joystick is released
|
||
for(i=0;i<info->bNumIfaces;i++) {
|
||
if(info->iface[i].device_type == HID_DEVICE_JOYSTICK) {
|
||
uint8_t c_jindex = info->iface[i].jindex;
|
||
hid_debugf("releasing joystick #%d, renumbering", c_jindex);
|
||
|
||
// walk through all devices and search for sticks with a higher id
|
||
|
||
// search for all joystick interfaces on all hid devices
|
||
usb_device_t *dev = usb_get_devices();
|
||
uint8_t j;
|
||
for(j=0;j<USB_NUMDEVICES;j++) {
|
||
if(dev[j].bAddress && (dev[j].class == &usb_hid_class)) {
|
||
// search for joystick interfaces
|
||
uint8_t k;
|
||
for(k=0;k<MAX_IFACES;k++) {
|
||
if(dev[j].hid_info.iface[k].device_type == HID_DEVICE_JOYSTICK) {
|
||
if(dev[j].hid_info.iface[k].jindex > c_jindex) {
|
||
hid_debugf("decreasing jindex of dev #%d from %d to %d", j,
|
||
dev[j].hid_info.iface[k].jindex, dev[j].hid_info.iface[k].jindex-1);
|
||
dev[j].hid_info.iface[k].jindex--;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// one less joystick in the system ...
|
||
joysticks--;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
// special 5200daptor button processing
|
||
static void handle_5200daptor(usb_hid_iface_info_t *iface, uint8_t *buf) {
|
||
|
||
// list of buttons that are reported as keys
|
||
static const struct {
|
||
uint8_t byte_offset; // offset of the byte within the report which the button bit is in
|
||
uint8_t mask; // bitmask of the button bit
|
||
uint8_t key_code[2]; // usb keycodes to be sent for joystick 0 and joystick 1
|
||
} button_map[] = {
|
||
{ 4, 0x10, 0x3a, 0x3d }, /* START -> f1/f4 */
|
||
{ 4, 0x20, 0x3b, 0x3e }, /* PAUSE -> f2/f5 */
|
||
{ 4, 0x40, 0x3c, 0x3f }, /* RESET -> f3/f6 */
|
||
{ 5, 0x01, 0x1e, 0x21 }, /* 1 -> 1/4 */
|
||
{ 5, 0x02, 0x1f, 0x22 }, /* 2 -> 2/5 */
|
||
{ 5, 0x04, 0x20, 0x23 }, /* 3 -> 3/6 */
|
||
{ 5, 0x08, 0x14, 0x15 }, /* 4 -> q/r */
|
||
{ 5, 0x10, 0x1a, 0x17 }, /* 5 -> w/t */
|
||
{ 5, 0x20, 0x08, 0x1c }, /* 6 -> e/y */
|
||
{ 5, 0x40, 0x04, 0x09 }, /* 7 -> a/f */
|
||
{ 5, 0x80, 0x16, 0x0a }, /* 8 -> s/g */
|
||
{ 6, 0x01, 0x07, 0x0b }, /* 9 -> d/h */
|
||
{ 6, 0x02, 0x1d, 0x19 }, /* * -> z/v */
|
||
{ 6, 0x04, 0x1b, 0x05 }, /* 0 -> x/b */
|
||
{ 6, 0x08, 0x06, 0x11 }, /* # -> c/n */
|
||
{ 0, 0x00, 0x00, 0x00 } /* ---- end ---- */
|
||
};
|
||
|
||
// keyboard events are only generated for the first
|
||
// two joysticks in the system
|
||
if(iface->jindex > 1) return;
|
||
|
||
// build map of pressed keys
|
||
uint8_t i;
|
||
uint16_t keys = 0;
|
||
for(i=0;button_map[i].mask;i++)
|
||
if(buf[button_map[i].byte_offset] & button_map[i].mask)
|
||
keys |= (1<<i);
|
||
|
||
// check if keys have changed
|
||
if(iface->key_state != keys) {
|
||
uint8_t buf[6] = { 0,0,0,0,0,0 };
|
||
uint8_t p = 0;
|
||
|
||
// report up to 6 pressed keys
|
||
for(i=0;(i<16)&&(p<6);i++)
|
||
if(keys & (1<<i))
|
||
buf[p++] = button_map[i].key_code[iface->jindex];
|
||
|
||
// iprintf("5200: %d %d %d %d %d %d\n", buf[0],buf[1],buf[2],buf[3],buf[4],buf[5]);
|
||
|
||
// generate key events
|
||
event_keyboard(0x00, buf);
|
||
|
||
// save current state of keys
|
||
iface->key_state = keys;
|
||
}
|
||
}
|
||
|
||
static uint8_t usb_hid_poll(usb_device_t *dev) {
|
||
usb_hid_info_t *info = &(dev->hid_info);
|
||
int8_t i;
|
||
|
||
if (!info->bPollEnable)
|
||
return 0;
|
||
|
||
for(i=0;i<info->bNumIfaces;i++) {
|
||
usb_hid_iface_info_t *iface = info->iface+i;
|
||
|
||
if(iface->device_type != HID_DEVICE_UNKNOWN) {
|
||
|
||
if (iface->qNextPollTime <= timer_get_msec()) {
|
||
// hid_debugf("poll %d...", iface->ep.epAddr);
|
||
|
||
uint16_t read = iface->ep.maxPktSize;
|
||
uint8_t buf[iface->ep.maxPktSize];
|
||
uint8_t rcode =
|
||
usb_in_transfer(dev, &(iface->ep), &read, buf);
|
||
|
||
if (rcode) {
|
||
// MWW if (rcode != hrNAK)
|
||
if (rcode != OHS900_STATMASK_NAK_RXED)
|
||
hid_debugf("%s() error: %d", __FUNCTION__, rcode);
|
||
} else {
|
||
|
||
// successfully received some bytes
|
||
if(iface->has_boot_mode) {
|
||
if(iface->device_type == HID_DEVICE_MOUSE) {
|
||
// boot mouse needs at least three bytes
|
||
if(read >= 3) {
|
||
// forward all three bytes to the user_io layer
|
||
event_mouse(buf[0], buf[1], buf[2]);
|
||
}
|
||
}
|
||
|
||
if(iface->device_type == HID_DEVICE_KEYBOARD) {
|
||
// boot kbd needs at least eight bytes
|
||
if(read >= 8) {
|
||
event_keyboard(buf[0], buf+2);
|
||
}
|
||
}
|
||
}
|
||
|
||
if(iface->device_type == HID_DEVICE_JOYSTICK) {
|
||
hid_config_t *conf = &iface->conf;
|
||
if(read >= conf->report_size) {
|
||
uint8_t jmap = 0;
|
||
uint16_t a[2];
|
||
uint8_t idx, i;
|
||
|
||
// hid_debugf("Joystick data:"); hexdump(buf, read, 0);
|
||
|
||
// two axes ...
|
||
for(i=0;i<2;i++) {
|
||
a[i] = buf[conf->joystick.axis[i].byte_offset];
|
||
if(conf->joystick.axis[i].size == 16)
|
||
a[i] += (buf[conf->joystick.axis[i].byte_offset+1])<<8;
|
||
|
||
// scale to 0 -> 255 range. 99% of the joysticks already deliver that
|
||
if((conf->joystick.axis[i].logical.min != 0) ||
|
||
(conf->joystick.axis[i].logical.max != 255)) {
|
||
a[i] = ((a[i] - conf->joystick.axis[i].logical.min) * 255)/
|
||
(conf->joystick.axis[i].logical.max -
|
||
conf->joystick.axis[i].logical.min);
|
||
}
|
||
}
|
||
|
||
if(a[0] < 64) jmap |= JOY_LEFT;
|
||
if(a[0] > 192) jmap |= JOY_RIGHT;
|
||
if(a[1] < 64) jmap |= JOY_UP;
|
||
if(a[1] > 192) jmap |= JOY_DOWN;
|
||
|
||
// iprintf("JOY X:%d Y:%d\n", a[0], a[1]);
|
||
|
||
// ... and four buttons
|
||
for(i=0;i<4;i++)
|
||
if(buf[conf->joystick.button[i].byte_offset] &
|
||
conf->joystick.button[i].bitmask) jmap |= (JOY_BTN1<<i);
|
||
|
||
// iprintf("JOY D:%d\n", jmap);
|
||
|
||
// swap joystick 0 and 1 since 1 is the one
|
||
// used primarily on most systems
|
||
idx = iface->jindex;
|
||
if(idx == 0) idx = 1;
|
||
else if(idx == 1) idx = 0;
|
||
|
||
// check if joystick state has changed
|
||
if(jmap != iface->jmap) {
|
||
// iprintf("jmap %d changed to %x\n", idx, jmap);
|
||
|
||
// and feed into joystick input system
|
||
event_digital_joystick(idx, jmap);
|
||
iface->jmap = jmap;
|
||
}
|
||
|
||
// also send analog values
|
||
event_analog_joystick(idx, a[0]-128, a[1]-128);
|
||
|
||
// do special 5200daptor treatment
|
||
if(iface->is_5200daptor)
|
||
handle_5200daptor(iface, buf);
|
||
}
|
||
}
|
||
}
|
||
iface->qNextPollTime += iface->interval; // poll at requested rate
|
||
}
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
void hid_set_kbd_led(unsigned char led, bool on) {
|
||
// check if led state has changed
|
||
if( (on && !(kbd_led_state&led)) || (!on && (kbd_led_state&led))) {
|
||
if(on) kbd_led_state |= led;
|
||
else kbd_led_state &= ~led;
|
||
|
||
// search for all keyboard interfaces on all hid devices
|
||
usb_device_t *dev = usb_get_devices();
|
||
int i;
|
||
for(i=0;i<USB_NUMDEVICES;i++) {
|
||
if(dev[i].bAddress && (dev[i].class == &usb_hid_class)) {
|
||
// search for keyboard interfaces
|
||
int j;
|
||
for(j=0;j<MAX_IFACES;j++)
|
||
if(dev[i].hid_info.iface[j].device_type == HID_DEVICE_KEYBOARD)
|
||
hid_set_report(dev+i, dev[i].hid_info.iface[j].iface_idx, 2, 0, 1, &kbd_led_state);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
const usb_device_class_config_t usb_hid_class = {
|
||
usb_hid_init, usb_hid_release, usb_hid_poll };
|
||
|
firmware/usb/hid.h | ||
---|---|---|
#ifndef HID_H
|
||
#define HID_H
|
||
|
||
#include <stdbool.h>
|
||
#include <inttypes.h>
|
||
#include "hidparser.h"
|
||
|
||
#define HID_LED_NUM_LOCK 0x01
|
||
#define HID_LED_CAPS_LOCK 0x02
|
||
#define HID_LED_SCROLL_LOCK 0x04
|
||
|
||
/* HID constants. Not part of chapter 9 */
|
||
/* Class-Specific Requests */
|
||
#define HID_REQUEST_GET_REPORT 0x01
|
||
#define HID_REQUEST_GET_IDLE 0x02
|
||
#define HID_REQUEST_GET_PROTOCOL 0x03
|
||
#define HID_REQUEST_SET_REPORT 0x09
|
||
#define HID_REQUEST_SET_IDLE 0x0A
|
||
#define HID_REQUEST_SET_PROTOCOL 0x0B
|
||
|
||
#define HID_DESCRIPTOR_HID 0x21
|
||
#define HID_DESCRIPTOR_REPORT 0x22
|
||
#define HID_DESRIPTOR_PHY 0x23
|
||
|
||
/* Protocol Selection */
|
||
#define HID_BOOT_PROTOCOL 0x00
|
||
#define HID_RPT_PROTOCOL 0x01
|
||
|
||
/* HID Interface Class SubClass Codes */
|
||
#define HID_BOOT_INTF_SUBCLASS 0x01
|
||
|
||
#define HID_PROTOCOL_NONE 0x00
|
||
#define HID_PROTOCOL_KEYBOARD 0x01
|
||
#define HID_PROTOCOL_MOUSE 0x02
|
||
|
||
#define HID_REQ_HIDREPORT USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_INTERFACE
|
||
#define HID_REQ_HIDOUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
|
||
|
||
#define MAX_IFACES 2 // max supported interfaces per device. 2 to support kbd/mouse combos
|
||
|
||
#define HID_DEVICE_UNKNOWN 0
|
||
#define HID_DEVICE_MOUSE 1
|
||
#define HID_DEVICE_KEYBOARD 2
|
||
#define HID_DEVICE_JOYSTICK 3
|
||
|
||
typedef struct {
|
||
ep_t ep; // interrupt endpoint info structure
|
||
|
||
uint8_t iface_idx;
|
||
uint16_t report_desc_size;
|
||
|
||
uint8_t device_type;
|
||
bool has_boot_mode: 1; // device supports boot mode
|
||
bool is_5200daptor: 1; // device is a 5200daptor with special key handling
|
||
uint16_t key_state; // needed to detect key state changes in 5200daptor
|
||
|
||
// additional info extracted from the report descriptor
|
||
// (currently only used for joysticks)
|
||
uint8_t jmap; // last reported joystick state
|
||
uint8_t jindex; // joystick index
|
||
hid_config_t conf;
|
||
|
||
uint8_t interval;
|
||
uint32_t qNextPollTime; // next poll time
|
||
|
||
} usb_hid_iface_info_t;
|
||
|
||
typedef struct {
|
||
bool bPollEnable; // poll enable flag
|
||
uint8_t bNumIfaces;
|
||
|
||
usb_hid_iface_info_t iface[MAX_IFACES];
|
||
} usb_hid_info_t;
|
||
|
||
/* HID descriptor */
|
||
typedef struct {
|
||
uint8_t bLength;
|
||
uint8_t bDescriptorType;
|
||
uint16_t bcdHID; // HID class specification release
|
||
uint8_t bCountryCode;
|
||
uint8_t bNumDescriptors; // Number of additional class specific descriptors
|
||
uint8_t bDescrType; // Type of class descriptor
|
||
uint8_t wDescriptorLength[2]; // Total size of the Report descriptor
|
||
} __attribute__((packed)) usb_hid_descriptor_t;
|
||
|
||
// interface to usb core
|
||
extern const usb_device_class_config_t usb_hid_class;
|
||
|
||
void hid_set_kbd_led(unsigned char led, bool on);
|
||
uint8_t hid_get_joysticks(void);
|
||
|
||
#endif // HID_H
|
firmware/usb/hidparser.c | ||
---|---|---|
// http://www.frank-zhao.com/cache/hid_tutorial_1.php
|
||
|
||
#include <inttypes.h>
|
||
#include <stdbool.h>
|
||
#include <stdio.h>
|
||
|
||
#include "hidparser.h"
|
||
#include "debug.h"
|
||
|
||
#if 1
|
||
#define hidp_extreme_debugf(...) hidp_debugf(__VA_ARGS__)
|
||
#else
|
||
#define hidp_extreme_debugf(...)
|
||
#endif
|
||
|
||
typedef struct {
|
||
uint8_t bSize: 2;
|
||
uint8_t bType: 2;
|
||
uint8_t bTag: 4;
|
||
} __attribute__((packed)) item_t;
|
||
|
||
// flags for joystick components required
|
||
#define JOYSTICK_REQ_AXIS_X 0x01
|
||
#define JOYSTICK_REQ_AXIS_Y 0x02
|
||
#define JOYSTICK_REQ_BTN_0 0x04
|
||
#define JOYSTICK_COMPLETE (JOYSTICK_REQ_AXIS_X | JOYSTICK_REQ_AXIS_Y | JOYSTICK_REQ_BTN_0)
|
||
|
||
#define USAGE_PAGE_GENERIC_DESKTOP 1
|
||
#define USAGE_PAGE_SIMULATION 2
|
||
#define USAGE_PAGE_VR 3
|
||
#define USAGE_PAGE_SPORT 4
|
||
#define USAGE_PAGE_GAMING 5
|
||
#define USAGE_PAGE_GENERIC_DEVICE 6
|
||
#define USAGE_PAGE_KEYBOARD 7
|
||
#define USAGE_PAGE_LEDS 8
|
||
#define USAGE_PAGE_BUTTON 9
|
||
#define USAGE_PAGE_ORDINAL 10
|
||
#define USAGE_PAGE_TELEPHONY 11
|
||
#define USAGE_PAGE_CONSUMER 12
|
||
|
||
|
||
#define USAGE_POINTER 1
|
||
#define USAGE_MOUSE 2
|
||
#define USAGE_JOYSTICK 4
|
||
#define USAGE_GAMEPAD 5
|
||
#define USAGE_KEYBOARD 6
|
||
#define USAGE_KEYPAD 7
|
||
#define USAGE_MULTIAXIS 8
|
||
|
||
#define USAGE_X 48
|
||
#define USAGE_Y 49
|
||
#define USAGE_Z 50
|
||
#define USAGE_WHEEL 56
|
||
|
||
bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf) {
|
||
int8_t app_collection = 0;
|
||
int8_t phys_log_collection = 0;
|
||
uint8_t skip_collection = 0;
|
||
int8_t generic_desktop = -1; // depth at which first gen_desk was found
|
||
uint8_t collection_depth = 0;
|
||
|
||
uint8_t i;
|
||
|
||
//
|
||
uint8_t report_size, report_count;
|
||
uint16_t bit_count = 0, usage_count = 0;
|
||
uint16_t logical_minimum=0, logical_maximum=0;
|
||
|
||
// mask used to check of all required components have been found, so
|
||
// that e.g. both axes and the button of a joystick are ready to be used
|
||
uint8_t setup_complete = 0;
|
||
|
||
// joystick/mouse components
|
||
int8_t axis[2] = { -1, -1};
|
||
uint8_t btns = 0;
|
||
|
||
conf->type = CONFIG_TYPE_NONE;
|
||
|
||
while(rep_size) {
|
||
// extract short item
|
||
uint8_t tag = ((item_t*)rep)->bTag;
|
||
uint8_t type = ((item_t*)rep)->bType;
|
||
uint8_t size = ((item_t*)rep)->bSize;
|
||
|
||
rep++;
|
||
rep_size--; // one byte consumed
|
||
|
||
uint32_t value = 0;
|
||
if(size) { // size 1/2/3
|
||
value = *rep++;
|
||
rep_size--;
|
||
}
|
||
|
||
if(size > 1) { // size 2/3
|
||
value = (value & 0xff) + ((uint32_t)(*rep++)<<8);
|
||
rep_size--;
|
||
}
|
||
|
||
if(size > 2) { // size 3
|
||
value &= 0xffff;
|
||
value |= ((uint32_t)(*rep++)<<16);
|
||
value |= ((uint32_t)(*rep++)<<24);
|
||
rep_size-=2;
|
||
}
|
||
|
||
// hidp_extreme_debugf("Value = %d (%u)\n", value, value);
|
||
|
||
// we are currently skipping an unknown/unsupported collection)
|
||
if(skip_collection) {
|
||
if(!type) { // main item
|
||
// any new collection increases the depth of collections to skip
|
||
if(tag == 10) {
|
||
skip_collection++;
|
||
collection_depth++;
|
||
}
|
||
|
||
// any end collection decreases it
|
||
if(tag == 12) {
|
||
skip_collection--;
|
||
collection_depth--;
|
||
|
||
// leaving the depth the generic desktop was valid for
|
||
if(generic_desktop > collection_depth)
|
||
generic_desktop = -1;
|
||
}
|
||
}
|
||
|
||
|
||
} else {
|
||
// hidp_extreme_debugf("-> Item tag=%d type=%d size=%d\n", tag, type, size);
|
||
|
||
switch(type) {
|
||
case 0:
|
||
// main item
|
||
|
||
switch(tag) {
|
||
case 8:
|
||
//
|
||
if(btns) {
|
||
if(conf->type == CONFIG_TYPE_JOYSTICK) {
|
||
// scan for up to four buttons
|
||
char b;
|
||
for(b=0;b<4;b++) {
|
||
if(report_count > b) {
|
||
uint16_t this_bit = bit_count+b;
|
||
|
||
hidp_debugf("BUTTON%d @ %d (byte %d, mask %d)\n", b,
|
||
this_bit, this_bit/8, 1 << (this_bit%8));
|
||
|
||
conf->joystick.button[b].byte_offset = this_bit/8;
|
||
conf->joystick.button[b].bitmask = 1 << (this_bit%8);
|
||
}
|
||
}
|
||
|
||
// we found at least one button which is all we want to accept this as a valid
|
||
// joystick
|
||
setup_complete |= JOYSTICK_REQ_BTN_0;
|
||
}
|
||
}
|
||
|
||
//
|
||
char c;
|
||
for(c=0;c<2;c++) {
|
||
if(axis[c] >= 0) {
|
||
uint16_t cnt = bit_count + report_size * axis[c];
|
||
hidp_debugf(" (%c-AXIS @ %d (byte %d))\n", 'X'+c,
|
||
cnt, cnt/8);
|
||
|
||
// only 8/16 bit axes at byte boundaries are supported for
|
||
// joysticks
|
||
if((conf->type == CONFIG_TYPE_JOYSTICK) &&
|
||
((report_size == 8) || (report_size == 16)) && ((cnt&7) == 0)) {
|
||
// save in joystick config
|
||
conf->joystick.axis[c].byte_offset = cnt/8;
|
||
conf->joystick.axis[c].size = report_size;
|
||
conf->joystick.axis[c].logical.min = logical_minimum;
|
||
conf->joystick.axis[c].logical.max = logical_maximum;
|
||
conf->joystick.axis[c].size = report_size;
|
||
if(c==0) setup_complete |= JOYSTICK_REQ_AXIS_X;
|
||
if(c==1) setup_complete |= JOYSTICK_REQ_AXIS_Y;
|
||
}
|
||
|
||
if((report_size != 8) && (report_size != 16))
|
||
hidp_debugf("Unsupported report size %d\n", report_size);
|
||
|
||
if((cnt&7) != 0)
|
||
hidp_debugf("Unsupported bit offset %d\n", cnt&7);
|
||
}
|
||
}
|
||
|
||
hidp_extreme_debugf("INPUT(%d)\n", value);
|
||
|
||
// reset for next inputs
|
||
bit_count += report_count * report_size;
|
||
usage_count = 0;
|
||
btns = 0;
|
||
axis[0] = axis[1] = -1;
|
||
break;
|
||
|
||
case 9:
|
||
hidp_extreme_debugf("OUTPUT(%d)\n", value);
|
||
break;
|
||
|
||
case 11:
|
||
hidp_extreme_debugf("FEATURE(%d)\n", value);
|
||
break;
|
||
|
||
case 10:
|
||
hidp_extreme_debugf("COLLECTION(%d)\n", value);
|
||
collection_depth++;
|
||
usage_count = 0;
|
||
|
||
if(value == 1) { // app collection
|
||
hidp_extreme_debugf(" -> application\n");
|
||
app_collection++;
|
||
} else if(value == 0) { // physical collection
|
||
hidp_extreme_debugf(" -> physical\n");
|
||
phys_log_collection++;
|
||
} else if(value == 2) { // logical collection
|
||
hidp_extreme_debugf(" -> logical\n");
|
||
phys_log_collection++;
|
||
} else {
|
||
hidp_extreme_debugf("skipping unsupported collection\n");
|
||
skip_collection++;
|
||
}
|
||
break;
|
||
|
||
case 12:
|
||
hidp_extreme_debugf("END_COLLECTION(%d)\n", value);
|
||
collection_depth--;
|
||
|
||
// leaving the depth the generic desktop was valid for
|
||
if(generic_desktop > collection_depth)
|
||
generic_desktop = -1;
|
||
|
||
if(phys_log_collection) {
|
||
hidp_extreme_debugf(" -> phys/log end\n");
|
||
phys_log_collection--;
|
||
} else if(app_collection) {
|
||
hidp_extreme_debugf(" -> app end\n");
|
||
app_collection--;
|
||
} else {
|
||
hidp_debugf(" -> unexpected\n");
|
||
return false;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
hidp_debugf("unexpected main item %d\n", tag);
|
||
return false;
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case 1:
|
||
// global item
|
||
switch(tag) {
|
||
case 0:
|
||
hidp_extreme_debugf("USAGE_PAGE(%d/0x%x)\n", value, value);
|
||
|
||
if(value == USAGE_PAGE_KEYBOARD) {
|
||
hidp_extreme_debugf(" -> Keyboard\n");
|
||
} else if(value == USAGE_PAGE_GAMING) {
|
||
hidp_extreme_debugf(" -> Game device\n");
|
||
} else if(value == USAGE_PAGE_LEDS) {
|
||
hidp_extreme_debugf(" -> LEDs\n");
|
||
} else if(value == USAGE_PAGE_CONSUMER) {
|
||
hidp_extreme_debugf(" -> Consumer\n");
|
||
} else if(value == USAGE_PAGE_BUTTON) {
|
||
hidp_extreme_debugf(" -> Buttons\n");
|
||
btns = 1;
|
||
} else if(value == USAGE_PAGE_GENERIC_DESKTOP) {
|
||
hidp_extreme_debugf(" -> Generic Desktop\n");
|
||
|
||
if(generic_desktop < 0)
|
||
generic_desktop = collection_depth;
|
||
} else
|
||
hidp_extreme_debugf(" -> UNSUPPORTED USAGE_PAGE\n");
|
||
|
||
break;
|
||
|
||
case 1:
|
||
hidp_extreme_debugf("LOGICAL_MINIMUM(%d/%d)\n", value, (int8_t)value);
|
||
logical_minimum = value;
|
||
break;
|
||
|
||
case 2:
|
||
hidp_extreme_debugf("LOGICAL_MAXIMUM(%d)\n", value);
|
||
logical_maximum = value;
|
||
break;
|
||
|
||
case 3:
|
||
hidp_extreme_debugf("PHYSICAL_MINIMUM(%d/%d)\n", value, (int8_t)value);
|
||
break;
|
||
|
||
case 4:
|
||
hidp_extreme_debugf("PHYSICAL_MAXIMUM(%d)\n", value);
|
||
break;
|
||
|
||
case 5:
|
||
hidp_extreme_debugf("UNIT_EXPONENT(%d)\n", value);
|
||
break;
|
||
|
||
case 6:
|
||
hidp_extreme_debugf("UNIT(%d)\n", value);
|
||
break;
|
||
|
||
case 7:
|
||
hidp_extreme_debugf("REPORT_SIZE(%d)\n", value);
|
||
report_size = value;
|
||
break;
|
||
|
||
case 8:
|
||
hidp_extreme_debugf("REPORT_ID(%d)\n", value);
|
||
conf->report_id = value;
|
||
break;
|
||
|
||
case 9:
|
||
hidp_extreme_debugf("REPORT_COUNT(%d)\n", value);
|
||
report_count = value;
|
||
break;
|
||
|
||
default:
|
||
hidp_debugf("unexpected global item %d\n", tag);
|
||
return false;
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case 2:
|
||
// local item
|
||
switch(tag) {
|
||
case 0:
|
||
// we only support mice, keyboards and joysticks
|
||
hidp_extreme_debugf("USAGE(%d/0x%x)\n", value, value);
|
||
|
||
if( !collection_depth && (value == USAGE_KEYBOARD)) {
|
||
// usage(keyboard) is always allowed
|
||
hidp_debugf(" -> Keyboard\n");
|
||
conf->type = CONFIG_TYPE_KEYBOARD;
|
||
} else if(!collection_depth && (value == USAGE_MOUSE)) {
|
||
// usage(mouse) is always allowed
|
||
hidp_debugf(" -> Mouse\n");
|
||
conf->type = CONFIG_TYPE_MOUSE;
|
||
} else if(!collection_depth &&
|
||
((value == USAGE_GAMEPAD) || (value == USAGE_JOYSTICK))) {
|
||
hidp_extreme_debugf(" -> Gamepad/Joystick\n");
|
||
hidp_debugf("Gamepad/Joystick usage found\n");
|
||
conf->type = CONFIG_TYPE_JOYSTICK;
|
||
} else if(value == USAGE_POINTER && app_collection) {
|
||
// usage(pointer) is allowed within the application collection
|
||
|
||
hidp_debugf(" -> Pointer\n");
|
||
|
||
} else if((value == USAGE_X || value == USAGE_Y) && app_collection) {
|
||
// usage(x) and usage(y) are allowed within the app collection
|
||
hidp_extreme_debugf(" -> axis usage\n");
|
||
|
||
// we support x and y axis on joysticks
|
||
if(conf->type == CONFIG_TYPE_JOYSTICK) {
|
||
if(value == USAGE_X) {
|
||
hidp_extreme_debugf("JOYSTICK: found x axis @ %d\n", usage_count);
|
||
axis[0] = usage_count;
|
||
}
|
||
if(value == USAGE_Y) {
|
||
hidp_extreme_debugf("JOYSTICK: found y axis @ %d\n", usage_count);
|
||
axis[1] = usage_count;
|
||
}
|
||
}
|
||
} else {
|
||
hidp_extreme_debugf(" -> UNSUPPORTED USAGE\n");
|
||
// return false;
|
||
}
|
||
|
||
usage_count++;
|
||
break;
|
||
|
||
case 1:
|
||
hidp_extreme_debugf("USAGE_MINIMUM(%d)\n", value);
|
||
usage_count -= (value-1);
|
||
break;
|
||
|
||
case 2:
|
||
hidp_extreme_debugf("USAGE_MAXIMUM(%d)\n", value);
|
||
usage_count += value;
|
||
break;
|
||
|
||
default:
|
||
hidp_extreme_debugf("unexpected local item %d\n", tag);
|
||
// return false;
|
||
break;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
// reserved
|
||
hidp_extreme_debugf("unexpected resreved item %d\n", tag);
|
||
// return false;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
hidp_debugf("total bit count: %d (%d bytes, %d bits)\n",
|
||
bit_count, bit_count/8, bit_count%8);
|
||
|
||
conf->report_size = bit_count/8;
|
||
|
||
// check if something useful was detected
|
||
if(conf->type == CONFIG_TYPE_JOYSTICK) {
|
||
if(setup_complete == JOYSTICK_COMPLETE) {
|
||
hidp_debugf("Joystick ok\n");
|
||
return true;
|
||
}
|
||
|
||
hidp_debugf("Ignoring incomplete joystick %x\n", setup_complete);
|
||
} else
|
||
hidp_debugf("No joystick\n");
|
||
|
||
return false;
|
||
}
|
firmware/usb/hidparser.h | ||
---|---|---|
#ifndef HIDPARSER_H
|
||
#define HIDPARSER_H
|
||
|
||
#define CONFIG_TYPE_NONE 0
|
||
#define CONFIG_TYPE_MOUSE 1
|
||
#define CONFIG_TYPE_KEYBOARD 2
|
||
#define CONFIG_TYPE_JOYSTICK 3
|
||
|
||
// currently only joysticks are supported
|
||
typedef struct {
|
||
uint8_t type: 2; // CONFIG_TYPE_...
|
||
uint8_t report_id;
|
||
uint8_t report_size;
|
||
|
||
union {
|
||
struct {
|
||
struct {
|
||
uint8_t byte_offset;
|
||
uint8_t size; // 8 or 16 bits supported
|
||
struct {
|
||
uint16_t min;
|
||
uint16_t max;
|
||
} logical;
|
||
} axis[2]; // x and y axis
|
||
|
||
struct {
|
||
uint8_t byte_offset;
|
||
uint8_t bitmask;
|
||
} button[4]; // 4 buttons
|
||
} joystick;
|
||
};
|
||
} hid_config_t;
|
||
|
||
bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf);
|
||
|
||
#endif // HIDPARSER_H
|
firmware/usb/hub.c | ||
---|---|---|
#include <stdio.h>
|
||
|
||
#include "usb.h"
|
||
#include "timer.h"
|
||
#include "debug.h"
|
||
|
||
static uint8_t usb_hub_clear_hub_feature(usb_device_t *dev, uint8_t fid ) {
|
||
return( usb_ctrl_req( dev, USB_HUB_REQ_CLEAR_HUB_FEATURE,
|
||
USB_REQUEST_CLEAR_FEATURE, fid, 0, 0, 0, NULL));
|
||
}
|
||
|
||
// Clear Port Feature
|
||
static uint8_t usb_hub_clear_port_feature(usb_device_t *dev, uint8_t fid, uint8_t port, uint8_t sel ) {
|
||
return( usb_ctrl_req( dev , USB_HUB_REQ_CLEAR_PORT_FEATURE,
|
||
USB_REQUEST_CLEAR_FEATURE, fid, 0, ((0x0000|port)|(sel<<8)), 0, NULL));
|
||
}
|
||
// Get Hub Descriptor
|
||
static uint8_t usb_hub_get_hub_descriptor(usb_device_t *dev, uint8_t index,
|
||
uint16_t nbytes, usb_hub_descriptor_t *dataptr ) {
|
||
return( usb_ctrl_req( dev, USB_HUB_REQ_GET_HUB_DESCRIPTOR,
|
||
USB_REQUEST_GET_DESCRIPTOR, index, 0x29, 0, nbytes, (uint8_t*)dataptr));
|
||
}
|
||
|
||
// Set Port Feature
|
||
static uint8_t usb_hub_set_port_feature(usb_device_t *dev, uint8_t fid, uint8_t port, uint8_t sel ) {
|
||
return( usb_ctrl_req( dev, USB_HUB_REQ_SET_PORT_FEATURE,
|
||
USB_REQUEST_SET_FEATURE, fid, 0, (((0x0000|sel)<<8)|port), 0, NULL));
|
||
}
|
||
|
||
// Get Port Status
|
||
static uint8_t usb_hub_get_port_status(usb_device_t *dev, uint8_t port, uint16_t nbytes, uint8_t* dataptr ) {
|
||
return( usb_ctrl_req( dev, USB_HUB_REQ_GET_PORT_STATUS,
|
||
USB_REQUEST_GET_STATUS, 0, 0, port, nbytes, dataptr));
|
||
}
|
||
|
||
static uint8_t usb_hub_init(usb_device_t *dev) {
|
||
iprintf("%s()\n", __FUNCTION__);
|
||
|
||
uint8_t rcode;
|
||
uint8_t i;
|
||
|
||
usb_hub_info_t *info = &(dev->hub_info);
|
||
|
||
union {
|
||
usb_device_descriptor_t dev_desc;
|
||
usb_configuration_descriptor_t conf_desc;
|
||
usb_hub_descriptor_t hub_desc;
|
||
} buf;
|
||
|
||
// reset status
|
||
info->bNbrPorts = 0;
|
||
info->qNextPollTime = 0;
|
||
info->bPollEnable = false;
|
||
|
||
info->ep.epAddr = 1;
|
||
info->ep.maxPktSize = 8; //kludge
|
||
info->ep.epAttribs = 0;
|
||
info->ep.bmNakPower = USB_NAK_NOWAIT;
|
||
|
||
rcode = usb_get_dev_descr( dev, 8, &buf.dev_desc );
|
||
if( rcode ) {
|
||
puts("failed to get device descriptor 1");
|
||
return rcode;
|
||
}
|
||
|
||
// Extract device class from device descriptor
|
||
// If device class is not a hub return
|
||
if (buf.dev_desc.bDeviceClass != USB_CLASS_HUB) {
|
||
puts("not a hub!");
|
||
return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
|
||
}
|
||
|
||
// try to re-read full device descriptor from newly assigned address
|
||
if(rcode = usb_get_dev_descr( dev, sizeof(usb_device_descriptor_t), &buf.dev_desc )) {
|
||
puts("failed to get device descriptor 2");
|
||
return rcode;
|
||
}
|
||
|
||
// Get hub descriptor
|
||
rcode = usb_hub_get_hub_descriptor(dev, 0, 8, &buf.hub_desc);
|
||
|
||
if (rcode) {
|
||
puts("failed to get hub descriptor");
|
||
return rcode;
|
||
}
|
||
|
||
// Save number of ports for future use
|
||
info->bNbrPorts = buf.hub_desc.bNbrPorts;
|
||
|
||
// Read configuration Descriptor in Order To Obtain Proper Configuration Value
|
||
rcode = usb_get_conf_descr(dev, sizeof(usb_configuration_descriptor_t), 0, &buf.conf_desc);
|
||
if (rcode) {
|
||
puts("failed to read configuration descriptor");
|
||
return rcode;
|
||
}
|
||
|
||
// Set Configuration Value
|
||
rcode = usb_set_conf(dev, buf.conf_desc.bConfigurationValue);
|
||
if (rcode) {
|
||
iprintf("failed to set configuration to %d\n", buf.conf_desc.bConfigurationValue);
|
||
return rcode;
|
||
}
|
||
|
||
// Power on all ports
|
||
for (i=1; i<=info->bNbrPorts; i++)
|
||
usb_hub_set_port_feature(dev, HUB_FEATURE_PORT_POWER, i, 0); // HubPortPowerOn(i);
|
||
|
||
if(!dev->parent)
|
||
usb_SetHubPreMask();
|
||
|
||
info->bPollEnable = true;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static uint8_t usb_hub_release(usb_device_t *dev) {
|
||
puts(__FUNCTION__);
|
||
|
||
// root hub unplugged
|
||
if(!dev->parent)
|
||
usb_ResetHubPreMask();
|
||
|
||
return 0;
|
||
}
|
||
|
||
static void usb_hub_show_port_status(uint8_t port, uint16_t status, uint16_t changed) {
|
||
iprintf("Status of port %d:\n", port);
|
||
|
||
if(status & USB_HUB_PORT_STATUS_PORT_CONNECTION) puts(" connected");
|
||
if(status & USB_HUB_PORT_STATUS_PORT_ENABLE) puts(" enabled");
|
||
if(status & USB_HUB_PORT_STATUS_PORT_SUSPEND) puts(" suspended");
|
||
if(status & USB_HUB_PORT_STATUS_PORT_OVER_CURRENT) puts(" over current");
|
||
if(status & USB_HUB_PORT_STATUS_PORT_RESET) puts(" reset");
|
||
if(status & USB_HUB_PORT_STATUS_PORT_POWER) puts(" powered");
|
||
if(status & USB_HUB_PORT_STATUS_PORT_LOW_SPEED) puts(" low speed");
|
||
if(status & USB_HUB_PORT_STATUS_PORT_HIGH_SPEED) puts(" high speed");
|
||
if(status & USB_HUB_PORT_STATUS_PORT_TEST) puts(" test");
|
||
if(status & USB_HUB_PORT_STATUS_PORT_INDICATOR) puts(" indicator");
|
||
|
||
iprintf("Changes on port %d:\n", port);
|
||
if(changed & USB_HUB_PORT_STATUS_PORT_CONNECTION) puts(" connected");
|
||
if(changed & USB_HUB_PORT_STATUS_PORT_ENABLE) puts(" enabled");
|
||
if(changed & USB_HUB_PORT_STATUS_PORT_SUSPEND) puts(" suspended");
|
||
if(changed & USB_HUB_PORT_STATUS_PORT_OVER_CURRENT) puts(" over current");
|
||
if(changed & USB_HUB_PORT_STATUS_PORT_RESET) puts(" reset");
|
||
}
|
||
|
||
static uint8_t usb_hub_port_status_change(usb_device_t *dev, uint8_t port, hub_event_t evt) {
|
||
usb_hub_info_t *info = &(dev->hub_info);
|
||
|
||
iprintf("status change on port %d, 0x%x\n", port, evt.bmEvent);
|
||
usb_hub_show_port_status(port, evt.bmStatus, evt.bmChange);
|
||
|
||
static bool bResetInitiated = false;
|
||
|
||
switch (evt.bmEvent) {
|
||
// Device connected event
|
||
case USB_HUB_PORT_EVENT_CONNECT:
|
||
case USB_HUB_PORT_EVENT_LS_CONNECT:
|
||
iprintf(" dev %x: port %d connect!\n", dev->bAddress, port);
|
||
|
||
if (bResetInitiated) {
|
||
iprintf("reset already in progress\n");
|
||
return 0;
|
||
}
|
||
|
||
// timer_delay_msec(100);
|
||
|
||
iprintf("resetting port %d\n", port);
|
||
usb_hub_clear_port_feature(dev, HUB_FEATURE_C_PORT_ENABLE, port, 0);
|
||
usb_hub_clear_port_feature(dev, HUB_FEATURE_C_PORT_CONNECTION, port, 0);
|
||
usb_hub_set_port_feature(dev, HUB_FEATURE_PORT_RESET, port, 0);
|
||
bResetInitiated = true;
|
||
return HUB_ERROR_PORT_HAS_BEEN_RESET;
|
||
|
||
// Device disconnected event
|
||
case USB_HUB_PORT_EVENT_DISCONNECT:
|
||
iprintf(" port %d disconnect!\n", port);
|
||
|
||
usb_hub_clear_port_feature(dev, HUB_FEATURE_C_PORT_ENABLE, port, 0);
|
||
usb_hub_clear_port_feature(dev, HUB_FEATURE_C_PORT_CONNECTION, port, 0);
|
||
bResetInitiated = false;
|
||
|
||
usb_release_device(dev->bAddress, port);
|
||
|
||
return 0;
|
||
|
||
// Reset complete event
|
||
case USB_HUB_PORT_EVENT_RESET_COMPLETE:
|
||
case USB_HUB_PORT_EVENT_LS_RESET_COMPLETE:
|
||
iprintf(" port %d reset complete!\n", port);
|
||
usb_hub_clear_port_feature(dev, HUB_FEATURE_C_PORT_RESET, port, 0);
|
||
usb_hub_clear_port_feature(dev, HUB_FEATURE_C_PORT_CONNECTION, port, 0);
|
||
|
||
usb_configure(dev->bAddress, port,
|
||
(evt.bmStatus & USB_HUB_PORT_STATUS_PORT_LOW_SPEED)!=0 );
|
||
|
||
bResetInitiated = false;
|
||
break;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static uint8_t usb_hub_check_hub_status(usb_device_t *dev, uint8_t ports) {
|
||
usb_hub_info_t *info = &(dev->hub_info);
|
||
|
||
uint8_t rcode;
|
||
uint8_t buf[8];
|
||
uint16_t read = 1;
|
||
|
||
// iprintf("%s(addr=%x)\n", __FUNCTION__, dev->bAddress);
|
||
|
||
rcode = usb_in_transfer(dev, &(info->ep), &read, buf);
|
||
if(rcode)
|
||
return rcode;
|
||
|
||
uint8_t port, mask;
|
||
for(port=1,mask=0x02; port<8; mask<<=1, port++) {
|
||
if (buf[0] & mask) {
|
||
hub_event_t evt;
|
||
evt.bmEvent = 0;
|
||
|
||
rcode = usb_hub_get_port_status(dev, port, sizeof(evt.evtBuff), evt.evtBuff);
|
||
if (rcode)
|
||
continue;
|
||
|
||
rcode = usb_hub_port_status_change(dev, port, evt);
|
||
|
||
if (rcode == HUB_ERROR_PORT_HAS_BEEN_RESET)
|
||
return 0;
|
||
|
||
if (rcode)
|
||
return rcode;
|
||
}
|
||
} // for
|
||
|
||
for (port=1; port<=ports; port++) {
|
||
hub_event_t evt;
|
||
evt.bmEvent = 0;
|
||
|
||
rcode = usb_hub_get_port_status(dev, port, 4, evt.evtBuff);
|
||
if (rcode)
|
||
continue;
|
||
|
||
if ((evt.bmStatus & USB_HUB_PORT_STATE_CHECK_DISABLED) != USB_HUB_PORT_STATE_DISABLED)
|
||
continue;
|
||
|
||
// Emulate connection event for the port
|
||
evt.bmChange |= USB_HUB_PORT_STATUS_PORT_CONNECTION;
|
||
|
||
rcode = usb_hub_port_status_change(dev, port, evt);
|
||
if (rcode == HUB_ERROR_PORT_HAS_BEEN_RESET)
|
||
return 0;
|
||
|
||
if (rcode)
|
||
return rcode;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static uint8_t usb_hub_poll(usb_device_t *dev) {
|
||
usb_hub_info_t *info = &(dev->hub_info);
|
||
|
||
uint8_t rcode = 0;
|
||
|
||
if (!info->bPollEnable)
|
||
return 0;
|
||
|
||
if (info->qNextPollTime <= timer_get_msec()) {
|
||
rcode = usb_hub_check_hub_status(dev, info->bNbrPorts);
|
||
info->qNextPollTime = timer_get_msec() + 100; // poll 10 times a second
|
||
}
|
||
|
||
return rcode;
|
||
}
|
||
|
||
const usb_device_class_config_t usb_hub_class = {
|
||
usb_hub_init, usb_hub_release, usb_hub_poll };
|
||
|
firmware/usb/hub.h | ||
---|---|---|
#ifndef HUB_H
|
||
#define HUB_H
|
||
|
||
#include <stdbool.h>
|
||
#include <inttypes.h>
|
||
|
||
typedef struct {
|
||
uint8_t bNbrPorts; // number of ports
|
||
uint32_t qNextPollTime; // next poll time
|
||
bool bPollEnable; // poll enable flag
|
||
ep_t ep; // interrupt endpoint info structure
|
||
} usb_hub_info_t;
|
||
|
||
// Hub Requests
|
||
#define USB_HUB_REQ_CLEAR_HUB_FEATURE USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
|
||
#define USB_HUB_REQ_CLEAR_PORT_FEATURE USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
|
||
#define USB_HUB_REQ_CLEAR_TT_BUFFER USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
|
||
#define USB_HUB_REQ_GET_HUB_DESCRIPTOR USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
|
||
#define USB_HUB_REQ_GET_HUB_STATUS USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
|
||
#define USB_HUB_REQ_GET_PORT_STATUS USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
|
||
#define USB_HUB_REQ_RESET_TT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
|
||
#define USB_HUB_REQ_SET_HUB_DESCRIPTOR USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
|
||
#define USB_HUB_REQ_SET_HUB_FEATURE USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
|
||
#define USB_HUB_REQ_SET_PORT_FEATURE USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
|
||
#define USB_HUB_REQ_GET_TT_STATE USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
|
Also available in: Unified diff
Untested support for usbhostslave from opencores. Based on working usb code in mist.