From 43aa53382c76a0d48915e9c6b4f0f7fa87660768 Mon Sep 17 00:00:00 2001 From: Frederik Walk Date: Sat, 22 Apr 2023 18:10:42 +0200 Subject: [PATCH] Add xinput driver --- include/usb/usb_driver.h | 2 +- include/usb/xinput_driver.h | 41 ++++++++++++ include/utils/InputState.h | 3 + src/main.cpp | 2 +- src/usb/usb_driver.c | 24 +++---- src/usb/xinput_driver.c | 130 ++++++++++++++++++++++++++++++++++++ src/utils/InputState.cpp | 39 ++++++++++- 7 files changed, 224 insertions(+), 17 deletions(-) create mode 100644 include/usb/xinput_driver.h create mode 100644 src/usb/xinput_driver.c diff --git a/include/usb/usb_driver.h b/include/usb/usb_driver.h index 5e9faf4..9ee716a 100644 --- a/include/usb/usb_driver.h +++ b/include/usb/usb_driver.h @@ -19,7 +19,7 @@ typedef enum { // USB_MODE_SWITCH_HORIPAD, // USB_MODE_DUALSHOCK3, // USB_MODE_DUALSHOCK4, - // USB_MODE_XBOX360, + USB_MODE_XBOX360, USB_MODE_DEBUG, } usb_mode_t; diff --git a/include/usb/xinput_driver.h b/include/usb/xinput_driver.h new file mode 100644 index 0000000..0cf5d9a --- /dev/null +++ b/include/usb/xinput_driver.h @@ -0,0 +1,41 @@ +#ifndef _USB_XINPUT_DRIVER_H_ +#define _USB_XINPUT_DRIVER_H_ + +#include "usb/usb_driver.h" + +#include "device/usbd_pvt.h" + +#include + +#define USBD_XINPUT_NAME "XInput Gamepad" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct __attribute((packed, aligned(1))) { + uint8_t report_id; + uint8_t report_size; + uint8_t buttons1; + uint8_t buttons2; + uint8_t lt; + uint8_t rt; + int16_t lx; + int16_t ly; + int16_t rx; + int16_t ry; + uint8_t _reserved[6]; +} xinput_report_t; + +bool receive_xinput_report(void); +bool send_xinput_report(usb_report_t report); + +extern const tusb_desc_device_t xinput_desc_device; +extern const uint8_t xinput_desc_cfg[]; +extern const usbd_class_driver_t xinput_app_driver; + +#ifdef __cplusplus +} +#endif + +#endif // _USB_XINPUT_DRIVER_H_ \ No newline at end of file diff --git a/include/utils/InputState.h b/include/utils/InputState.h index 32fae51..c5ae5d4 100644 --- a/include/utils/InputState.h +++ b/include/utils/InputState.h @@ -2,6 +2,7 @@ #define _UTILS_INPUTSTATE_H_ #include "usb/usb_driver.h" +#include "usb/xinput_driver.h" #include #include @@ -18,8 +19,10 @@ struct InputState { Drum drum; private: + xinput_report_t m_xinput_report; std::string m_debug_report; + usb_report_t getXinputReport(); usb_report_t getDebugReport(); public: diff --git a/src/main.cpp b/src/main.cpp index f7f9716..3427cdb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,7 +36,7 @@ int main() { Utils::InputState input_state; Peripherals::Drum drum(Config::Default::drum_config); - usb_mode_t mode = USB_MODE_DEBUG; + usb_mode_t mode = USB_MODE_XBOX360; usb_driver_init(mode); diff --git a/src/usb/usb_driver.c b/src/usb/usb_driver.c index 3906ba3..12c20d0 100644 --- a/src/usb/usb_driver.c +++ b/src/usb/usb_driver.c @@ -1,7 +1,7 @@ #include "usb/usb_driver.h" #include "usb/debug_driver.h" // #include "usb/hid_driver.h" -// #include "usb/xinput_driver.h" +#include "usb/xinput_driver.h" #include "bsp/board.h" #include "pico/unique_id.h" @@ -18,13 +18,13 @@ static bool (*usbd_receive_report)() = NULL; static char usbd_serial_str[USBD_SERIAL_STR_SIZE] = {}; char *const usbd_desc_str[] = { - [USBD_STR_MANUFACTURER] = USBD_MANUFACTURER, // - [USBD_STR_PRODUCT] = USBD_PRODUCT, // - [USBD_STR_SERIAL] = usbd_serial_str, // + [USBD_STR_MANUFACTURER] = USBD_MANUFACTURER, // + [USBD_STR_PRODUCT] = USBD_PRODUCT, // + [USBD_STR_SERIAL] = usbd_serial_str, // // [USBD_STR_SWITCH] = USBD_SWITCH_NAME, // // [USBD_STR_PS3] = USBD_PS3_NAME, // // [USBD_STR_PS4] = USBD_PS4_NAME, // - // [USBD_STR_XINPUT] = USBD_XINPUT_NAME, // + [USBD_STR_XINPUT] = USBD_XINPUT_NAME, // [USBD_STR_CDC] = USBD_DEBUG_CDC_NAME, // [USBD_STR_RPI_RESET] = USBD_DEBUG_RESET_NAME, // }; @@ -65,13 +65,13 @@ void usb_driver_init(usb_mode_t mode) { // usbd_send_report = send_hid_ps4_report; // usbd_receive_report = NULL; // break; - // case USB_MODE_XBOX360: - // usbd_desc_device = &xinput_desc_device; - // usbd_desc_cfg = xinput_desc_cfg; - // usbd_app_driver = &xinput_app_driver; - // usbd_send_report = send_xinput_report; - // usbd_receive_report = receive_xinput_report; - // break; + case USB_MODE_XBOX360: + usbd_desc_device = &xinput_desc_device; + usbd_desc_cfg = xinput_desc_cfg; + usbd_app_driver = &xinput_app_driver; + usbd_send_report = send_xinput_report; + usbd_receive_report = receive_xinput_report; + break; case USB_MODE_DEBUG: usbd_desc_device = &debug_desc_device; usbd_desc_cfg = debug_desc_cfg; diff --git a/src/usb/xinput_driver.c b/src/usb/xinput_driver.c new file mode 100644 index 0000000..1f855a2 --- /dev/null +++ b/src/usb/xinput_driver.c @@ -0,0 +1,130 @@ +#include "usb/xinput_driver.h" +#include "usb/usb_driver.h" + +#include "tusb.h" + +#define XINPUT_OUT_SIZE 32 +#define XINPUT_INTERFACE_SUBCLASS (0x5D) +#define XINPUT_INTERFACE_PROTOCOL (0x01) +#define TUD_XINPUT_DESC_LEN (39) + +const tusb_desc_device_t xinput_desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_VENDOR_SPECIFIC, + .bDeviceSubClass = 0xFF, + .bDeviceProtocol = 0xFF, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .idVendor = 0x045E, + .idProduct = 0x028E, + .bcdDevice = 0x0114, + .iManufacturer = USBD_STR_MANUFACTURER, + .iProduct = USBD_STR_PRODUCT, + .iSerialNumber = USBD_STR_SERIAL, + .bNumConfigurations = 1, +}; + +enum { + USBD_ITF_XINPUT, + USBD_ITF_MAX, +}; + +#define TUD_XINPUT_DESCRIPTOR(_itfnum, _stridx) \ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_VENDOR_SPECIFIC, XINPUT_INTERFACE_SUBCLASS, \ + XINPUT_INTERFACE_PROTOCOL, _stridx, 0x10, 0x21, 0x10, 0x01, 0x01, 0x24, 0x81, 0x14, 0x03, 0x00, 0x03, 0x13, \ + 0x01, 0x00, 0x03, 0x00, 0x07, 0x05, 0x81, 0x03, 0x20, 0x00, 0x01, 0x07, 0x05, 0x01, 0x03, 0x20, 0x00, 0x08 + +#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_XINPUT_DESC_LEN) + +const uint8_t xinput_desc_cfg[USBD_DESC_LEN] = { + TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_LANGUAGE, USBD_DESC_LEN, 0, USBD_MAX_POWER_MAX), + TUD_XINPUT_DESCRIPTOR(USBD_ITF_XINPUT, USBD_STR_XINPUT), +}; + +static uint8_t endpoint_in = 0; +static uint8_t endpoint_out = 0; +static uint8_t xinput_out_buffer[XINPUT_OUT_SIZE] = {}; + +bool receive_xinput_report(void) { + bool success = false; + + if (tud_ready() && (endpoint_out != 0) && (!usbd_edpt_busy(0, endpoint_out))) { + usbd_edpt_claim(0, endpoint_out); // Take control of OUT endpoint + success = usbd_edpt_xfer(0, endpoint_out, xinput_out_buffer, XINPUT_OUT_SIZE); // Retrieve report buffer + usbd_edpt_release(0, endpoint_out); // Release control of OUT endpoint + } + return success; +} + +bool send_xinput_report(usb_report_t report) { + bool success = false; + + if (tud_ready() && // Is the device ready? + (endpoint_in != 0) && (!usbd_edpt_busy(0, endpoint_in)) // Is the IN endpoint available? + ) { + usbd_edpt_claim(0, endpoint_in); // Take control of IN endpoint + success = usbd_edpt_xfer(0, endpoint_in, report.data, report.size); // Send report buffer + usbd_edpt_release(0, endpoint_in); // Release control of IN endpoint + } + + return success; +} + +static void xinput_init(void) {} + +static void xinput_reset(uint8_t rhport) { (void)rhport; } + +static uint16_t xinput_open(uint8_t rhport, tusb_desc_interface_t const *itf_descriptor, uint16_t max_length) { + uint16_t driver_length = + sizeof(tusb_desc_interface_t) + (itf_descriptor->bNumEndpoints * sizeof(tusb_desc_endpoint_t)) + 16; + + TU_VERIFY(max_length >= driver_length, 0); + + uint8_t const *current_descriptor = tu_desc_next(itf_descriptor); + uint8_t found_endpoints = 0; + while ((found_endpoints < itf_descriptor->bNumEndpoints) && (driver_length <= max_length)) { + tusb_desc_endpoint_t const *endpoint_descriptor = (tusb_desc_endpoint_t const *)current_descriptor; + if (TUSB_DESC_ENDPOINT == tu_desc_type(endpoint_descriptor)) { + TU_ASSERT(usbd_edpt_open(rhport, endpoint_descriptor)); + + if (tu_edpt_dir(endpoint_descriptor->bEndpointAddress) == TUSB_DIR_IN) + endpoint_in = endpoint_descriptor->bEndpointAddress; + else + endpoint_out = endpoint_descriptor->bEndpointAddress; + + ++found_endpoints; + } + + current_descriptor = tu_desc_next(current_descriptor); + } + return driver_length; +} + +static bool xinput_control_xfer_callback(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) { + (void)rhport; + (void)stage; + (void)request; + + return true; +} + +static bool xinput_xfer_callback(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + (void)rhport; + (void)ep_addr; + (void)result; + (void)xferred_bytes; + + return true; +} + +const usbd_class_driver_t xinput_app_driver = { +#if CFG_TUSB_DEBUG >= 2 + .name = "XINPUT", +#endif + .init = xinput_init, + .reset = xinput_reset, + .open = xinput_open, + .control_xfer_cb = xinput_control_xfer_callback, + .xfer_cb = xinput_xfer_callback, + .sof = NULL}; diff --git a/src/utils/InputState.cpp b/src/utils/InputState.cpp index fffe0e5..7043a07 100644 --- a/src/utils/InputState.cpp +++ b/src/utils/InputState.cpp @@ -4,7 +4,9 @@ namespace Doncon::Utils { -InputState::InputState() : drum({false, false, false, false}) {} +InputState::InputState() + : drum({false, false, false, false}), m_xinput_report({0x00, sizeof(xinput_report_t), 0, 0, 0, 0, 0, 0, 0, 0, {}}) { +} usb_report_t InputState::getReport(usb_mode_t mode) { switch (mode) { @@ -16,14 +18,45 @@ usb_report_t InputState::getReport(usb_mode_t mode) { // case USB_MODE_PS4_DIVACON: // case USB_MODE_DUALSHOCK4: // return getPS4InputReport(); - // case USB_MODE_XBOX360: - // return getXinputReport(); + case USB_MODE_XBOX360: + return getXinputReport(); case USB_MODE_DEBUG: default: return getDebugReport(); } } +usb_report_t InputState::getXinputReport() { + m_xinput_report.buttons1 = 0 // + | (false ? (1 << 0) : 0) // Dpad Up + | (drum.don_left ? (1 << 1) : 0) // Dpad Down + | (drum.ka_left ? (1 << 2) : 0) // Dpad Left + | (false ? (1 << 3) : 0) // Dpad Right + | (false ? (1 << 4) : 0) // Start + | (false ? (1 << 5) : 0) // Select + | (false ? (1 << 6) : 0) // L3 + | (false ? (1 << 7) : 0); // R3 + + m_xinput_report.buttons2 = 0 // + | (false ? (1 << 0) : 0) // L1 + | (false ? (1 << 1) : 0) // R1 + | (false ? (1 << 2) : 0) // Guide + | (drum.don_right ? (1 << 4) : 0) // A + | (drum.ka_right ? (1 << 5) : 0) // B + | (false ? (1 << 6) : 0) // X + | (false ? (1 << 7) : 0); // Y + + m_xinput_report.lt = 0; + m_xinput_report.rt = 0; + + m_xinput_report.lx = 0; + m_xinput_report.ly = 0; + m_xinput_report.rx = 0; + m_xinput_report.ry = 0; + + return {(uint8_t *)&m_xinput_report, sizeof(xinput_report_t)}; +} + usb_report_t InputState::getDebugReport() { std::stringstream out;