diff --git a/.gitmodules b/.gitmodules index a354889..5572084 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,4 @@ url = https://github.com/hathach/tinyusb [submodule "sw/tusb_xinput"] path = sw/tusb_xinput - url = https://github.com/RobertDaleSmith/tusb_xinput + url = https://github.com/Ryzee119/tusb_xinput diff --git a/sw/CMakeLists.txt b/sw/CMakeLists.txt index f247ef5..b5e6726 100644 --- a/sw/CMakeLists.txt +++ b/sw/CMakeLists.txt @@ -100,18 +100,17 @@ function(config_target TARGET_NAME MULTIFW) # USB stack common stuff target_link_libraries(${TARGET_NAME} tinyusb_host) target_compile_definitions(${TARGET_NAME} PRIVATE USB_STACK=1) + target_sources(${TARGET_NAME} PRIVATE usb_hid/hid_app.c) endif() if(USB_JOYSTICK) # Joystick stuff - target_sources(${TARGET_NAME} PRIVATE joy_hid/joy_hid.c) target_link_libraries(${TARGET_NAME} hardware_pwm) - target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/joy_hid) - target_link_libraries(${TARGET_NAME} xinput_host_app_driver) + target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/usb_hid) + target_link_libraries(${TARGET_NAME} xinput_host) target_compile_definitions(${TARGET_NAME} PRIVATE USB_JOYSTICK=1) endif() if (USB_MOUSE) # USB Mouse stuff - target_sources(${TARGET_NAME} PRIVATE mouse/mouse_hid.c) target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/mouse) target_compile_definitions(${TARGET_NAME} PRIVATE USB_MOUSE=1) endif() @@ -143,7 +142,7 @@ add_subdirectory(opl) ################################################################################ # Build GUS firmware function(build_gus TARGET_NAME MULTIFW) - set(USB_JOYSTICK 1) + set(USB_JOYSTICK TRUE) config_target(${TARGET_NAME} ${MULTIFW}) pico_set_program_name(${TARGET_NAME} "picogus-gus") target_sources(${TARGET_NAME} PRIVATE gusplay.cpp pico_pic.c isa_dma.c) @@ -177,7 +176,7 @@ endfunction() ################################################################################ # Build SB firmware function(build_sb TARGET_NAME MULTIFW) - set(USB_JOYSTICK 1) + set(USB_JOYSTICK TRUE) config_target(${TARGET_NAME} ${MULTIFW}) pico_set_program_name(${TARGET_NAME} "picogus-sb") target_sources(${TARGET_NAME} PRIVATE sbdsp.cpp sbplay.cpp pico_pic.c isa_dma.c) @@ -200,8 +199,8 @@ endfunction() ################################################################################ # Build AdLib firmware function(build_adlib TARGET_NAME MULTIFW) - set(USB_JOYSTICK 1) - set(USB_MOUSE 1) + set(USB_JOYSTICK TRUE) + set(USB_MOUSE TRUE) config_target(${TARGET_NAME} ${MULTIFW}) pico_set_program_name(${TARGET_NAME} "picogus-sb") target_sources(${TARGET_NAME} PRIVATE sbplay.cpp) @@ -221,7 +220,7 @@ endfunction() ################################################################################ # Build MPU firmware function(build_mpu TARGET_NAME MULTIFW) - set(USB_JOYSTICK 1) + set(USB_JOYSTICK TRUE) config_target(${TARGET_NAME} ${MULTIFW}) pico_set_program_name(${TARGET_NAME} "picogus-mpu401") target_compile_definitions(${TARGET_NAME} PRIVATE @@ -246,8 +245,8 @@ endfunction() ################################################################################ # Build Tandy firmware function(build_tandy TARGET_NAME MULTIFW) - set(USB_JOYSTICK 1) - set(USB_MOUSE 1) + set(USB_JOYSTICK TRUE) + set(USB_MOUSE TRUE) config_target(${TARGET_NAME} ${MULTIFW}) pico_set_program_name(${TARGET_NAME} "picogus-tandy") target_compile_definitions(${TARGET_NAME} PRIVATE @@ -267,8 +266,8 @@ endfunction() ################################################################################ # Build CMS firmware function(build_cms TARGET_NAME MULTIFW) - set(USB_JOYSTICK 1) - set(USB_MOUSE 1) + set(USB_JOYSTICK TRUE) + set(USB_MOUSE TRUE) config_target(${TARGET_NAME} ${MULTIFW}) pico_set_program_name(${TARGET_NAME} "picogus-cms") target_compile_definitions(${TARGET_NAME} PRIVATE @@ -287,8 +286,8 @@ endfunction() ################################################################################ # Build USB-only firmware (joystick + mouse support) function(build_usb TARGET_NAME MULTIFW) - set(USB_JOYSTICK 1) - set(USB_MOUSE 1) + set(USB_JOYSTICK TRUE) + set(USB_MOUSE TRUE) config_target(${TARGET_NAME} ${MULTIFW}) pico_set_program_name(${TARGET_NAME} "picogus-usb") target_compile_definitions(${TARGET_NAME} PRIVATE diff --git a/sw/mouse/mouse_hid.c b/sw/mouse/mouse_hid.c deleted file mode 100644 index 8054231..0000000 --- a/sw/mouse/mouse_hid.c +++ /dev/null @@ -1,178 +0,0 @@ -// Adapted from hid_controller example from TinyUSB for PicoGUS -/* - * The MIT License (MIT) - * - * Copyright (c) 2021, Ha Thach (tinyusb.org) - * Copyright (c) 2023, Ian Scott - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -#include "tusb.h" -#include "sermouse.h" - -#define MAX_REPORT 4 - -// Each HID instance can has multiple reports -static struct -{ - uint8_t report_count; - tuh_hid_report_info_t report_info[MAX_REPORT]; -}hid_info[CFG_TUH_HID]; - -static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len); - -//--------------------------------------------------------------------+ -// TinyUSB Callbacks -//--------------------------------------------------------------------+ - -// Invoked when device with hid interface is mounted -// Report descriptor is also available for use. tuh_hid_parse_report_descriptor() -// can be used to parse common/simple enough descriptor. -// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped -// therefore report_desc = NULL, desc_len = 0 -void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) -{ - printf("HID dev:%d inst:%d mounted\r\n", dev_addr, instance); - - // Interface protocol (hid_interface_protocol_enum_t) - const char* protocol_str[] = { "n/a", "kbd", "mouse" }; - uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); - - printf("HID protocol=%s\r\n", protocol_str[itf_protocol]); - - // By default host stack will use activate boot protocol on supported interface. - // Therefore for this simple example, we only need to parse generic report descriptor (with built-in parser) - if ( itf_protocol == HID_ITF_PROTOCOL_NONE ) - { - hid_info[instance].report_count = tuh_hid_parse_report_descriptor(hid_info[instance].report_info, MAX_REPORT, desc_report, desc_len); - printf("HID has %u reports \r\n", hid_info[instance].report_count); - } else { - // force boot protocol - tuh_hid_set_protocol(dev_addr, instance, HID_PROTOCOL_BOOT); - } - - // request to receive report - // tuh_hid_report_received_cb() will be invoked when report is available - if ( !tuh_hid_receive_report(dev_addr, instance) ) - { - printf("err(%d:%d) can't recv HID report\r\n", dev_addr, instance); - } -} - -// Invoked when device with hid interface is un-mounted -void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) -{ - printf("HID dev:%d inst:%d unmounted\r\n", dev_addr, instance); -} - -// Invoked when received report from device via interrupt endpoint -void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) -{ - uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); - - switch (itf_protocol) - { - case HID_ITF_PROTOCOL_MOUSE: - sermouse_process_report( (hid_mouse_report_t const*) report ); - break; - - case HID_ITF_PROTOCOL_KEYBOARD: - // skip keyboard reports - break; - - default: - // Generic report requires matching ReportID and contents with previous parsed report info - process_generic_report(dev_addr, instance, report, len); - break; - } - - // continue to request to receive report - if ( !tuh_hid_receive_report(dev_addr, instance) ) - { - printf("err(%d:%d) can't recv HID report\r\n", dev_addr, instance); - } -} - -//--------------------------------------------------------------------+ -// Mouse -//--------------------------------------------------------------------+ - -//--------------------------------------------------------------------+ -// Generic Report -//--------------------------------------------------------------------+ -static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) -{ - (void) dev_addr; - - uint8_t const rpt_count = hid_info[instance].report_count; - tuh_hid_report_info_t* rpt_info_arr = hid_info[instance].report_info; - tuh_hid_report_info_t* rpt_info = NULL; - - if ( rpt_count == 1 && rpt_info_arr[0].report_id == 0) - { - // Simple report without report ID as 1st byte - rpt_info = &rpt_info_arr[0]; - }else - { - // Composite report, 1st byte is report ID, data starts from 2nd byte - uint8_t const rpt_id = report[0]; - - // Find report id in the array - for(uint8_t i=0; iusage_page == HID_USAGE_PAGE_DESKTOP ) - { - switch (rpt_info->usage) - { - case HID_USAGE_DESKTOP_MOUSE: - TU_LOG1("HID receive mouse report\r\n"); - // Assume mouse follow boot report layout - sermouse_process_report( (hid_mouse_report_t const*) report ); - break; - - default: break; - } - } -} \ No newline at end of file diff --git a/sw/picogus.cpp b/sw/picogus.cpp index 4d06d36..ef52141 100644 --- a/sw/picogus.cpp +++ b/sw/picogus.cpp @@ -87,7 +87,7 @@ cms_buffer_t cms_buffer = { {0}, 0, 0 }; #endif #ifdef USB_JOYSTICK -#include "joy_hid/joy.h" +#include "usb_hid/joy.h" extern "C" joystate_struct_t joystate_struct; uint8_t joystate_bin; #include "hardware/pwm.h" diff --git a/sw/joy_hid/joy_hid.c b/sw/usb_hid/hid_app.c similarity index 67% rename from sw/joy_hid/joy_hid.c rename to sw/usb_hid/hid_app.c index ef6b3d3..fffe021 100644 --- a/sw/joy_hid/joy_hid.c +++ b/sw/usb_hid/hid_app.c @@ -25,10 +25,22 @@ * */ -/* #include "bsp/board_api.h" */ #include "tusb.h" +#ifdef USB_JOYSTICK #include "xinput_host.h" +#endif +#ifdef USB_MOUSE +#include "sermouse.h" +#endif +#define MAX_REPORT 4 +static struct +{ + uint8_t report_count; + tuh_hid_report_info_t report_info[MAX_REPORT]; +} hid_info[CFG_TUH_HID]; + +#ifdef USB_JOYSTICK #include "joy.h" joystate_struct_t joystate_struct; @@ -97,48 +109,7 @@ static inline bool is_ps_classic(uint8_t dev_addr) ); } -//--------------------------------------------------------------------+ -// TinyUSB Callbacks -//--------------------------------------------------------------------+ - -// Invoked when device with hid interface is mounted -// Report descriptor is also available for use. tuh_hid_parse_report_descriptor() -// can be used to parse common/simple enough descriptor. -// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped -// therefore report_desc = NULL, desc_len = 0 -#define MAX_REPORT 4 -void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) -{ - uint16_t vid, pid; - tuh_vid_pid_get(dev_addr, &vid, &pid); - - printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance); - printf("VID = %04x, PID = %04x\r\n", vid, pid); - - tuh_hid_report_info_t report_info[MAX_REPORT]; - uint8_t report_count = tuh_hid_parse_report_descriptor(report_info, MAX_REPORT, desc_report, desc_len); - - for (int i = 0; i < report_count; ++i) { - printf("HID device report id: %u usage: %u usage page: %u\n", - report_info[i].report_id, report_info[i].usage, report_info[i].usage_page); - if (report_info[i].usage == HID_USAGE_DESKTOP_JOYSTICK || report_info[i].usage == HID_USAGE_DESKTOP_GAMEPAD) { - printf("joystick connected - requesting reports from it\n"); - if ( !tuh_hid_receive_report(dev_addr, instance) ) - { - printf("Error: cannot request to receive report\r\n"); - } - return; - } - } -} - -// Invoked when device with hid interface is un-mounted -void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) -{ - printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance); -} - -void update_joystate(uint8_t dpad, uint8_t x, uint8_t y, uint8_t z, uint8_t rz, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4) { +static inline void update_joystate(uint8_t dpad, uint8_t x, uint8_t y, uint8_t z, uint8_t rz, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4) { joystate_struct.button_mask = (!b1 << 4) | (!b2 << 5) | (!b3 << 6) | (!b4 << 7); switch (dpad) { case 0: // N @@ -182,7 +153,7 @@ void update_joystate(uint8_t dpad, uint8_t x, uint8_t y, uint8_t z, uint8_t rz, joystate_struct.joy2_y = rz; } -inline void process_sony_ds4(uint8_t const* report, uint16_t len) +static inline void process_sony_ds4(uint8_t const* report, uint16_t len) { uint8_t const report_id = report[0]; report++; @@ -224,27 +195,147 @@ inline void process_sony_ds4(uint8_t const* report, uint16_t len) */ } } +#endif + +//--------------------------------------------------------------------+ +// TinyUSB Callbacks +//--------------------------------------------------------------------+ + +// Invoked when device with hid interface is mounted +// Report descriptor is also available for use. tuh_hid_parse_report_descriptor() +// can be used to parse common/simple enough descriptor. +// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped +// therefore report_desc = NULL, desc_len = 0 +void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) +{ + printf("HID dev:%d inst:%d mounted\r\n", dev_addr, instance); + + // Interface protocol (hid_interface_protocol_enum_t) + const char* protocol_str[] = { "n/a", "kbd", "mouse" }; + uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); + + printf("HID protocol=%s\r\n", protocol_str[itf_protocol]); + + // By default host stack will use activate boot protocol on supported interface. + // Therefore for this simple example, we only need to parse generic report descriptor (with built-in parser) + if (itf_protocol == HID_ITF_PROTOCOL_NONE) { + hid_info[instance].report_count = tuh_hid_parse_report_descriptor(hid_info[instance].report_info, MAX_REPORT, desc_report, desc_len); + printf("HID has %u reports \r\n", hid_info[instance].report_count); + } else { + // force boot protocol + tuh_hid_set_protocol(dev_addr, instance, HID_PROTOCOL_BOOT); + } + + // request to receive report + // tuh_hid_report_received_cb() will be invoked when report is available + if (!tuh_hid_receive_report(dev_addr, instance)) { + printf("err(%d:%d) can't recv HID report\r\n", dev_addr, instance); + } +} + +// Invoked when device with hid interface is un-mounted +void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) +{ + printf("HID dev:%d inst:%d unmounted\r\n", dev_addr, instance); +} + +//--------------------------------------------------------------------+ +// Generic Report +//--------------------------------------------------------------------+ +static inline void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) +{ + (void) dev_addr; + + uint8_t const rpt_count = hid_info[instance].report_count; + tuh_hid_report_info_t* rpt_info_arr = hid_info[instance].report_info; + tuh_hid_report_info_t* rpt_info = NULL; + + if (rpt_count == 1 && rpt_info_arr[0].report_id == 0) { + // Simple report without report ID as 1st byte + rpt_info = &rpt_info_arr[0]; + } else { + // Composite report, 1st byte is report ID, data starts from 2nd byte + uint8_t const rpt_id = report[0]; + + // Find report id in the array + for (uint8_t i=0; iusage_page == HID_USAGE_PAGE_DESKTOP) { + switch (rpt_info->usage) { + case HID_USAGE_DESKTOP_MOUSE: + TU_LOG1("HID receive mouse report\r\n"); + // Assume mouse follow boot report layout +#ifdef USB_MOUSE + sermouse_process_report((hid_mouse_report_t const*) report); +#endif + break; + case HID_USAGE_DESKTOP_JOYSTICK: + TU_LOG1("HID receive joystick report\r\n"); +#ifdef USB_JOYSTICK + if (is_sony_ds4(dev_addr)) { + process_sony_ds4(report, len); + } +#endif + break; + case HID_USAGE_DESKTOP_GAMEPAD: + TU_LOG1("HID receive gamepad report\r\n"); + break; + + default: break; + } + } +} // Invoked when received report from device via interrupt endpoint void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) { - /* - for (uint16_t i = 0; i < len; ++i) { - printf("%02x ", report[i]); - } - putchar('\n'); - */ - if (is_sony_ds4(dev_addr)) { - process_sony_ds4(report, len); + uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); + + switch (itf_protocol) { + case HID_ITF_PROTOCOL_MOUSE: +#ifdef USB_MOUSE + sermouse_process_report((hid_mouse_report_t const*) report); +#endif + break; + + case HID_ITF_PROTOCOL_KEYBOARD: + // skip keyboard reports + break; + + default: + // Generic report requires matching ReportID and contents with previous parsed report info + process_generic_report(dev_addr, instance, report, len); + break; } // continue to request to receive report if (!tuh_hid_receive_report(dev_addr, instance)) { - printf("Error: cannot request to receive report\r\n"); + printf("err(%d:%d) can't recv HID report\r\n", dev_addr, instance); } } -inline void update_joystate_xinput(uint16_t wButtons, int16_t sThumbLX, int16_t sThumbLY, int16_t sThumbRX, int16_t sThumbRY, uint8_t bLeftTrigger, uint8_t bRightTrigger) { +#ifdef USB_JOYSTICK +static inline void update_joystate_xinput(uint16_t wButtons, int16_t sThumbLX, int16_t sThumbLY, int16_t sThumbRX, int16_t sThumbRY, uint8_t bLeftTrigger, uint8_t bRightTrigger) { uint8_t dpad = wButtons & 0xf; if (!dpad) { joystate_struct.joy1_x = ((int32_t)sThumbLX + 32768) >> 8; @@ -253,6 +344,7 @@ inline void update_joystate_xinput(uint16_t wButtons, int16_t sThumbLX, int16_t joystate_struct.joy1_x = (dpad & XINPUT_GAMEPAD_DPAD_RIGHT) ? 255 : ((dpad & XINPUT_GAMEPAD_DPAD_LEFT) ? 0 : 127); joystate_struct.joy1_y = (dpad & XINPUT_GAMEPAD_DPAD_DOWN) ? 255 : ((dpad & XINPUT_GAMEPAD_DPAD_UP) ? 0 : 127); } + // Analog triggers are mapped to up/down on joystick 1 to emulate throttle/brake for driving games if (bLeftTrigger) { joystate_struct.joy1_y = 127u + (bLeftTrigger >> 1); } else if (bRightTrigger) { @@ -264,10 +356,9 @@ inline void update_joystate_xinput(uint16_t wButtons, int16_t sThumbLX, int16_t /* printf("%04x %04x\n", wButtons, joystate_struct.button_mask); */ } -void tuh_xinput_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) +void tuh_xinput_report_received_cb(uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* xid_itf, uint16_t len) { - xinputh_interface_t *xid_itf = (xinputh_interface_t *)report; - xinput_gamepad_t *p = &xid_itf->pad; + const xinput_gamepad_t *p = &xid_itf->pad; /* const char* type_str; switch (xid_itf->type) @@ -338,3 +429,4 @@ void tuh_xinput_umount_cb(uint8_t dev_addr, uint8_t instance) { printf("XINPUT UNMOUNTED %02x %d\n", dev_addr, instance); } +#endif diff --git a/sw/joy_hid/joy.h b/sw/usb_hid/joy.h similarity index 100% rename from sw/joy_hid/joy.h rename to sw/usb_hid/joy.h diff --git a/sw/joy_hid/tusb_config.h b/sw/usb_hid/tusb_config.h similarity index 100% rename from sw/joy_hid/tusb_config.h rename to sw/usb_hid/tusb_config.h