| #include "usb.hpp" |
| |
| #include "multiplayer.hpp" |
| |
| #include "tusb.h" |
| |
| #include "config.h" |
| |
| // hid |
| #ifdef INPUT_USB_HID |
| |
| static int hid_report_id = -1; |
| static uint16_t buttons_offset = 0, num_buttons = 0; |
| static uint16_t hat_offset = 0, stick_offset = 0; |
| |
| uint32_t hid_gamepad_id = 0; |
| bool hid_keyboard_detected = false; |
| uint8_t hid_joystick[2]{0x80, 0x80}; |
| uint8_t hid_hat = 8; |
| uint32_t hid_buttons = 0; |
| uint8_t hid_keys[6]{}; |
| |
| void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) { |
| uint16_t vid = 0, pid = 0; |
| tuh_vid_pid_get(dev_addr, &vid, &pid); |
| |
| printf("Mount %i %i, %04x:%04x\n", dev_addr, instance, vid, pid); |
| |
| auto protocol = tuh_hid_interface_protocol(dev_addr, instance); |
| |
| hid_keyboard_detected = protocol == HID_ITF_PROTOCOL_KEYBOARD; |
| |
| // don't attempt to use a keyboard/mouse as a gamepad |
| if(protocol != HID_ITF_PROTOCOL_NONE) { |
| tuh_hid_receive_report(dev_addr, instance); |
| return; |
| } |
| |
| // basic and probably wrong report descriptor parsing |
| auto desc_end = desc_report + desc_len; |
| auto p = desc_report; |
| |
| int report_id = -1; |
| int usage_page = -1; |
| int usage = -1; |
| int report_count = 0, report_size = 0; |
| |
| bool found_any = false; |
| |
| int bit_offset = 0; |
| |
| while(p != desc_end) { |
| uint8_t b = *p++; |
| |
| int len = b & 0x3; |
| int type = (b >> 2) & 0x3; |
| int tag = b >> 4; |
| |
| if(type == RI_TYPE_MAIN) { |
| // ignore constants |
| if(tag == RI_MAIN_INPUT) { |
| if(usage_page == HID_USAGE_PAGE_DESKTOP && usage == HID_USAGE_DESKTOP_X) { |
| stick_offset = bit_offset; |
| hid_report_id = report_id; // assume everything is in the same report as the stick... and that the first x/y is the stick |
| found_any = true; |
| } else if(usage_page == HID_USAGE_PAGE_DESKTOP && usage == HID_USAGE_DESKTOP_HAT_SWITCH) { |
| hat_offset = bit_offset; |
| found_any = true; |
| } else if(usage_page == HID_USAGE_PAGE_BUTTON && !(*p & HID_CONSTANT)) { |
| // assume this is "the buttons" |
| buttons_offset = bit_offset; |
| num_buttons = report_count; |
| found_any = true; |
| } |
| |
| usage = -1; |
| bit_offset += report_size * report_count; |
| } else if(tag == RI_MAIN_COLLECTION) { |
| usage = -1; // check that this is gamepad? |
| } |
| } else if(type == RI_TYPE_GLOBAL) { |
| if(tag == RI_GLOBAL_USAGE_PAGE) |
| usage_page = *p; |
| else if(tag == RI_GLOBAL_REPORT_SIZE) |
| report_size = *p; |
| else if(tag == RI_GLOBAL_REPORT_ID) { |
| report_id = *p; |
| bit_offset = 0; |
| } else if(tag == RI_GLOBAL_REPORT_COUNT) |
| report_count = *p; |
| |
| |
| } else if(type == RI_TYPE_LOCAL) { |
| if(tag == RI_LOCAL_USAGE && usage == -1) |
| usage = *p; // FIXME: multiple usages are a thing |
| } |
| |
| p += len; |
| } |
| |
| // don't bother requesting reports we can't parse |
| if(!found_any) |
| return; |
| |
| hid_gamepad_id = (vid << 16) | pid; |
| |
| if(!tuh_hid_receive_report(dev_addr, instance)) { |
| printf("Cound not request report!\n"); |
| } |
| } |
| |
| void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) { |
| hid_keyboard_detected = false; |
| hid_gamepad_id = 0; |
| } |
| |
| // should this be here or in input.cpp? |
| void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) { |
| |
| auto report_data = hid_report_id == -1 ? report : report + 1; |
| |
| auto protocol = tuh_hid_interface_protocol(dev_addr, instance); |
| |
| if(protocol == HID_ITF_PROTOCOL_KEYBOARD) { |
| hid_keyboard_detected = true; |
| auto keyboard_report = (hid_keyboard_report_t const*) report; |
| memcpy(hid_keys, keyboard_report->keycode, 6); |
| |
| tuh_hid_receive_report(dev_addr, instance); |
| return; |
| } |
| |
| // check report id if we have one |
| if(hid_report_id == -1 || report[0] == hid_report_id) { |
| // I hope these are reasonably aligned |
| hid_hat = (report_data[hat_offset / 8] >> (hat_offset % 8)) & 0xF; |
| |
| hid_joystick[0] = report_data[stick_offset / 8]; |
| hid_joystick[1] = report_data[stick_offset / 8 + 1]; |
| |
| // get up to 32 buttons |
| hid_buttons = 0; |
| int bits = buttons_offset % 8; |
| int i = 0; |
| auto p = report_data + buttons_offset / 8; |
| |
| // partial byte |
| if(bits) { |
| hid_buttons |= (*p++) >> bits; |
| i += 8 - bits; |
| } |
| |
| for(; i < num_buttons; i+= 8) |
| hid_buttons |= (*p++) << i; |
| } |
| |
| // next report |
| tuh_hid_receive_report(dev_addr, instance); |
| } |
| #endif |
| |
| // cdc |
| static uint8_t cdc_index = 0; // TODO: multiple devices? |
| |
| void tuh_cdc_mount_cb(uint8_t idx) { |
| cdc_index = idx; |
| |
| send_multiplayer_handshake(); |
| } |
| |
| void init_usb() { |
| tusb_init(); |
| } |
| |
| void update_usb() { |
| tuh_task(); |
| |
| // TODO: resend multiplayer handshake |
| } |
| |
| void usb_debug(const char *message) { |
| |
| } |
| |
| bool usb_cdc_connected() { |
| return tuh_cdc_mounted(cdc_index); |
| } |
| |
| uint16_t usb_cdc_read(uint8_t *data, uint16_t len) { |
| return tuh_cdc_read(cdc_index, data, len); |
| } |
| |
| uint32_t usb_cdc_read_available() { |
| return tuh_cdc_read_available(cdc_index); |
| } |
| |
| void usb_cdc_write(const uint8_t *data, uint16_t len) { |
| uint32_t done = tuh_cdc_write(cdc_index,data, len); |
| |
| while(done < len) { |
| tuh_task(); |
| if(!tuh_cdc_mounted(cdc_index)) |
| break; |
| |
| done += tuh_cdc_write(cdc_index, data + done, len - done); |
| } |
| } |
| |
| void usb_cdc_flush_write() { |
| tuh_cdc_write_flush(cdc_index); |
| } |