#include #include "string.h" #include "usb.h" #include "max3421e.h" #include "timer.h" #include "hidparser.h" #include "debug.h" #include "../user_io.h" #include "../hardware.h" #include "../mist_cfg.h" static unsigned char kbd_led_state = 0; // default: all leds off static unsigned char joysticks = 0; // number of detected usb joysticks // up to 8 buttons can be remapped #define MAX_JOYSTICK_BUTTON_REMAP 8 #define MAX_VIRTUAL_JOYSTICK_REMAP 4 #define MAX_JOYSTICK_KEYBOARD_MAP 16 /*****************************************************************************/ static struct { uint16_t vid; // vendor id uint16_t pid; // product id uint8_t offset; // bit index within report uint8_t button; // joystick button to be reported } joystick_button_remap[MAX_JOYSTICK_BUTTON_REMAP]; void hid_joystick_button_remap_init(void) { memset(joystick_button_remap, 0, sizeof(joystick_button_remap)); } void hid_joystick_button_remap(char *s) { uint8_t i; hid_debugf("%s(%s)", __FUNCTION__, s); if(strlen(s) < 13) { hid_debugf("malformed entry"); return; } // parse remap request for(i=0;i %d", joystick_button_remap[i].vid, joystick_button_remap[i].pid, joystick_button_remap[i].offset, joystick_button_remap[i].button); return; } } } /*****************************************************************************/ /* Virtual joystick remap - custom parsing The mapping translates directions plus generic HID buttons (1-12) into a standard MiST "virtual joystick" */ static struct { uint16_t vid; uint16_t pid; uint16_t mapping[16]; } joystick_mappers[MAX_VIRTUAL_JOYSTICK_REMAP]; void virtual_joystick_remap_init(void) { memset(joystick_mappers, 0, sizeof(joystick_mappers)); } void virtual_joystick_remap(char *s) { uint8_t i; uint8_t count; uint8_t off = 0; //set to 3 to start mapping at buttons uint8_t len = strlen(s); char *token; hid_debugf("%s(%s)", __FUNCTION__, s); if(len < 13) { hid_debugf("malformed entry"); return; } // parse remap request for(i=0;i %d", joystick_mappers[i].vid, joystick_mappers[i].pid, count-1, joystick_mappers[i].mapping[off+count-2]); } s = strtok (NULL, ","); count+=1; } return; // finished processing input string so exit } } } /*****************************************************************************/ /* Custom parsing for joystick->keyboard map We bind a bitmask of the virtual joypad with a keyboard USB code */ static struct { uint16_t mask; uint8_t modifier; uint8_t keys[6]; // support up to 6 key codes } joy_key_map[MAX_JOYSTICK_KEYBOARD_MAP]; void joy_key_map_init(void) { memset(joy_key_map, 0, sizeof(joy_key_map)); } void joystick_key_map(char *s) { uint8_t i,j; uint8_t count; uint8_t assign=0; uint8_t len = strlen(s); uint8_t scancode=0; char *token; hid_debugf("%s(%s)", __FUNCTION__, s); if(len < 3) { hid_debugf("malformed entry"); return; } // parse remap request for(i=0;i223) { // bit 0 1 2 3 4 5 6 7 // key LCTRL LSHIFT LALT LGUI RCTRL RSHIFT RALT RGUI // scancode -= 223; joy_key_map[i].modifier |= (0x01 << (scancode-1)); } else { // max 6 keys if (assign < 7) joy_key_map[i].keys[assign++] = scancode; } } s = strtok (NULL, ","); count+=1; } return; // finished processing input string so exit } } } /*****************************************************************************/ 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:"); 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 == REPORT_TYPE_JOYSTICK) { hid_debugf("Detected USB joystick #%d", joysticks); info->iface[i].device_type = HID_DEVICE_JOYSTICK; info->iface[i].jindex = joysticks++; } } else { // parsing failed. Fall back to boot mode for mice 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; } } } 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].ignore_boot_mode = false; 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 = REPORT_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"); // don't use boot mode for mice unless it's explicitey requested in mist.ini if(!mist_cfg.mouse_boot_mode) info->iface[info->bNumIfaces].ignore_boot_mode = true; 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;iiface[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; ibNumIfaces) { 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; ibNumIfaces; 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 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 == 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 == REPORT_TYPE_JOYSTICK) { char k; iprintf("JOYSTICK: 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); info->iface[i].conf.joystick_mouse.pid = pid; info->iface[i].conf.joystick_mouse.vid = vid; for(k=0;k<2;k++) iprintf("Axis%d: %d@%d %d->%d\n", k, info->iface[i].conf.joystick_mouse.axis[k].size, info->iface[i].conf.joystick_mouse.axis[k].offset/8, info->iface[i].conf.joystick_mouse.axis[k].logical.min, info->iface[i].conf.joystick_mouse.axis[k].logical.max); for(k=0;k<4;k++) iprintf("Button%d: @%d/%d\n", k, info->iface[i].conf.joystick_mouse.button[k].byte_offset, info->iface[i].conf.joystick_mouse.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_mouse.button[0].byte_offset = 5; info->iface[0].conf.joystick_mouse.button[0].bitmask = 32; info->iface[0].conf.joystick_mouse.button[1].byte_offset = 5; info->iface[0].conf.joystick_mouse.button[1].bitmask = 64 | 16; info->iface[0].conf.joystick_mouse.button[2].byte_offset = 6; info->iface[0].conf.joystick_mouse.button[2].bitmask = 16; info->iface[0].conf.joystick_mouse.button[3].byte_offset = 6; info->iface[0].conf.joystick_mouse.button[3].bitmask = 32; } if((vid == 0x04d8) && (pid == 0xf6ec) && (i==0)) { iprintf("hacking 5200daptor\n"); info->iface[0].conf.joystick_mouse.button[2].byte_offset = 4; info->iface[0].conf.joystick_mouse.button[2].bitmask = 0x40; // "Reset" info->iface[0].conf.joystick_mouse.button[3].byte_offset = 4; info->iface[0].conf.joystick_mouse.button[3].bitmask = 0x10; // "Start" info->iface[0].is_5200daptor = true; } // apply remap information from mist.ini if present uint8_t j; for(j=0;jiface[0].conf.joystick_mouse.button[but].byte_offset = joystick_button_remap[j].offset >> 3; info->iface[0].conf.joystick_mouse.button[but].bitmask = 0x80 >> (joystick_button_remap[j].offset & 7); iprintf("hacking from ini file %d %d -> %d\n", info->iface[0].conf.joystick_mouse.button[but].byte_offset, info->iface[0].conf.joystick_mouse.button[but].bitmask, but); } } } rcode = hid_set_idle(dev, info->iface[i].iface_idx, 0, 0); if (rcode && rcode != hrSTALL) return rcode; // enable boot mode if its not diabled 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"); // update leds for(i=0;ihid_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;ibNumIfaces;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 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<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<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 user_io_kbd(0x00, buf); // save current state of keys iface->key_state = keys; } } // 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("0 m:%x by:%d bi=%d sh=%d ->", mask, byte, bits, shift); uint16_t rval = (p[byte++] & mask) >> shift; // iprintf("%d\n", (int16_t)rval); mask = 0xff; shift = 8-shift; bits -= shift; // first byte already contained more bits than we need if(shift > size) { // iprintf(" too many bits, masked %x ->", (1<>(8-bits)):0xff; // iprintf("+ m:%x by:%d bi=%d sh=%d ->", mask, byte, bits, shift); rval += (p[byte++] & mask) << shift; // iprintf("%d\n", (int16_t)rval); 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(" is negative -> sign expand to %d\n", (int16_t)rval); } } return rval; } 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;ibNumIfaces;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]; // 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 { uint8_t keyb_hit = 0; // 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) // 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) { user_io_kbd(buf[0], buf+2); if (buf[0]||buf[1]) keyb_hit=1; //declare keyboard as pressed for later overrides } } } // 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_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; uint8_t btn_extra = 0; 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++) { // 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 first buttons for(i=0;i<4;i++) if(p[conf->joystick_mouse.button[i].byte_offset] & conf->joystick_mouse.button[i].bitmask) btn |= (1<joystick_mouse.button[i].byte_offset] & conf->joystick_mouse.button[i].bitmask) btn_extra |= (1<<(i-4)); //if (btn_extra != 0) // iprintf("EXTRA BTNS:%d\n", btn_extra); // ---------- process mouse ------------- if(iface->device_type == HID_DEVICE_MOUSE) { // iprintf("mouse %d %d %x\n", (int16_t)a[0], (int16_t)a[1], btn); // limit mouse movement to +/- 128 for(i=0;i<2;i++) { if((int16_t)a[i] > 127) a[i] = 127; if((int16_t)a[i] < -128) a[i] = -128; } user_io_mouse(btn, a[0], a[1]); } // ---------- process joystick ------------- if(iface->device_type == HID_DEVICE_JOYSTICK) { for(i=0;i<2;i++) { // scale to 0 -> 255 range. 99% of the joysticks already deliver that if((conf->joystick_mouse.axis[i].logical.min != 0) || (conf->joystick_mouse.axis[i].logical.max != 255)) { a[i] = ((a[i] - conf->joystick_mouse.axis[i].logical.min) * 255)/ (conf->joystick_mouse.axis[i].logical.max - conf->joystick_mouse.axis[i].logical.min); } } // handle hat if present and overwrite any axis value if(conf->joystick_mouse.hat.size && !mist_cfg.joystick_ignore_hat) { uint8_t hat = collect_bits(p, conf->joystick_mouse.hat.offset, conf->joystick_mouse.hat.size, 0); // we don't want more than 4 bits uint8_t size = conf->joystick_mouse.hat.size; while(size-- > 4) hat >>= 1; // iprintf("HAT = %d\n", hat); // TODO: Deal with 3 bit (4 direction/no diagonal) hats static const uint8_t hat2x[] = { 127,255,255,255,127, 0, 0, 0 }; static const uint8_t hat2y[] = { 0, 0,127,255,255,255,127, 0 }; if(hat&8) { // hat is idle - don't override analog /* if (a[0] > JOYSTICK_AXIS_TRIGGER_MIN) || a[0] < JOYSTICK_AXIS_TRIGGER_MAX) a[0] = JOYSTICK_AXIS_MID; if (a[1] > JOYSTICK_AXIS_TRIGGER_MIN) || a[1] < JOYSTICK_AXIS_TRIGGER_MAX) a[1] = JOYSTICK_AXIS_MID; */ } else { uint8_t x_val = hat2x[hat]; uint8_t y_val = hat2y[hat]; // cancel out with X analog axis if it pushes on the opposite direction if(x_val < JOYSTICK_AXIS_TRIGGER_MIN) { // hat pointing left, compensate if analog is pointing right if (a[0] > JOYSTICK_AXIS_TRIGGER_MAX) { a[0] = JOYSTICK_AXIS_MID; } else a[0] = x_val; } else { if(x_val > JOYSTICK_AXIS_TRIGGER_MAX) { // hat pointing right, compensate if analog pointing left if (a[0] < JOYSTICK_AXIS_TRIGGER_MIN) { a[0] = JOYSTICK_AXIS_MID; } else a[0] = x_val; } } // same logic for Y axis if(y_val < JOYSTICK_AXIS_TRIGGER_MIN) { // hat pointing down if (a[1] > JOYSTICK_AXIS_TRIGGER_MAX) { a[1] = JOYSTICK_AXIS_MID; } else a[1] = y_val; } else { if(y_val > JOYSTICK_AXIS_TRIGGER_MAX) { // hat pointing up if (a[1] < JOYSTICK_AXIS_TRIGGER_MIN) { a[1] = JOYSTICK_AXIS_MID; } else a[1] = y_val; //otherwise override } } } } // iprintf("JOY X:%d Y:%d\n", a[0], a[1]); if(a[0] < JOYSTICK_AXIS_TRIGGER_MIN) jmap |= JOY_LEFT; if(a[0] > JOYSTICK_AXIS_TRIGGER_MAX) jmap |= JOY_RIGHT; if(a[1] < JOYSTICK_AXIS_TRIGGER_MIN) jmap |= JOY_UP; if(a[1] > JOYSTICK_AXIS_TRIGGER_MAX) jmap |= JOY_DOWN; jmap |= btn << JOY_BTN_SHIFT; // add buttons // map virtual joypad uint16_t vjoy_input = jmap; vjoy_input |= btn_extra << 8; //if (vjoy_input != 0) // iprintf("VJOY bit:%d\n", vjoy_input); uint16_t default_mapping [16] = { JOY_RIGHT, JOY_LEFT, JOY_DOWN, JOY_UP, JOY_A, JOY_B, JOY_SELECT, JOY_START, JOY_X, JOY_Y, JOY_L, JOY_R, JOY_L2, JOY_R2, JOY_L3, JOY_R3 }; uint16_t vid = conf->joystick_mouse.vid; uint16_t pid = conf->joystick_mouse.pid; // defines translations between physical buttons and virtual joysticks uint16_t mapping[16]; // keep directions by default for(i=0; i<4; i++) mapping[i]=default_mapping[i]; // blank the rest for(i=4; i<16; i++) mapping[i]=0; uint8_t use_default=1; uint8_t btn_off = 3; // start at three since array is 0 based, so 4 = button 1 // mapping for Qanba Q4RAF if( vid==0x0F30 && pid==0x1012) { mapping[btn_off+1] = JOY_A; mapping[btn_off+2] = JOY_B; mapping[btn_off+4] = JOY_A; mapping[btn_off+3] = JOY_B; mapping[btn_off+5] = JOY_X; //for jump mapping[btn_off+6] = JOY_SELECT; mapping[btn_off+8] = JOY_SELECT; mapping[btn_off+10] = JOY_START; use_default=0; } // mapping for no-brand cheap snes clone pad if(vid==0x081F && pid==0xE401) { mapping[btn_off+2] = JOY_A; mapping[btn_off+3] = JOY_B; mapping[btn_off+1] = JOY_B; // allow two ways to hold the controller mapping[btn_off+4] = JOY_UP; mapping[btn_off+5] = JOY_L | JOY_L2; // also bind to buttons for flippers mapping[btn_off+6] = JOY_R | JOY_R2; mapping[btn_off+9] = JOY_SELECT; mapping[btn_off+10] = JOY_START; use_default=0; } // mapping for iBuffalo SNES pad - BSGP801 if(vid==0x0583 && pid==0x2060) { mapping[btn_off+1] = JOY_A; mapping[btn_off+2] = JOY_B; mapping[btn_off+3] = JOY_B; // allow two ways to hold the controller mapping[btn_off+4] = JOY_UP; mapping[btn_off+5] = JOY_L | JOY_L2; // also bind to buttons for flippers mapping[btn_off+6] = JOY_R | JOY_R2; mapping[btn_off+7] = JOY_SELECT; mapping[btn_off+8] = JOY_START; use_default=0; } //mapping for Buffalo NES pad - BGCFC801 if(vid==0x0411 && pid==0x00C6) { mapping[btn_off+1] = JOY_A; mapping[btn_off+2] = JOY_B; mapping[btn_off+3] = JOY_B; // allow two ways to hold the controller mapping[btn_off+4] = JOY_UP; mapping[btn_off+5] = JOY_L | JOY_L2; // also bind to buttons for flippers mapping[btn_off+6] = JOY_R | JOY_R2; mapping[btn_off+7] = JOY_SELECT; mapping[btn_off+8] = JOY_START; use_default=0; } //mapping for RetroLink N64 and Gamecube pad (same vid/pid) if(vid==0x0079 && pid==0x0006) { mapping[btn_off+7] = JOY_A; // A on N64 pad mapping[btn_off+9] = JOY_B; // B on N64 pad mapping[btn_off+3] = JOY_A; // A on GC pad mapping[btn_off+4] = JOY_B; // B on GC pad mapping[btn_off+5] = JOY_L | JOY_SELECT; mapping[btn_off+8] = JOY_L | JOY_SELECT; // Z button on N64 pad mapping[btn_off+6] = JOY_R | JOY_SELECT; mapping[btn_off+10] = JOY_START; use_default=0; } //mapping for ROYDS Stick.EX if(vid==0x1F4F && pid==0x0003) { mapping[btn_off+3] = JOY_A; // Circle (usually select in PSx) mapping[btn_off+1] = JOY_B; // Cross (usually cancel in PSx) mapping[btn_off+2] = JOY_X; // Triangle mapping[btn_off+4] = JOY_Y; // Square mapping[btn_off+5] = JOY_L; mapping[btn_off+6] = JOY_R; mapping[btn_off+7] = JOY_L2; mapping[btn_off+8] = JOY_R2; mapping[btn_off+9] = JOY_SELECT; mapping[btn_off+10] = JOY_START; use_default=0; } // apply remap information from mist.ini if present uint8_t j; for(j=0;j> 8); jmap = (vjoy & 0x00FF); //if (jmap != 0) iprintf("JMAP post map:%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) { // and feed into joystick input system user_io_digital_joystick(idx, jmap); iface->jmap = jmap; } // also send analog values user_io_analog_joystick(idx, a[0]-128, a[1]-128); // do special 5200daptor treatment if(iface->is_5200daptor) handle_5200daptor(iface, buf); // use button combinations as shortcut for certain keys if(!mist_cfg.joystick_disable_shortcuts) { uint8_t buf[6] = { 0,0,0,0,0,0 }; uint8_t key_hit = 0; // if OSD is open control it via USB joystick if(user_io_osd_is_visible() && !mist_cfg.joystick_ignore_osd) { if(vjoy & JOY_A) buf[0] = 0x28; // ENTER if(vjoy & JOY_B) buf[0] = 0x29; // ESC if(vjoy & JOY_START) buf[0] = 0x45; // F12 if(vjoy & JOY_LEFT) buf[0] = 0x50; // left arrow if(vjoy & JOY_RIGHT) buf[0] = 0x4F; // right arrow // up and down uses SELECT or L for faster scrolling if(vjoy & JOY_UP) { if (vjoy & JOY_SELECT || vjoy & JOY_L) buf[1] = 0x4B; // page up else buf[1] = 0x52; // up arrow } if(vjoy & JOY_DOWN) { if (vjoy & JOY_SELECT || vjoy & JOY_L) buf[1] = 0x4E; // page down else buf[1] = 0x51; // down arrow } user_io_kbd(0x00, buf); // generate key events key_hit=1; } else { // shortcuts mapped if start is pressed (take priority) if (vjoy & JOY_START) { if(vjoy & JOY_A) buf[0] = 0x28; // ENTER if(vjoy & JOY_B) buf[1] = 0x2C; // SPACE if(vjoy & JOY_L) buf[1] = 0x29; // ESC if(vjoy & JOY_R) buf[1] = 0x3A; // F1 if(vjoy & JOY_SELECT) buf[2] = 0x45; //F12 // i.e. open OSD in most cores user_io_kbd(0x00, buf); // generate key events key_hit=1; } else { // shortcuts with SELECT - mouse emulation if (vjoy & JOY_SELECT) { unsigned char but = 0; char a0 = 0; char a1 = 0; if (vjoy & JOY_L) but |= 1; if (vjoy & JOY_R) but |= 2; if (vjoy & JOY_LEFT) a0 = -4; if (vjoy & JOY_RIGHT) a0 = 4; if (vjoy & JOY_UP) a1 = -2; if (vjoy & JOY_DOWN) a1 = 2; user_io_mouse(but, a0, a1); } } // process mapped keyboard commands from mist.ini uint8_t i, j, count=0; uint8_t mapped_hit = 0; uint8_t modifier = 0; uint8_t has_mapping = 0; uint8_t joy_buf[6] = { 0,0,0,0,0,0 }; for(i=0;ikeyboard shortcuts } // end joystick handling } // end hid custom report parsing } // end of HID complex parsing } // end if else rcode iface->qNextPollTime += iface->interval; // poll at requested rate } } // end if known device } // end for loop (bNumIfaces) 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