1
0
mirror of https://github.com/mist-devel/mist-firmware.git synced 2026-02-26 08:14:13 +00:00

Started USB joystick support

This commit is contained in:
harbaum
2014-03-14 20:28:55 +00:00
parent f08016b8f6
commit 2476b9d6b2
7 changed files with 532 additions and 13 deletions

View File

@@ -9,8 +9,9 @@ TODAY = `date +"%m/%d/%y"`
PRJ = firmware
SRC = Cstartup_SAM7.c fat.c fdd.c firmware.c fpga.c hardware.c hdd.c main.c menu.c mmc.c osd.c syscalls.c user_io.c boot_print.c boot_logo.c rafile.c config.c tos.c ikbd.c
SRC += usb/max3421e.c usb/usb.c usb/hub.c usb/hid.c usb/timer.c
SRC += usb/max3421e.c usb/usb.c usb/hub.c usb/hid.c usb/hidparser.c usb/timer.c
SRC += cdc_enumerate.c cdc_control.c
OBJ = $(SRC:.c=.o)
DEP = $(SRC:.c=.d)
@@ -19,7 +20,7 @@ LIBDIR =
# Commandline options for each tool.
DFLAGS = -I. -Iusb -DMIST
CFLAGS = -I. -Iusb -c -fno-common -O3 -DMIST -fsigned-char -DVDATE=\"`date +"%y%m%d"`\"
CFLAGS = $(DFLAGS) -c -fno-common -O3 -fsigned-char -DVDATE=\"`date +"%y%m%d"`\"
AFLAGS = -ahls -mapcs-32
LFLAGS = -nostartfiles -Wl,-Map,$(PRJ).map -T$(LINKMAP) $(LIBDIR)
CPFLAGS = --output-target=ihex
@@ -36,10 +37,8 @@ clean:
rm -f *.d *.o *.hex *.elf *.map *.lst core *~ */*.d */*.o $(MKUPG) *.bin *.upg
INTERFACE=olimex-arm-usb-tiny-h
ADAPTER_KHZ=10000
#INTERFACE=busblaster
#ADAPTER_KHZ=100
ADAPTER_KHZ=10000
reset:
openocd -f interface/$(INTERFACE).cfg -f target/at91sam7sx.cfg --command "adapter_khz $(ADAPTER_KHZ); init; reset init; resume; shutdown"

View File

@@ -2,6 +2,14 @@
#ifndef DEBUG_H
#define DEBUG_H
// ------------ usb debugging -----------
#if 1
#define hidp_debugf(...) iprintf(__VA_ARGS__)
#else
#define hidp_debugf(...)
#endif
// ------------ generic debugging -----------
#if 0

View File

