blob: 1d78b62bd34b5849f26dbcf48c929b615753010f [file] [log] [blame] [edit]
#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);
}