From 620a9dab14ba57279651e459d93027893c7dd0fb Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sun, 3 Oct 2021 14:51:06 +0100 Subject: [PATCH 1/5] pico: Minimal USB host config --- 32blit-pico/tusb_config.h | 32 ++++++++++++++++++++++++++++++++ 32blit-pico/usb.cpp | 20 +++++++++++++++++++- 32blit-pico/usb_descriptors.c | 4 ++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/32blit-pico/tusb_config.h b/32blit-pico/tusb_config.h index 9a0262d30..05e3d7cfa 100644 --- a/32blit-pico/tusb_config.h +++ b/32blit-pico/tusb_config.h @@ -56,6 +56,15 @@ #endif // Device mode with rhport and speed defined by board.mk +#ifdef USB_HOST +#if BOARD_DEVICE_RHPORT_NUM == 0 + #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST | BOARD_DEVICE_RHPORT_SPEED) +#elif BOARD_DEVICE_RHPORT_NUM == 1 + #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_HOST | BOARD_DEVICE_RHPORT_SPEED) +#else + #error "Incorrect RHPort configuration" +#endif +#else #if BOARD_DEVICE_RHPORT_NUM == 0 #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) #elif BOARD_DEVICE_RHPORT_NUM == 1 @@ -63,6 +72,7 @@ #else #error "Incorrect RHPort configuration" #endif +#endif // This example doesn't use an RTOS #ifndef CFG_TUSB_OS @@ -91,6 +101,27 @@ // DEVICE CONFIGURATION //-------------------------------------------------------------------- +#ifdef USB_HOST + +// Size of buffer to hold descriptors and other data used for enumeration +#define CFG_TUH_ENUMERATION_BUFSIZE 256 + +#define CFG_TUH_HUB 0 +#define CFG_TUH_CDC 0 +#define CFG_TUH_HID 0 +#define CFG_TUH_MSC 0 +#define CFG_TUH_VENDOR 0 + +// max device support (excluding hub device) +// 1 hub typically has 4 ports +#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) + +//------------- HID -------------// + +#define CFG_TUH_HID_EP_BUFSIZE 64 + +#else + #ifndef CFG_TUD_ENDPOINT0_SIZE #define CFG_TUD_ENDPOINT0_SIZE 64 #endif @@ -111,6 +142,7 @@ // MSC Buffer size of Device Mass storage #define CFG_TUD_MSC_EP_BUFSIZE 512 +#endif #ifdef __cplusplus } diff --git a/32blit-pico/usb.cpp b/32blit-pico/usb.cpp index a58e54d6e..8ad83405f 100644 --- a/32blit-pico/usb.cpp +++ b/32blit-pico/usb.cpp @@ -11,6 +11,7 @@ #include "engine/api_private.hpp" // msc +#ifndef USB_HOST static bool storage_ejected = false; void tud_mount_cb() { @@ -93,8 +94,11 @@ int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, u bool tud_msc_is_writable_cb(uint8_t lun) { return !get_files_open(); } +#endif // cdc +#ifndef USB_HOST + static bool multiplayer_enabled = false; static bool peer_connected = false; @@ -125,13 +129,16 @@ static void send_handshake(bool is_reply = false) { send_all(buf, 9); tud_cdc_write_flush(); } - +#endif void init_usb() { tusb_init(); } void update_usb() { +#ifdef USB_HOST + tuh_task(); +#else // device tud_task(); if(!tud_ready()) { // tud_cdc_connected returns false with STM USB host @@ -189,28 +196,38 @@ void update_usb() { } } } +#endif } void usb_debug(const char *message) { +#ifndef USB_HOST if(!tud_cdc_connected()) return; auto len = strlen(message); send_all(message, len); +#endif } bool is_multiplayer_connected() { +#ifdef USB_HOST + return false; +#else return multiplayer_enabled && peer_connected; +#endif } void set_multiplayer_enabled(bool enabled) { +#ifndef USB_HOST // could be supported with USB host, but we'd need a hub multiplayer_enabled = enabled; if(!enabled) send_handshake(); +#endif } void send_multiplayer_message(const uint8_t *data, uint16_t len) { +#ifndef USB_HOST if(!peer_connected) return; @@ -222,4 +239,5 @@ void send_multiplayer_message(const uint8_t *data, uint16_t len) { send_all((uint8_t *)data, len); tud_cdc_write_flush(); +#endif } diff --git a/32blit-pico/usb_descriptors.c b/32blit-pico/usb_descriptors.c index dd4146099..c4e6ab562 100644 --- a/32blit-pico/usb_descriptors.c +++ b/32blit-pico/usb_descriptors.c @@ -29,6 +29,8 @@ #include "config.h" +#ifndef USB_HOST + /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. * @@ -182,3 +184,5 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) return _desc_str; } + +#endif From db3f6af5d4681d74596de20ca1f10cc72289f227 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sun, 29 Jan 2023 23:45:00 +0000 Subject: [PATCH 2/5] pico: split usb device/host driver --- 32blit-pico/CMakeLists.txt | 10 +++++++- 32blit-pico/usb.hpp | 1 + 32blit-pico/{usb.cpp => usb_device.cpp} | 19 --------------- 32blit-pico/usb_host.cpp | 31 +++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 20 deletions(-) rename 32blit-pico/{usb.cpp => usb_device.cpp} (94%) create mode 100644 32blit-pico/usb_host.cpp diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index c52ce785d..9e91bf1e3 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -32,7 +32,6 @@ target_sources(BlitHalPico INTERFACE ${CMAKE_CURRENT_LIST_DIR}/main.cpp ${CMAKE_CURRENT_LIST_DIR}/storage.cpp ${CMAKE_CURRENT_LIST_DIR}/st7789.cpp - ${CMAKE_CURRENT_LIST_DIR}/usb.cpp ${CMAKE_CURRENT_LIST_DIR}/usb_descriptors.c ) @@ -81,6 +80,9 @@ endif() if(NOT BLIT_INPUT_DRIVER) set(BLIT_INPUT_DRIVER "none") endif() +if(NOT BLIT_USB_DRIVER) + set(BLIT_USB_DRIVER "device") +endif() # driver dependencies if(BLIT_AUDIO_DRIVER STREQUAL "i2s") @@ -99,6 +101,11 @@ elseif(BLIT_DISPLAY_DRIVER STREQUAL "st7789") list(APPEND BLIT_BOARD_DEFINITIONS DISPLAY_ST7789) # config defaults use this, also some games are using it for picosystem detection endif() +if(BLIT_USB_DRIVER STREQUAL "host") + list(APPEND BLIT_BOARD_DEFINITIONS USB_HOST) + list(APPEND BLIT_BOARD_LIBRARIES tinyusb_host) +endif() + # late SDK init # (pico_sdk_init needs to be after importing extras, which we don't know if we'll need until now) if(BLIT_REQUIRE_PICO_EXTRAS) @@ -115,6 +122,7 @@ target_sources(BlitHalPico INTERFACE ${CMAKE_CURRENT_LIST_DIR}/audio_${BLIT_AUDIO_DRIVER}.cpp ${CMAKE_CURRENT_LIST_DIR}/display_${BLIT_DISPLAY_DRIVER}.cpp ${CMAKE_CURRENT_LIST_DIR}/input_${BLIT_INPUT_DRIVER}.cpp + ${CMAKE_CURRENT_LIST_DIR}/usb_${BLIT_USB_DRIVER}.cpp ) if(BLIT_ENABLE_CORE1) diff --git a/32blit-pico/usb.hpp b/32blit-pico/usb.hpp index 6c5132f27..477897204 100644 --- a/32blit-pico/usb.hpp +++ b/32blit-pico/usb.hpp @@ -7,6 +7,7 @@ void update_usb(); void usb_debug(const char *message); +// TODO: separate multiplayer from usb bool is_multiplayer_connected(); void set_multiplayer_enabled(bool enabled); void send_multiplayer_message(const uint8_t *data, uint16_t len); diff --git a/32blit-pico/usb.cpp b/32blit-pico/usb_device.cpp similarity index 94% rename from 32blit-pico/usb.cpp rename to 32blit-pico/usb_device.cpp index 8ad83405f..cc96ebbec 100644 --- a/32blit-pico/usb.cpp +++ b/32blit-pico/usb_device.cpp @@ -11,7 +11,6 @@ #include "engine/api_private.hpp" // msc -#ifndef USB_HOST static bool storage_ejected = false; void tud_mount_cb() { @@ -94,11 +93,8 @@ int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, u bool tud_msc_is_writable_cb(uint8_t lun) { return !get_files_open(); } -#endif // cdc -#ifndef USB_HOST - static bool multiplayer_enabled = false; static bool peer_connected = false; @@ -129,16 +125,12 @@ static void send_handshake(bool is_reply = false) { send_all(buf, 9); tud_cdc_write_flush(); } -#endif void init_usb() { tusb_init(); } void update_usb() { -#ifdef USB_HOST - tuh_task(); -#else // device tud_task(); if(!tud_ready()) { // tud_cdc_connected returns false with STM USB host @@ -196,38 +188,28 @@ void update_usb() { } } } -#endif } void usb_debug(const char *message) { -#ifndef USB_HOST if(!tud_cdc_connected()) return; auto len = strlen(message); send_all(message, len); -#endif } bool is_multiplayer_connected() { -#ifdef USB_HOST - return false; -#else return multiplayer_enabled && peer_connected; -#endif } void set_multiplayer_enabled(bool enabled) { -#ifndef USB_HOST // could be supported with USB host, but we'd need a hub multiplayer_enabled = enabled; if(!enabled) send_handshake(); -#endif } void send_multiplayer_message(const uint8_t *data, uint16_t len) { -#ifndef USB_HOST if(!peer_connected) return; @@ -239,5 +221,4 @@ void send_multiplayer_message(const uint8_t *data, uint16_t len) { send_all((uint8_t *)data, len); tud_cdc_write_flush(); -#endif } diff --git a/32blit-pico/usb_host.cpp b/32blit-pico/usb_host.cpp new file mode 100644 index 000000000..b550cca37 --- /dev/null +++ b/32blit-pico/usb_host.cpp @@ -0,0 +1,31 @@ +#include "usb.hpp" + +#include + +#include "tusb.h" + +#include "config.h" + +void init_usb() { + tusb_init(); +} + +void update_usb() { + tuh_task(); +} + +void usb_debug(const char *message) { + +} + +// multiplayer could be supported with USB host, but we'd need a hub +// (host code is used for hid) +bool is_multiplayer_connected() { + return false; +} + +void set_multiplayer_enabled(bool enabled) { +} + +void send_multiplayer_message(const uint8_t *data, uint16_t len) { +} From f22ffdbbe2af899f76fc2ec9d415ea4928cff9d7 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sun, 3 Oct 2021 20:11:43 +0100 Subject: [PATCH 3/5] pico: Add basic USB HID input support Requires Pico SDK 1.3.0+. Only tested on the two non-xbox controllers I could find. --- 32blit-pico/CMakeLists.txt | 4 ++ 32blit-pico/input_usb_hid.cpp | 65 +++++++++++++++++++ 32blit-pico/tusb_config.h | 4 ++ 32blit-pico/usb_host.cpp | 116 ++++++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+) create mode 100644 32blit-pico/input_usb_hid.cpp diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index 9e91bf1e3..0aa3d1ea9 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -101,6 +101,10 @@ elseif(BLIT_DISPLAY_DRIVER STREQUAL "st7789") list(APPEND BLIT_BOARD_DEFINITIONS DISPLAY_ST7789) # config defaults use this, also some games are using it for picosystem detection endif() +if(BLIT_INPUT_DRIVER STREQUAL "usb_hid") + list(APPEND BLIT_BOARD_DEFINITIONS INPUT_USB_HID) +endif() + if(BLIT_USB_DRIVER STREQUAL "host") list(APPEND BLIT_BOARD_DEFINITIONS USB_HOST) list(APPEND BLIT_BOARD_LIBRARIES tinyusb_host) diff --git a/32blit-pico/input_usb_hid.cpp b/32blit-pico/input_usb_hid.cpp new file mode 100644 index 000000000..b2b0f918d --- /dev/null +++ b/32blit-pico/input_usb_hid.cpp @@ -0,0 +1,65 @@ +// GPIO dpad + ABXY +#include "input.hpp" + +#include "hardware/gpio.h" + +#include "pico/binary_info.h" + +#include "engine/api_private.hpp" +#include "engine/input.hpp" + +// from USB code +extern uint32_t hid_gamepad_id; +extern uint8_t hid_joystick[2]; +extern uint8_t hid_hat; +extern uint32_t hid_buttons; +struct GamepadMapping { + uint32_t id; // vid:pid + uint8_t a, b, x, y; + uint8_t menu, home, joystick; +}; + +static const GamepadMapping gamepad_mappings[]{ + {0x15320705, 0, 1, 3, 4, 16, 15, 13}, // Razer Raiju Mobile + {0x20D6A711, 2, 1, 3, 0, 8, 12, 10}, // PowerA wired Switch pro controller + {0x00000000, 0, 1, 2, 3, 4, 5, 6} // probably wrong fallback +}; + +// hat -> dpad +const uint32_t dpad_map[]{ + blit::Button::DPAD_UP, + blit::Button::DPAD_UP | blit::Button::DPAD_RIGHT, + blit::Button::DPAD_RIGHT, + blit::Button::DPAD_DOWN | blit::Button::DPAD_RIGHT, + blit::Button::DPAD_DOWN, + blit::Button::DPAD_DOWN | blit::Button::DPAD_LEFT, + blit::Button::DPAD_LEFT, + blit::Button::DPAD_UP | blit::Button::DPAD_LEFT, + 0 +}; + +void init_input() { +} + +void update_input() { + using namespace blit; + + if(!hid_gamepad_id) + return; + + auto mapping = gamepad_mappings; + while(mapping->id && mapping->id != hid_gamepad_id) + mapping++; + + api.buttons = dpad_map[hid_hat > 8 ? 8 : hid_hat] + | (hid_buttons & (1 << mapping->a) ? uint32_t(Button::A) : 0) + | (hid_buttons & (1 << mapping->b) ? uint32_t(Button::B) : 0) + | (hid_buttons & (1 << mapping->x) ? uint32_t(Button::X) : 0) + | (hid_buttons & (1 << mapping->y) ? uint32_t(Button::Y) : 0) + | (hid_buttons & (1 << mapping->menu) ? uint32_t(Button::MENU) : 0) + | (hid_buttons & (1 << mapping->home) ? uint32_t(Button::HOME) : 0) + | (hid_buttons & (1 << mapping->joystick) ? uint32_t(Button::JOYSTICK) : 0); + + api.joystick.x = (float(hid_joystick[0]) - 0x80) / 0x80; + api.joystick.y = (float(hid_joystick[1]) - 0x80) / 0x80; +} diff --git a/32blit-pico/tusb_config.h b/32blit-pico/tusb_config.h index 05e3d7cfa..162717ffa 100644 --- a/32blit-pico/tusb_config.h +++ b/32blit-pico/tusb_config.h @@ -108,7 +108,11 @@ #define CFG_TUH_HUB 0 #define CFG_TUH_CDC 0 +#ifdef INPUT_USB_HID +#define CFG_TUH_HID 4 // typical keyboard + mouse device can have 3-4 HID interfaces +#else #define CFG_TUH_HID 0 +#endif #define CFG_TUH_MSC 0 #define CFG_TUH_VENDOR 0 diff --git a/32blit-pico/usb_host.cpp b/32blit-pico/usb_host.cpp index b550cca37..0a57fba21 100644 --- a/32blit-pico/usb_host.cpp +++ b/32blit-pico/usb_host.cpp @@ -6,6 +6,122 @@ #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; +uint8_t hid_joystick[2]{0x80, 0x80}; +uint8_t hid_hat = 8; +uint32_t hid_buttons = 0; + +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); + + hid_gamepad_id = (vid << 16) | pid; + + // 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; + + 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 + } else if(usage_page == HID_USAGE_PAGE_DESKTOP && usage == HID_USAGE_DESKTOP_HAT_SWITCH) { + hat_offset = bit_offset; + } else if(usage_page == HID_USAGE_PAGE_BUTTON && !(*p & HID_CONSTANT)) { + // assume this is "the buttons" + buttons_offset = bit_offset; + num_buttons = report_count; + } + + 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; + } + + if(!tuh_hid_receive_report(dev_addr, instance)) { + printf("Cound not request report!\n"); + } +} + +// 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; + + // 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 + void init_usb() { tusb_init(); } From 9e5e103f912ab529837311f9e4e73a75af5c9176 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sun, 29 Jan 2023 23:54:47 +0000 Subject: [PATCH 4/5] pico: Enable HID for VGA board --- 32blit-pico/board/vgaboard/config.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/32blit-pico/board/vgaboard/config.cmake b/32blit-pico/board/vgaboard/config.cmake index d31afc375..4dea17220 100644 --- a/32blit-pico/board/vgaboard/config.cmake +++ b/32blit-pico/board/vgaboard/config.cmake @@ -7,3 +7,5 @@ set(BLIT_BOARD_DEFINITIONS blit_driver(audio i2s) blit_driver(display scanvideo) +blit_driver(input usb_hid) +blit_driver(usb host) From 3d1ed599f0d37417b29a117fcd724506645122e0 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 31 Jan 2023 16:17:47 +0000 Subject: [PATCH 5/5] pico: HID keyboard This works most of the time. --- 32blit-pico/input_usb_hid.cpp | 64 +++++++++++++++++++++++++++++++++++ 32blit-pico/usb_host.cpp | 39 ++++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/32blit-pico/input_usb_hid.cpp b/32blit-pico/input_usb_hid.cpp index b2b0f918d..0dd4e6852 100644 --- a/32blit-pico/input_usb_hid.cpp +++ b/32blit-pico/input_usb_hid.cpp @@ -5,14 +5,19 @@ #include "pico/binary_info.h" +#include "class/hid/hid.h" + #include "engine/api_private.hpp" #include "engine/input.hpp" // from USB code extern uint32_t hid_gamepad_id; +extern bool hid_keyboard_detected; extern uint8_t hid_joystick[2]; extern uint8_t hid_hat; extern uint32_t hid_buttons; +extern uint8_t hid_keys[6]; + struct GamepadMapping { uint32_t id; // vid:pid uint8_t a, b, x, y; @@ -44,9 +49,68 @@ void init_input() { void update_input() { using namespace blit; + // keyboard + if(hid_keyboard_detected) { + uint32_t new_buttons = 0; + + for(int i = 0; i < 6; i++) { + switch(hid_keys[i]) { + case HID_KEY_ARROW_UP: + case HID_KEY_W: + new_buttons |= uint32_t(Button::DPAD_UP); + break; + case HID_KEY_ARROW_DOWN: + case HID_KEY_S: + new_buttons |= uint32_t(Button::DPAD_DOWN); + break; + case HID_KEY_ARROW_LEFT: + case HID_KEY_A: + new_buttons |= uint32_t(Button::DPAD_LEFT); + break; + case HID_KEY_ARROW_RIGHT: + case HID_KEY_D: + new_buttons |= uint32_t(Button::DPAD_RIGHT); + break; + + case HID_KEY_Z: + case HID_KEY_U: + new_buttons |= uint32_t(Button::A); + break; + case HID_KEY_X: + case HID_KEY_I: + new_buttons |= uint32_t(Button::B); + break; + case HID_KEY_C: + case HID_KEY_O: + new_buttons |= uint32_t(Button::X); + break; + case HID_KEY_V: + case HID_KEY_P: + new_buttons |= uint32_t(Button::Y); + break; + + case HID_KEY_1: + new_buttons |= uint32_t(Button::HOME); + break; + + case HID_KEY_2: + case HID_KEY_ESCAPE: + new_buttons |= uint32_t(Button::MENU); + break; + + case HID_KEY_3: + new_buttons |= uint32_t(Button::JOYSTICK); + break; + } + } + + api.buttons = new_buttons; + } + if(!hid_gamepad_id) return; + // gamepad auto mapping = gamepad_mappings; while(mapping->id && mapping->id != hid_gamepad_id) mapping++; diff --git a/32blit-pico/usb_host.cpp b/32blit-pico/usb_host.cpp index 0a57fba21..585d6d486 100644 --- a/32blit-pico/usb_host.cpp +++ b/32blit-pico/usb_host.cpp @@ -14,9 +14,11 @@ 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; @@ -24,7 +26,15 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re printf("Mount %i %i, %04x:%04x\n", dev_addr, instance, vid, pid); - hid_gamepad_id = (vid << 16) | 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; @@ -35,6 +45,8 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re int usage = -1; int report_count = 0, report_size = 0; + bool found_any = false; + int bit_offset = 0; while(p != desc_end) { @@ -50,12 +62,15 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re 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; @@ -83,16 +98,38 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re 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