@@ -3,9 +3,17 @@
#include "usb.h"
#include "max3421e.h"
#include "timer.h"
#include "hidparser.h"
#include "../user_io.h"
// joystick todo:
// - renumber on unplug
// - shift legacy joysticks up
// - emulate extra joysticks (at printerport, ...)
// - second fire button (no known system uses it, but OSD may have a use ...)
static unsigned char kbd_led_state = 0; // default: all leds off
static unsigned char joysticks = 0; // number of detected usb joysticks
static void hexdump(void *data, int size) {
int i,n = 0, b2c;
@@ -42,12 +50,26 @@ static uint8_t hid_get_report_descr(usb_device_t *dev, uint8_t iface, uint16_t s
iprintf("%s(%x, if=%d, size=%d)\n", __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, iface, size, size, buf, NULL);
if(!rcode)
if(!rcode) {
iprintf("HID report descriptor:\n");
hexdump(buf, size);
// we got a report descriptor. Try to parse it
if(parse_report_descriptor(buf, size)) {
if(hid_conf[0].type == CONFIG_TYPE_JOYSTICK) {
iprintf("Detected USB joystick %d\n", joysticks);
info->iface_info[iface].device_type = HID_DEVICE_JOYSTICK;
info->iface_info[iface].conf = hid_conf[0];
info->iface_info[iface].jindex = joysticks++;
}
}
}
return rcode;
}
@@ -119,6 +141,7 @@ static uint8_t usb_hid_parse_conf(usb_device_t *dev, uint8_t conf, uint16_t len)
info->iface_info[info->bNumIfaces].iface_idx = p->iface_desc.bInterfaceNumber;
info->iface_info[info->bNumIfaces].has_boot_mode = false;
info->iface_info[info->bNumIfaces].device_type = HID_DEVICE_UNKNOWN;
info->iface_info[info->bNumIfaces].conf.type = CONFIG_TYPE_NONE;
if(p->iface_desc.bInterfaceSubClass == HID_BOOT_INTF_SUBCLASS) {
iprintf("Iface %d is Boot sub class\n", info->bNumIfaces);
@@ -265,10 +288,13 @@ static uint8_t usb_hid_init(usb_device_t *dev) {
// process all supported interfaces
for(i=0; i<info->bNumIfaces; i++) {
rcode = hid_get_report_descr(dev,
info->iface_info[i].iface_idx, info->iface_info[i].report_size);
if (rcode)
return rcode;
// no boot mode, try to parse HID report descriptor
if(!info->iface_info[info->bNumIfaces].has_boot_mode) {
rcode = hid_get_report_descr(dev,
info->iface_info[i].iface_idx, info->iface_info[i].report_size);
if (rcode)
return rcode;
}
rcode = hid_set_idle(dev, info->iface_info[i].iface_idx, 0, 0);
if (rcode && rcode != hrSTALL)
@@ -291,7 +317,19 @@ static uint8_t usb_hid_init(usb_device_t *dev) {
}
static uint8_t usb_hid_release(usb_device_t *dev) {
usb_hid_info_t *info = &(dev->hid_info);
puts(__FUNCTION__);
int8_t i;
// check if a joystick is released
for(i=0;i<info->bNumIfaces;i++) {
if(info->iface_info[i].device_type == HID_DEVICE_JOYSTICK) {
iprintf("releasing joystick #%d, renumbering\n", info->iface_info[i].jindex);
}
}
return 0;
}
@@ -337,6 +375,39 @@ static uint8_t usb_hid_poll(usb_device_t *dev) {
}
}
}
if(info->iface_info[i].device_type == HID_DEVICE_JOYSTICK) {
hid_config_t *conf = &info->iface_info[i].conf;
uint8_t jmap = 0;
uint8_t ax;
iprintf("Joystick data:\n");
hexdump(buf, read);
// currently only byte sized axes are allowed
ax = buf[conf->joystick.axis_byte_offset[0]];
if(ax < 64) jmap |= JOY_LEFT;
if(ax > 192) jmap |= JOY_RIGHT;
ax = buf[conf->joystick.axis_byte_offset[1]];
if(ax < 64) jmap |= JOY_UP;
if(ax > 192) jmap |= JOY_DOWN;
// ... and one button
if(buf[conf->joystick.button_byte_offset] & conf->joystick.button0_bitmask)
jmap |= JOY_BTN1;
// swap joystick 0 and 1 since 1 is the one used primarily on most systems
ax = info->iface_info[i].jindex;
if(ax == 0) ax = 1;
else if(ax == 1) ax = 0;
// check if joystick state has changed
if(jmap != info->iface_info[i].jmap) {
// and feed into joystick input system
user_io_joystick(ax, jmap);
info->iface_info[i].jmap = jmap;
}
}
}
}

View File

