1
0
mirror of https://github.com/mist-devel/mist-firmware.git synced 2026-04-29 05:26:02 +00:00

[FIRMWARE] More beta HID parser work. Fixing Rii wireless keyboard/touchpad

This commit is contained in:
harbaum
2015-04-27 19:18:09 +00:00
parent 6640dc5547
commit 0aea286549
4 changed files with 132 additions and 93 deletions

135
usb/hid.c
View File

@@ -1,5 +1,6 @@
#include <stdio.h>
#include "string.h"
#include "usb.h"
#include "max3421e.h"
#include "timer.h"
@@ -30,7 +31,7 @@ static uint8_t hid_get_report_descr(usb_device_t *dev, uint8_t i, uint16_t size)
// 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) {
if(info->iface[i].conf.type == REPORT_TYPE_JOYSTICK) {
hid_debugf("Detected USB joystick #%d", joysticks);
info->iface[i].device_type = HID_DEVICE_JOYSTICK;
@@ -38,7 +39,7 @@ static uint8_t hid_get_report_descr(usb_device_t *dev, uint8_t i, uint16_t size)
}
} else {
// parsing failed. Fall back to boot mode for mice
if(info->iface[i].conf.type == CONFIG_TYPE_MOUSE) {
if(info->iface[i].conf.type == REPORT_TYPE_MOUSE) {
hid_debugf("Failed to parse mouse, try using boot mode");
info->iface[i].ignore_boot_mode = false;
}
@@ -118,7 +119,7 @@ static uint8_t usb_hid_parse_conf(usb_device_t *dev, uint8_t conf, uint16_t len)
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;
info->iface[info->bNumIfaces].conf.type = REPORT_TYPE_NONE;
if(p->iface_desc.bInterfaceSubClass == HID_BOOT_INTF_SUBCLASS) {
// hid_debugf("Iface %d is Boot sub class", info->bNumIfaces);
@@ -137,7 +138,8 @@ static uint8_t usb_hid_parse_conf(usb_device_t *dev, uint8_t conf, uint16_t len)
case HID_PROTOCOL_MOUSE:
hid_debugf("HID protocol is MOUSE");
info->iface[info->bNumIfaces].ignore_boot_mode = true; // don't use boot mode for mice
// don't use boot mode for mice
info->iface[info->bNumIfaces].ignore_boot_mode = true;
info->iface[info->bNumIfaces].device_type = HID_DEVICE_MOUSE;
break;
@@ -262,20 +264,20 @@ static uint8_t usb_hid_init(usb_device_t *dev) {
// process all supported interfaces
for(i=0; i<info->bNumIfaces; i++) {
// no boot mode, try to parse HID report descriptor
// when running archie core force the usage of the HID descriptor as boot mode only
// supports two buttons and the archie wants three
// when running archie core force the usage of the HID descriptor as
// boot mode only supports two buttons and the archie wants three
if(!info->iface[i].has_boot_mode || info->iface[i].ignore_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_MOUSE) {
if(info->iface[i].device_type == REPORT_TYPE_MOUSE) {
iprintf("MOUSE: 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);
}
if(info->iface[i].device_type == CONFIG_TYPE_JOYSTICK) {
if(info->iface[i].device_type == REPORT_TYPE_JOYSTICK) {
char k;
iprintf("JOYSTICK: report type = %d, id = %d, size = %d\n",
@@ -332,7 +334,8 @@ static uint8_t usb_hid_init(usb_device_t *dev) {
if(info->iface[i].has_boot_mode && !info->iface[i].ignore_boot_mode) {
hid_debugf("enabling boot mode");
hid_set_protocol(dev, info->iface[i].iface_idx, HID_BOOT_PROTOCOL);
}
} else
hid_set_protocol(dev, info->iface[i].iface_idx, HID_RPT_PROTOCOL);
}
puts("HID configured");
@@ -444,6 +447,46 @@ static void handle_5200daptor(usb_hid_iface_info_t *iface, uint8_t *buf) {
}
}
// collect bits from byte stream and assemble them into a signed word
static uint16_t collect_bits(uint8_t *p, uint16_t offset, uint8_t size, bool is_signed) {
// mask unused bits of first byte
uint8_t mask = 0xff << (offset&7);
uint8_t byte = offset/8;
uint8_t bits = size;
uint8_t shift = offset&7;
// iprintf("%c0 m:%x by:%d bi=%d sh=%d ->", 'X'+i, mask, byte, bits, shift);
uint16_t rval = (p[byte++] & mask) >> shift;
// iprintf("%d\n", (int16_t)a[i]);
mask = 0xff;
shift = 8-shift;
bits -= shift;
// further bytes if required
while(bits) {
mask = (bits<8)?(0xff>>(8-bits)):0xff;
// iprintf("%c+ m:%x by:%d bi=%d sh=%d ->", 'X'+i, mask, byte, bits, shift);
rval += (p[byte++] & mask) << shift;
// iprintf("%d\n", (int16_t)a[i]);
shift += 8;
bits -= (bits>8)?8:bits;
}
if(is_signed) {
// do sign expansion
uint16_t sign_bit = 1<<(size-1);
if(rval & sign_bit) {
while(sign_bit) {
rval |= sign_bit;
sign_bit <<= 1;
}
// iprintf("%c is negative -> sign expand to %d\n", 'X'+i, (int16_t)a[i]);
}
}
return rval;
}
static uint8_t usb_hid_poll(usb_device_t *dev) {
usb_hid_info_t *info = &(dev->hid_info);
int8_t i;
@@ -461,83 +504,65 @@ static uint8_t usb_hid_poll(usb_device_t *dev) {
uint16_t read = iface->ep.maxPktSize;
uint8_t buf[iface->ep.maxPktSize];
// clear buffer
memset(buf, 0, iface->ep.maxPktSize);
uint8_t rcode =
usb_in_transfer(dev, &(iface->ep), &read, buf);
if (rcode) {
if (rcode != hrNAK)
hid_debugf("%s() error: %d", __FUNCTION__, rcode);
} else {
// successfully received some bytes
if(iface->has_boot_mode && !iface->ignore_boot_mode) {
if(iface->device_type == HID_DEVICE_MOUSE) {
// boot mouse needs at least three bytes
if(read >= 3) {
if(read >= 3)
// forward all three bytes to the user_io layer
user_io_mouse(buf[0], buf[1], buf[2]);
}
}
if(iface->device_type == HID_DEVICE_KEYBOARD) {
// boot kbd needs at least eight bytes
if(read >= 8) {
if(read >= 8)
user_io_kbd(buf[0], buf+2);
}
}
}
// use more complex parser for all joysticks. Use it for mice only if
// it's explicitely stated not to use boot mode
if((iface->device_type == HID_DEVICE_JOYSTICK) ||
((iface->device_type == HID_DEVICE_MOUSE) && iface->ignore_boot_mode)) {
hid_config_t *conf = &iface->conf;
if(read >= conf->report_size) {
((iface->device_type == HID_DEVICE_MOUSE) &&
iface->ignore_boot_mode)) {
hid_report_t *conf = &iface->conf;
// check size of report. If a report id was given then one
// additional byte is present with a matching report id
if((read == conf->report_size+(conf->report_id?1:0)) &&
(!conf->report_id || (buf[0] == conf->report_id))) {
uint8_t btn = 0, jmap = 0;
uint16_t a[2];
int16_t a[2];
uint8_t idx, i;
// skip report id if present
uint8_t *p = buf+(conf->report_id?1:0);
// hid_debugf("data:"); hexdump(buf, read, 0);
// two axes ...
for(i=0;i<2;i++) {
// mask unused bits of first byte
uint8_t mask = 0xff << (conf->joystick_mouse.axis[i].offset&7);
uint8_t byte = conf->joystick_mouse.axis[i].offset/8;
uint8_t bits = conf->joystick_mouse.axis[i].size;
uint8_t shift = conf->joystick_mouse.axis[i].offset&7;
// iprintf("%c0 m:%x by:%d bi=%d sh=%d ->", 'X'+i, mask, byte, bits, shift);
a[i] = (buf[byte++] & mask) >> shift;
// iprintf("%d\n", (int16_t)a[i]);
mask = 0xff;
shift = 8-shift;
bits -= shift;
// further bytes if required
while(bits) {
mask = (bits<8)?(0xff>>(8-bits)):0xff;
// iprintf("%c+ m:%x by:%d bi=%d sh=%d ->", 'X'+i, mask, byte, bits, shift);
a[i] += (buf[byte++] & mask) << shift;
// iprintf("%d\n", (int16_t)a[i]);
shift += 8;
bits -= (bits>8)?8:bits;
}
// do sign expansion
uint16_t sign_bit = 1<<(conf->joystick_mouse.axis[i].size-1);
if(a[i] & sign_bit) {
while(sign_bit) {
a[i] |= sign_bit;
sign_bit <<= 1;
}
// iprintf("%c is negative -> sign expand to %d\n", 'X'+i, (int16_t)a[i]);
}
// if logical minimum is > logical maximum then logical minimum
// is signed. This means that the value itself is also signed
bool is_signed = conf->joystick_mouse.axis[i].logical.min >
conf->joystick_mouse.axis[i].logical.max;
a[i] = collect_bits(p, conf->joystick_mouse.axis[i].offset,
conf->joystick_mouse.axis[i].size, is_signed);
}
// ... and four buttons
for(i=0;i<4;i++)
if(buf[conf->joystick_mouse.button[i].byte_offset] &
if(p[conf->joystick_mouse.button[i].byte_offset] &
conf->joystick_mouse.button[i].bitmask) btn |= (1<<i);
// ---------- process mouse -------------
@@ -564,7 +589,7 @@ static uint8_t usb_hid_poll(usb_device_t *dev) {
}
}
// iprintf("JOY X:%d Y:%d\n", a[0], a[1]);
// iprintf("JOY X:%d Y:%d\n", a[0], a[1]);
if(a[0] < 64) jmap |= JOY_LEFT;
if(a[0] > 192) jmap |= JOY_RIGHT;
@@ -582,8 +607,6 @@ static uint8_t usb_hid_poll(usb_device_t *dev) {
// 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
user_io_digital_joystick(idx, jmap);
iface->jmap = jmap;

View File

@@ -59,7 +59,7 @@ typedef struct {
// (currently only used for joysticks)
uint8_t jmap; // last reported joystick state
uint8_t jindex; // joystick index
hid_config_t conf;
hid_report_t conf;
uint8_t interval;
uint32_t qNextPollTime; // next poll time

View File

@@ -54,7 +54,25 @@ typedef struct {
#define USAGE_Z 50
#define USAGE_WHEEL 56
bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf) {
// check if the current report
bool report_is_usable(uint16_t bit_count, uint8_t report_complete, hid_report_t *conf) {
hidp_debugf(" - total bit count: %d (%d bytes, %d bits)",
bit_count, bit_count/8, bit_count%8);
conf->report_size = bit_count/8;
// check if something useful was detected
if( ((conf->type == REPORT_TYPE_JOYSTICK) && ((report_complete & JOYSTICK_COMPLETE) == JOYSTICK_COMPLETE)) ||
((conf->type == REPORT_TYPE_MOUSE) && ((report_complete & MOUSE_COMPLETE) == MOUSE_COMPLETE))) {
hidp_debugf(" - report %d is usable", conf->report_id);
return true;
}
hidp_debugf(" - unusable report %d", conf->report_id);
return false;
}
bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_report_t *conf) {
int8_t app_collection = 0;
int8_t phys_log_collection = 0;
uint8_t skip_collection = 0;
@@ -70,13 +88,13 @@ bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf
// 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;
uint8_t report_complete = 0;
// joystick/mouse components
int8_t axis[2] = { -1, -1};
uint8_t btns = 0;
conf->type = CONFIG_TYPE_NONE;
conf->type = REPORT_TYPE_NONE;
while(rep_size) {
// extract short item
@@ -139,8 +157,8 @@ bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf
case 8:
//
if(btns) {
if((conf->type == CONFIG_TYPE_JOYSTICK) ||
(conf->type == CONFIG_TYPE_MOUSE)) {
if((conf->type == REPORT_TYPE_JOYSTICK) ||
(conf->type == REPORT_TYPE_MOUSE)) {
// scan for up to four buttons
char b;
for(b=0;b<4;b++) {
@@ -157,8 +175,8 @@ bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf
// we found at least one button which is all we want to accept this as a valid
// joystick
setup_complete |= JOY_MOUSE_REQ_BTN_0;
if(report_count > 1) setup_complete |= JOY_MOUSE_REQ_BTN_1;
report_complete |= JOY_MOUSE_REQ_BTN_0;
if(report_count > 1) report_complete |= JOY_MOUSE_REQ_BTN_1;
}
}
@@ -170,15 +188,15 @@ bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf
hidp_debugf(" (%c-AXIS @ %d (byte %d, bit %d))", 'X'+c,
cnt, cnt/8, cnt&7);
if((conf->type == CONFIG_TYPE_JOYSTICK) || (conf->type == CONFIG_TYPE_MOUSE)) {
// save in joystick config
if((conf->type == REPORT_TYPE_JOYSTICK) || (conf->type == REPORT_TYPE_MOUSE)) {
// save in joystick report
conf->joystick_mouse.axis[c].offset = cnt;
conf->joystick_mouse.axis[c].size = report_size;
conf->joystick_mouse.axis[c].logical.min = logical_minimum;
conf->joystick_mouse.axis[c].logical.max = logical_maximum;
conf->joystick_mouse.axis[c].size = report_size;
if(c==0) setup_complete |= JOY_MOUSE_REQ_AXIS_X;
if(c==1) setup_complete |= JOY_MOUSE_REQ_AXIS_Y;
if(c==0) report_complete |= JOY_MOUSE_REQ_AXIS_X;
if(c==1) report_complete |= JOY_MOUSE_REQ_AXIS_Y;
}
}
}
@@ -234,6 +252,16 @@ bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf
} else if(app_collection) {
hidp_extreme_debugf(" -> app end");
app_collection--;
// check if report is usable and stop parsing if it is
if(report_is_usable(bit_count, report_complete, conf))
return true;
else {
// retry with next report
bit_count = 0;
report_complete = 0;
}
} else {
hidp_debugf(" -> unexpected");
return false;
@@ -332,16 +360,16 @@ bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf
if( !collection_depth && (value == USAGE_KEYBOARD)) {
// usage(keyboard) is always allowed
hidp_debugf(" -> Keyboard");
conf->type = CONFIG_TYPE_KEYBOARD;
conf->type = REPORT_TYPE_KEYBOARD;
} else if(!collection_depth && (value == USAGE_MOUSE)) {
// usage(mouse) is always allowed
hidp_debugf(" -> Mouse");
conf->type = CONFIG_TYPE_MOUSE;
conf->type = REPORT_TYPE_MOUSE;
} else if(!collection_depth &&
((value == USAGE_GAMEPAD) || (value == USAGE_JOYSTICK))) {
hidp_extreme_debugf(" -> Gamepad/Joystick");
hidp_debugf("Gamepad/Joystick usage found");
conf->type = CONFIG_TYPE_JOYSTICK;
conf->type = REPORT_TYPE_JOYSTICK;
} else if(value == USAGE_POINTER && app_collection) {
// usage(pointer) is allowed within the application collection
@@ -352,7 +380,7 @@ bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf
hidp_extreme_debugf(" -> axis usage");
// we support x and y axis on mice and joysticks
if((conf->type == CONFIG_TYPE_JOYSTICK) || (conf->type == CONFIG_TYPE_MOUSE)) {
if((conf->type == REPORT_TYPE_JOYSTICK) || (conf->type == REPORT_TYPE_MOUSE)) {
if(value == USAGE_X) {
hidp_extreme_debugf("JOYSTICK/MOUSE: found x axis @ %d", usage_count);
axis[0] = usage_count;
@@ -389,25 +417,13 @@ bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf
default:
// reserved
hidp_extreme_debugf("unexpected resreved item %d", tag);
hidp_extreme_debugf("unexpected reserved item %d", tag);
// return false;
break;
}
}
}
hidp_debugf("total bit count: %d (%d bytes, %d bits)",
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) && (setup_complete == JOYSTICK_COMPLETE)) ||
((conf->type == CONFIG_TYPE_MOUSE) && (setup_complete == MOUSE_COMPLETE))) {
hidp_debugf("Config ok");
return true;
}
hidp_debugf("Invalid config");
// if we get here then no usable setup was found
return false;
}

View File

@@ -1,14 +1,14 @@
#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
#define REPORT_TYPE_NONE 0
#define REPORT_TYPE_MOUSE 1
#define REPORT_TYPE_KEYBOARD 2
#define REPORT_TYPE_JOYSTICK 3
// currently only joysticks are supported
typedef struct {
uint8_t type: 2; // CONFIG_TYPE_...
uint8_t type: 2; // REPORT_TYPE_...
uint8_t report_id;
uint8_t report_size;
@@ -29,8 +29,8 @@ typedef struct {
} button[4]; // 4 buttons
} joystick_mouse;
};
} hid_config_t;
} hid_report_t;
bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_config_t *conf);
bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size, hid_report_t *conf);
#endif // HIDPARSER_H