Add xinput driver

This commit is contained in:
Frederik Walk 2023-04-22 18:10:42 +02:00
parent 50486c3f20
commit 43aa53382c
7 changed files with 224 additions and 17 deletions

View File

@ -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;

View File

@ -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 <stdint.h>
#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_

View File

@ -2,6 +2,7 @@
#define _UTILS_INPUTSTATE_H_
#include "usb/usb_driver.h"
#include "usb/xinput_driver.h"
#include <stdint.h>
#include <string>
@ -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:

View File

@ -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);

View File

@ -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;

130
src/usb/xinput_driver.c Normal file
View File

@ -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};

View File

@ -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;