@@ -3,6 +3,7 @@
#include <stdbool.h>
#include <inttypes.h>
#include "hidparser.h"
#define HID_LED_NUM_LOCK 0x01
#define HID_LED_CAPS_LOCK 0x02
@@ -40,6 +41,7 @@
#define HID_DEVICE_UNKNOWN 0
#define HID_DEVICE_MOUSE 1
#define HID_DEVICE_KEYBOARD 2
#define HID_DEVICE_JOYSTICK 3
typedef struct {
uint8_t iface_idx;
@@ -48,6 +50,12 @@ typedef struct {
uint8_t device_type;
bool has_boot_mode; // device supports boot mode
// 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;
} usb_hid_iface_info_t;
typedef struct {

403
usb/hidparser.c Normal file
View File

@@ -0,0 +1,403 @@
// 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 0
#define hidp_extreme_debugf(...) iprintf(__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)
hid_config_t hid_conf[MAX_CONF];
#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) {
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, config_idx = 0;
uint16_t bit_count = 0, usage_count = 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;
for(i=0;i<MAX_CONF;i++)
hid_conf[i].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) {
hidp_debugf("BUTTON0 @ %d (byte %d, mask %d)\n",
bit_count, bit_count/8, 1 << (bit_count%8));
if(hid_conf[config_idx].type == CONFIG_TYPE_JOYSTICK) {
hid_conf[config_idx].joystick.button_byte_offset = bit_count/8;
hid_conf[config_idx].joystick.button0_bitmask = 1 << (bit_count%8);
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 bit axes at byte boundaries are supported for
// joysticks
if((hid_conf[config_idx].type == CONFIG_TYPE_JOYSTICK) &&
(report_size == 8) && ((cnt&7) == 0)) {
// save in joystick config
hid_conf[config_idx].joystick.axis_byte_offset[c] = cnt/8;
if(c==0) setup_complete |= JOYSTICK_REQ_AXIS_X;
if(c==1) setup_complete |= JOYSTICK_REQ_AXIS_Y;
}
if(report_size != 8)
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++;
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);
break;
case 2:
hidp_extreme_debugf("LOGICAL_MAXIMUM(%d)\n", 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);
hid_conf[config_idx].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");
hid_conf[config_idx].type = CONFIG_TYPE_KEYBOARD;
} else if(!collection_depth && (value == USAGE_MOUSE)) {
// usage(mouse) is always allowed
hidp_debugf(" -> Mouse\n");
hid_conf[config_idx].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");
hid_conf[config_idx].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 ||
value == USAGE_Z || value == USAGE_WHEEL) &&
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(hid_conf[config_idx].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;
}
}
usage_count++;
} else {
hidp_extreme_debugf(" -> UNSUPPORTED USAGE\n");
// return false;
}
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);
// check if something useful was detected
if(hid_conf[config_idx].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 %d\n", config_idx);
return false;
}

28
usb/hidparser.h Normal file
View File

@@ -0,0 +1,28 @@
#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;
union {
struct {
uint8_t axis_byte_offset[2]; // x and y axis
uint8_t button_byte_offset;
uint8_t button0_bitmask;
} joystick;
};
} hid_config_t;
#define MAX_CONF 2
extern hid_config_t hid_conf[MAX_CONF];
bool parse_report_descriptor(uint8_t *rep, uint16_t rep_size);
#endif // HIDPARSER_H

View File

@@ -137,6 +137,8 @@ void user_io_detect_core_type() {
}
void user_io_joystick(unsigned char joystick, unsigned char map) {
iprintf("j%d: %x\n", joystick, map);
// most cores process joystick events themselves
if((core_type == CORE_TYPE_MINIMIG) ||
(core_type == CORE_TYPE_PACE) ||
@@ -314,7 +316,7 @@ void user_io_poll() {
if(status != bit8_status) {
char buffer[512];
bit8_debugf("st %08x", status);
// bit8_debugf("st %08x", status);
bit8_status = status;
// sector read testing
@@ -322,7 +324,7 @@ void user_io_poll() {
if((status & 0xff) == 0xa5) {
unsigned long sector = (status>>8)&0xffffff;
bit8_debugf("sec rd %u", sector);
// bit8_debugf("sec rd %u", sector);
if(MMC_Read(sector, buffer)) {
short i;