Port over the remaining USB modes from DivaCon

This commit is contained in:
Frederik Walk 2023-07-04 21:57:42 +02:00
parent e86d869c46
commit ee3eaac3d6
15 changed files with 1303 additions and 54 deletions

View File

@ -47,7 +47,7 @@ extern "C" {
#define CFG_TUD_CDC (1)
#define CFG_TUD_MSC (0)
#define CFG_TUD_MIDI (0)
#define CFG_TUD_HID (0)
#define CFG_TUD_HID (1)
#define CFG_TUD_VENDOR (0)
// Device class buffer sizes

21
include/usb/hid_driver.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef _USB_HID_DRIVER_H_
#define _USB_HID_DRIVER_H_
#include "usb/hid_ps3_driver.h"
#include "usb/hid_ps4_driver.h"
#include "usb/hid_switch_driver.h"
#include "usb/usb_driver.h"
#include "device/usbd_pvt.h"
#ifdef __cplusplus
extern "C" {
#endif
extern const usbd_class_driver_t hid_app_driver;
#ifdef __cplusplus
}
#endif
#endif // _USB_HID_DRIVER_H_

View File

@ -0,0 +1,57 @@
#ifndef _USB_HID_PS3_DRIVER_H_
#define _USB_HID_PS3_DRIVER_H_
#include "usb/usb_driver.h"
#include "device/usbd_pvt.h"
#include <stdint.h>
#define USBD_PS3_NAME "Dualshock3 Emulation"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct __attribute((packed, aligned(1))) {
uint8_t report_id;
uint8_t padding;
uint8_t buttons1;
uint8_t buttons2;
uint8_t buttons3;
uint8_t padding1;
uint8_t lx;
uint8_t ly;
uint8_t rx;
uint8_t ry;
uint8_t padding2[8];
uint8_t lt;
uint8_t rt;
uint8_t padding3[9];
uint8_t unknown_0x02_1;
uint8_t battery;
uint8_t unknown_0x12;
uint8_t padding4[4];
uint8_t unknown[5];
uint16_t acc_x;
uint16_t acc_z;
uint16_t acc_y;
uint8_t padding5;
uint8_t unknown_0x02_2;
} hid_ps3_report_t;
extern const tusb_desc_device_t ds3_desc_device;
extern const uint8_t ps3_desc_cfg[];
extern const uint8_t ps3_desc_hid_report[];
bool send_hid_ps3_report(usb_report_t report);
uint16_t hid_ps3_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer,
uint16_t reqlen);
void hid_ps3_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer,
uint16_t bufsize);
#ifdef __cplusplus
}
#endif
#endif // _USB_HID_PS3_DRIVER_H_

View File

@ -0,0 +1,63 @@
#ifndef _USB_HID_PS4_DRIVER_H_
#define _USB_HID_PS4_DRIVER_H_
#include "usb/usb_driver.h"
#include "device/usbd_pvt.h"
#include <stdint.h>
#define USBD_PS4_NAME "Dualshock4 Emulation"
#ifdef __cplusplus
extern "C" {
#endif
// https://www.psdevwiki.com/ps4/DS4-USB
typedef struct __attribute((packed, aligned(1))) {
uint8_t report_id;
uint8_t lx;
uint8_t ly;
uint8_t rx;
uint8_t ry;
uint8_t buttons1;
uint8_t buttons2;
uint8_t buttons3;
uint8_t lt;
uint8_t rt;
uint16_t timestamp;
uint8_t battery;
uint16_t gyrox;
uint16_t gyroy;
uint16_t gyroz;
int16_t accelx;
int16_t accely;
int16_t accelz;
uint8_t unknown1[5];
uint8_t extension;
uint8_t unknown2[2];
uint8_t touchpad_event_active;
uint8_t touchpad_counter;
uint8_t touchpad1_touches;
uint8_t touchpad1_position[3];
uint8_t touchpad2_touches;
uint8_t touchpad2_position[3];
uint8_t unknown3[21];
} hid_ps4_report_t;
extern const tusb_desc_device_t ps4_tatacon_desc_device;
extern const tusb_desc_device_t ds4_desc_device;
extern const uint8_t ps4_desc_cfg[];
extern const uint8_t ps4_desc_hid_report[];
bool send_hid_ps4_report(usb_report_t report);
uint16_t hid_ps4_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer,
uint16_t reqlen);
void hid_ps4_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer,
uint16_t bufsize);
#ifdef __cplusplus
}
#endif
#endif // _USB_HID_PS4_DRIVER_H_

View File

@ -0,0 +1,41 @@
#ifndef _USB_HID_SWITCH_DRIVER_H_
#define _USB_HID_SWITCH_DRIVER_H_
#include "usb/usb_driver.h"
#include "device/usbd_pvt.h"
#include <stdint.h>
#define USBD_SWITCH_NAME "Switch Horipad Emulation"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct __attribute((packed, aligned(1))) {
uint16_t buttons;
uint8_t hat;
uint8_t lx;
uint8_t ly;
uint8_t rx;
uint8_t ry;
uint8_t vendor;
} hid_switch_report_t;
extern const tusb_desc_device_t switch_tatacon_desc_device;
extern const tusb_desc_device_t switch_horipad_desc_device;
extern const uint8_t switch_desc_cfg[];
extern const uint8_t switch_desc_hid_report[];
bool send_hid_switch_report(usb_report_t report);
uint16_t hid_switch_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer,
uint16_t reqlen);
void hid_switch_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer,
uint16_t bufsize);
#ifdef __cplusplus
}
#endif
#endif // _USB_HID_SWITCH_DRIVER_H_

View File

@ -15,10 +15,11 @@ extern "C" {
#endif
typedef enum {
// USB_MODE_SWITCH_TATACON,
// USB_MODE_SWITCH_HORIPAD,
// USB_MODE_DUALSHOCK3,
// USB_MODE_DUALSHOCK4,
USB_MODE_SWITCH_TATACON,
USB_MODE_SWITCH_HORIPAD,
USB_MODE_DUALSHOCK3,
USB_MODE_PS4_TATACON,
USB_MODE_DUALSHOCK4,
USB_MODE_XBOX360,
USB_MODE_DEBUG,
} usb_mode_t;
@ -41,8 +42,27 @@ typedef struct {
uint16_t size;
} usb_report_t;
typedef enum {
USB_PLAYER_LED_ID,
USB_PLAYER_LED_COLOR,
} usb_player_led_type_t;
typedef struct {
usb_player_led_type_t type;
union {
uint8_t id;
struct {
uint8_t red;
uint8_t green;
uint8_t blue;
};
};
} usb_player_led_t;
extern char *const usbd_desc_str[];
typedef void (*usbd_player_led_cb_t)(usb_player_led_t);
void usb_driver_init(usb_mode_t mode);
void usb_driver_task();
@ -50,6 +70,9 @@ usb_mode_t usb_driver_get_mode();
void usb_driver_send_and_receive_report(usb_report_t report);
void usb_driver_set_player_led_cb(usbd_player_led_cb_t cb);
usbd_player_led_cb_t usb_driver_get_player_led_cb();
#ifdef __cplusplus
}
#endif

View File

@ -1,6 +1,7 @@
#ifndef _UTILS_INPUTSTATE_H_
#define _UTILS_INPUTSTATE_H_
#include "usb/hid_driver.h"
#include "usb/usb_driver.h"
#include "usb/xinput_driver.h"
@ -40,9 +41,15 @@ struct InputState {
Controller controller;
private:
hid_switch_report_t m_switch_report;
hid_ps3_report_t m_ps3_report;
hid_ps4_report_t m_ps4_report;
xinput_report_t m_xinput_report;
std::string m_debug_report;
usb_report_t getSwitchReport();
usb_report_t getPS3InputReport();
usb_report_t getPS4InputReport();
usb_report_t getXinputReport();
usb_report_t getDebugReport();

View File

@ -176,6 +176,16 @@ void Display::showMenu() { m_state = State::Menu; }
static std::string modeToString(usb_mode_t mode) {
switch (mode) {
case USB_MODE_SWITCH_TATACON:
return "Switch Tatacon";
case USB_MODE_SWITCH_HORIPAD:
return "Switch Horipad";
case USB_MODE_DUALSHOCK3:
return "Dualshock 3";
case USB_MODE_PS4_TATACON:
return "PS4 Tatacon";
case USB_MODE_DUALSHOCK4:
return "Dualshock 4";
case USB_MODE_XBOX360:
return "Xbox 360";
case USB_MODE_DEBUG:

64
src/usb/hid_driver.c Normal file
View File

@ -0,0 +1,64 @@
#include "usb/hid_driver.h"
#include "usb/usb_driver.h"
#include "class/hid/hid_device.h"
#include "tusb.h"
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer,
uint16_t reqlen) {
switch (usb_driver_get_mode()) {
case USB_MODE_SWITCH_TATACON:
case USB_MODE_SWITCH_HORIPAD:
return hid_switch_get_report_cb(itf, report_id, report_type, buffer, reqlen);
case USB_MODE_DUALSHOCK3:
return hid_ps3_get_report_cb(itf, report_id, report_type, buffer, reqlen);
// case USB_MODE_PS4_TATACON:
case USB_MODE_DUALSHOCK4:
return hid_ps4_get_report_cb(itf, report_id, report_type, buffer, reqlen);
default:
}
return 0;
}
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer,
uint16_t bufsize) {
switch (usb_driver_get_mode()) {
case USB_MODE_SWITCH_TATACON:
case USB_MODE_SWITCH_HORIPAD:
hid_switch_set_report_cb(itf, report_id, report_type, buffer, bufsize);
break;
case USB_MODE_DUALSHOCK3:
hid_ps3_set_report_cb(itf, report_id, report_type, buffer, bufsize);
break;
// case USB_MODE_PS4_TATACON:
case USB_MODE_DUALSHOCK4:
hid_ps4_set_report_cb(itf, report_id, report_type, buffer, bufsize);
break;
default:
}
}
bool hid_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
// Magic byte sequence to enable PS button on PS3
static const uint8_t magic_init_bytes[8] = {0x21, 0x26, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00};
if (stage == CONTROL_STAGE_SETUP && request->bmRequestType == 0xA1 &&
request->bRequest == HID_REQ_CONTROL_GET_REPORT && request->wValue == 0x0300) {
return tud_hid_report(0, magic_init_bytes, sizeof(magic_init_bytes));
} else {
return hidd_control_xfer_cb(rhport, stage, request);
}
}
const usbd_class_driver_t hid_app_driver = {
#if CFG_TUSB_DEBUG >= 2
.name = "HID",
#endif
.init = hidd_init,
.reset = hidd_reset,
.open = hidd_open,
.control_xfer_cb = hid_control_xfer_cb,
.xfer_cb = hidd_xfer_cb,
.sof = NULL};

207
src/usb/hid_ps3_driver.c Normal file
View File

@ -0,0 +1,207 @@
#include "usb/hid_ps3_driver.h"
#include "usb/usb_driver.h"
#include "class/hid/hid_device.h"
#include "pico/unique_id.h"
#include "tusb.h"
const tusb_desc_device_t ds3_desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_UNSPECIFIED,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x054C, // Sny
.idProduct = 0x0268, // Dualshock3
.bcdDevice = 0x0100,
.iManufacturer = USBD_STR_MANUFACTURER,
.iProduct = USBD_STR_PRODUCT,
.iSerialNumber = USBD_STR_SERIAL,
.bNumConfigurations = 1,
};
enum {
USBD_ITF_HID,
USBD_ITF_MAX,
};
#define USBD_PS3_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
const uint8_t ps3_desc_cfg[] = {
TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_LANGUAGE, USBD_PS3_DESC_LEN, 0, USBD_MAX_POWER_MAX),
TUD_HID_INOUT_DESCRIPTOR(USBD_ITF_HID, USBD_STR_PS3, 0, 148, 0x02, 0x81, CFG_TUD_HID_EP_BUFSIZE, 1),
};
const uint8_t ps3_desc_hid_report[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Application)
0xA1, 0x02, // Collection (Logical)
0x85, 0x01, // Report ID (1)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01, // Report Size (1)
0x95, 0x13, // Report Count (19)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x01, // Physical Maximum (1)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x13, // Usage Maximum (0x13)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01, // Report Size (1)
0x95, 0x0D, // Report Count (13)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x75, 0x08, // Report Size (8)
0x95, 0x27, // Report Count (39)
0x09, 0x01, // Usage (Pointer)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xA1, 0x02, // Collection (Logical)
0x85, 0x02, // Report ID (2)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xA1, 0x02, // Collection (Logical)
0x85, 0xEE, // Report ID (-18)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xA1, 0x02, // Collection (Logical)
0x85, 0xEF, // Report ID (-17)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xC0, // End Collection
};
static hid_ps3_report_t last_report = {};
bool send_hid_ps3_report(usb_report_t report) {
bool result = false;
if (tud_hid_ready()) {
result = tud_hid_report(0, report.data, report.size);
}
memcpy(&last_report, report.data, tu_min16(report.size, sizeof(hid_ps3_report_t)));
return result;
}
static uint8_t ps3_report_0xf2[] = {
0xff, 0xff, 0x00, // Unknown
0x00, 0x07, 0x04, // MAC address OUI (ALPS Co.)
0x39, 0x39, 0x39, // MAC manufacturer specific
0x00, 0x03, 0x50, 0x81, 0xd8, 0x01, 0x8a // Unknown
};
static const uint8_t ps3_report_0xf5[] = {0x00, 0xf0, 0xf0, 0x02, 0x5e, 0x16, 0x26}; // Unknown
uint16_t hid_ps3_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer,
uint16_t reqlen) {
(void)itf;
(void)reqlen;
static bool do_init_mac = true;
if (report_type == HID_REPORT_TYPE_INPUT) {
memcpy(buffer, &last_report, sizeof(hid_ps3_report_t));
return sizeof(hid_ps3_report_t);
} else if (report_type == HID_REPORT_TYPE_FEATURE) {
switch (report_id) {
case 0xf2:
if (do_init_mac) {
pico_unique_board_id_t uid;
pico_get_unique_board_id(&uid);
// Genrate manufacturer specific using pico board id
for (uint8_t i = 0; i < PICO_UNIQUE_BOARD_ID_SIZE_BYTES; ++i) {
ps3_report_0xf2[6 + (i % 3)] ^= uid.id[i];
}
do_init_mac = false;
}
memcpy(buffer, ps3_report_0xf2, sizeof(ps3_report_0xf2));
return sizeof(ps3_report_0xf2);
case 0xf5:
memcpy(buffer, ps3_report_0xf5, sizeof(ps3_report_0xf5));
return sizeof(ps3_report_0xf5);
default:
}
}
return 0;
}
typedef struct __attribute((packed, aligned(1))) {
uint8_t rumble[4];
uint8_t padding[4];
uint8_t leds_bitmap;
uint8_t leds[5][5];
} hid_ps3_ouput_report_t;
void hid_ps3_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer,
uint16_t bufsize) {
(void)itf;
if (report_type == HID_REPORT_TYPE_INVALID) {
if (bufsize > 0) {
report_id = buffer[0];
buffer = &buffer[1];
bufsize--;
}
}
switch (report_id) {
case 0x01:
if (bufsize == sizeof(hid_ps3_ouput_report_t)) {
hid_ps3_ouput_report_t *report = (hid_ps3_ouput_report_t *)buffer;
usb_player_led_t player_led = {.type = USB_PLAYER_LED_ID, .id = 0};
player_led.id = 0 | ((report->leds_bitmap & 0x02) ? (1 << 0) : 0) //
| ((report->leds_bitmap & 0x04) ? (1 << 1) : 0) //
| ((report->leds_bitmap & 0x08) ? (1 << 2) : 0) //
| ((report->leds_bitmap & 0x10) ? (1 << 3) : 0);
usb_driver_get_player_led_cb()(player_led);
}
break;
default:
}
}

414
src/usb/hid_ps4_driver.c Normal file
View File

@ -0,0 +1,414 @@
#include "usb/hid_ps4_driver.h"
#include "usb/usb_driver.h"
#include "class/hid/hid_device.h"
#include "pico/unique_id.h"
#include "tusb.h"
const tusb_desc_device_t ps4_tatacon_desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_UNSPECIFIED,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x0F0D, // HORI
.idProduct = 0x00C9, // PS4-095 aka Taiko Drum
.bcdDevice = 0x0100,
.iManufacturer = USBD_STR_MANUFACTURER,
.iProduct = USBD_STR_PRODUCT,
.iSerialNumber = USBD_STR_SERIAL,
.bNumConfigurations = 1,
};
const tusb_desc_device_t ds4_desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_UNSPECIFIED,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x054C, // Sny
.idProduct = 0x05C4, // Dualshock 4
.bcdDevice = 0x0100,
.iManufacturer = USBD_STR_MANUFACTURER,
.iProduct = USBD_STR_PRODUCT,
.iSerialNumber = USBD_STR_SERIAL,
.bNumConfigurations = 1,
};
enum {
USBD_ITF_HID,
USBD_ITF_MAX,
};
#define USBD_PS4_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
const uint8_t ps4_desc_cfg[] = {
TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_LANGUAGE, USBD_PS4_DESC_LEN, 0, USBD_MAX_POWER_MAX),
TUD_HID_INOUT_DESCRIPTOR(USBD_ITF_HID, USBD_STR_PS4, 0, 507, 0x03, 0x84, CFG_TUD_HID_EP_BUFSIZE, 1),
};
const uint8_t ps4_desc_hid_report[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID (1)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x39, // Usage (Hat switch)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x07, // Logical Maximum (7)
0x35, 0x00, // Physical Minimum (0)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x65, 0x00, // Unit (None)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0E, // Usage Maximum (0x0E)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0E, // Report Count (14)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0x20, // Usage (0x20)
0x75, 0x06, // Report Size (6)
0x95, 0x01, // Report Count (1)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x7F, // Logical Maximum (127)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x33, // Usage (Rx)
0x09, 0x34, // Usage (Ry)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x02, // Report Count (2)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0x21, // Usage (0x21)
0x95, 0x36, // Report Count (54)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x85, 0x05, // Report ID (5)
0x09, 0x22, // Usage (0x22)
0x95, 0x1F, // Report Count (31)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x04, // Report ID (4)
0x09, 0x23, // Usage (0x23)
0x95, 0x24, // Report Count (36)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x02, // Report ID (2)
0x09, 0x24, // Usage (0x24)
0x95, 0x24, // Report Count (36)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x08, // Report ID (8)
0x09, 0x25, // Usage (0x25)
0x95, 0x03, // Report Count (3)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x10, // Report ID (16)
0x09, 0x26, // Usage (0x26)
0x95, 0x04, // Report Count (4)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x11, // Report ID (17)
0x09, 0x27, // Usage (0x27)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x12, // Report ID (18)
0x06, 0x02, 0xFF, // Usage Page (Vendor Defined 0xFF02)
0x09, 0x21, // Usage (0x21)
0x95, 0x0F, // Report Count (15)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x13, // Report ID (19)
0x09, 0x22, // Usage (0x22)
0x95, 0x16, // Report Count (22)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x14, // Report ID (20)
0x06, 0x05, 0xFF, // Usage Page (Vendor Defined 0xFF05)
0x09, 0x20, // Usage (0x20)
0x95, 0x10, // Report Count (16)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x15, // Report ID (21)
0x09, 0x21, // Usage (0x21)
0x95, 0x2C, // Report Count (44)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x06, 0x80, 0xFF, // Usage Page (Vendor Defined 0xFF80)
0x85, 0x80, // Report ID (-128)
0x09, 0x20, // Usage (0x20)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x81, // Report ID (-127)
0x09, 0x21, // Usage (0x21)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x82, // Report ID (-126)
0x09, 0x22, // Usage (0x22)
0x95, 0x05, // Report Count (5)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x83, // Report ID (-125)
0x09, 0x23, // Usage (0x23)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x84, // Report ID (-124)
0x09, 0x24, // Usage (0x24)
0x95, 0x04, // Report Count (4)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x85, // Report ID (-123)
0x09, 0x25, // Usage (0x25)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x86, // Report ID (-122)
0x09, 0x26, // Usage (0x26)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x87, // Report ID (-121)
0x09, 0x27, // Usage (0x27)
0x95, 0x23, // Report Count (35)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x88, // Report ID (-120)
0x09, 0x28, // Usage (0x28)
0x95, 0x22, // Report Count (34)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x89, // Report ID (-119)
0x09, 0x29, // Usage (0x29)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x90, // Report ID (-112)
0x09, 0x30, // Usage (0x30)
0x95, 0x05, // Report Count (5)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x91, // Report ID (-111)
0x09, 0x31, // Usage (0x31)
0x95, 0x03, // Report Count (3)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x92, // Report ID (-110)
0x09, 0x32, // Usage (0x32)
0x95, 0x03, // Report Count (3)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0x93, // Report ID (-109)
0x09, 0x33, // Usage (0x33)
0x95, 0x0C, // Report Count (12)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA0, // Report ID (-96)
0x09, 0x40, // Usage (0x40)
0x95, 0x06, // Report Count (6)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA1, // Report ID (-95)
0x09, 0x41, // Usage (0x41)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA2, // Report ID (-94)
0x09, 0x42, // Usage (0x42)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA3, // Report ID (-93)
0x09, 0x43, // Usage (0x43)
0x95, 0x30, // Report Count (48)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA4, // Report ID (-92)
0x09, 0x44, // Usage (0x44)
0x95, 0x0D, // Report Count (13)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA5, // Report ID (-91)
0x09, 0x45, // Usage (0x45)
0x95, 0x15, // Report Count (21)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA6, // Report ID (-90)
0x09, 0x46, // Usage (0x46)
0x95, 0x15, // Report Count (21)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF0, // Report ID (-16)
0x09, 0x47, // Usage (0x47)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF1, // Report ID (-15)
0x09, 0x48, // Usage (0x48)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xF2, // Report ID (-14)
0x09, 0x49, // Usage (0x49)
0x95, 0x0F, // Report Count (15)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA7, // Report ID (-89)
0x09, 0x4A, // Usage (0x4A)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA8, // Report ID (-88)
0x09, 0x4B, // Usage (0x4B)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xA9, // Report ID (-87)
0x09, 0x4C, // Usage (0x4C)
0x95, 0x08, // Report Count (8)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAA, // Report ID (-86)
0x09, 0x4E, // Usage (0x4E)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAB, // Report ID (-85)
0x09, 0x4F, // Usage (0x4F)
0x95, 0x39, // Report Count (57)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAC, // Report ID (-84)
0x09, 0x50, // Usage (0x50)
0x95, 0x39, // Report Count (57)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAD, // Report ID (-83)
0x09, 0x51, // Usage (0x51)
0x95, 0x0B, // Report Count (11)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAE, // Report ID (-82)
0x09, 0x52, // Usage (0x52)
0x95, 0x01, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xAF, // Report ID (-81)
0x09, 0x53, // Usage (0x53)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xB0, // Report ID (-80)
0x09, 0x54, // Usage (0x54)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xB1, // Report ID (-79)
0x09, 0x55, // Usage (0x55)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xB2, // Report ID (-78)
0x09, 0x56, // Usage (0x56)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xE0, // Report ID (-32)
0x09, 0x57, // Usage (0x57)
0x95, 0x02, // Report Count (2)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xB3, // Report ID (-77)
0x09, 0x55, // Usage (0x55)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x85, 0xB4, // Report ID (-76)
0x09, 0x55, // Usage (0x55)
0x95, 0x3F, // Report Count (63)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
};
// MAC Address
static uint8_t ps4_0x81_report[] = {0x39, 0x39, 0x39, 0x68, 0x22, 0x00};
// Version Info
static const uint8_t ps4_0xa3_report[] = {0x4a, 0x75, 0x6c, 0x20, 0x31, 0x31, 0x20, 0x32, 0x30, 0x31, 0x36, 0x00,
0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x3a, 0x33, 0x33, 0x3a, 0x33, 0x38,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x64,
0x01, 0x00, 0x00, 0x00, 0x09, 0x70, 0x00, 0x02, 0x00, 0x80, 0x03, 0x00};
// Calibration Data
static const uint8_t ps4_0x02_report[] = {0x04, 0x00, 0xf9, 0xff, 0x06, 0x00, 0x1d, 0x22, 0xec, 0xdd, 0x68, 0x22,
0x88, 0xdd, 0xa9, 0x23, 0x62, 0xdc, 0x1c, 0x02, 0x1c, 0x02, 0x05, 0x20,
0xfb, 0xdf, 0x49, 0x20, 0xb7, 0xdf, 0x0d, 0x20, 0xf4, 0xdf, 0x01, 0x00};
static hid_ps4_report_t last_report = {};
bool send_hid_ps4_report(usb_report_t report) {
bool result = false;
if (tud_hid_ready()) {
result = tud_hid_report(0, report.data, report.size);
}
memcpy(&last_report, report.data, tu_min16(report.size, sizeof(hid_ps4_report_t)));
return result;
}
uint16_t hid_ps4_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer,
uint16_t reqlen) {
(void)itf;
(void)reqlen;
static bool do_init_mac = true;
if (report_type == HID_REPORT_TYPE_INPUT) {
memcpy(&buffer, &last_report, sizeof(hid_ps4_report_t));
return sizeof(hid_ps4_report_t);
} else if (report_type == HID_REPORT_TYPE_FEATURE) {
switch (report_id) {
case 0x81:
if (do_init_mac) {
pico_unique_board_id_t uid;
pico_get_unique_board_id(&uid);
// Genrate manufacturer specific using pico board id
for (uint8_t i = 0; i < PICO_UNIQUE_BOARD_ID_SIZE_BYTES; ++i) {
ps4_0x81_report[(i % 3)] ^= uid.id[i];
}
do_init_mac = false;
}
memcpy(buffer, ps4_0x81_report, sizeof(ps4_0x81_report));
return sizeof(ps4_0x81_report);
case 0xa3:
memcpy(buffer, ps4_0xa3_report, sizeof(ps4_0xa3_report));
return sizeof(ps4_0xa3_report);
case 0x02:
memcpy(buffer, ps4_0x02_report, sizeof(ps4_0x02_report));
return sizeof(ps4_0x02_report);
default:
}
}
return 0;
}
typedef struct __attribute((packed, aligned(1))) {
uint8_t content_flags; // 0x01: Rumble, 0x02: Color, 0x04: Flash
uint8_t unknown1[2];
uint8_t rumble_weak;
uint8_t rumble_strong;
uint8_t led_red;
uint8_t led_green;
uint8_t led_blue;
uint8_t flash_bright_time;
uint8_t flash_dark_time;
uint8_t unknown2[21];
} hid_ps4_ouput_report_t;
void hid_ps4_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer,
uint16_t bufsize) {
(void)itf;
if (report_type == HID_REPORT_TYPE_INVALID) {
if (bufsize > 0) {
report_id = buffer[0];
buffer = &buffer[1];
bufsize--;
}
}
switch (report_id) {
case 0x05:
if (bufsize == sizeof(hid_ps4_ouput_report_t)) {
hid_ps4_ouput_report_t *report = (hid_ps4_ouput_report_t *)buffer;
if (report->content_flags & 0x02) {
usb_player_led_t player_led = {.type = USB_PLAYER_LED_COLOR,
.red = report->led_red,
.green = report->led_green,
.blue = report->led_blue};
usb_driver_get_player_led_cb()(player_led);
}
}
break;
default:
}
}

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

@ -0,0 +1,130 @@
#include "usb/hid_switch_driver.h"
#include "usb/usb_driver.h"
#include "class/hid/hid_device.h"
#include "tusb.h"
const tusb_desc_device_t switch_tatacon_desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_UNSPECIFIED,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x0F0D, // HORI
.idProduct = 0x00F0, // NSW-079 aka Taiko Drum
.bcdDevice = 0x0100,
.iManufacturer = USBD_STR_MANUFACTURER,
.iProduct = USBD_STR_PRODUCT,
.iSerialNumber = USBD_STR_SERIAL,
.bNumConfigurations = 1,
};
const tusb_desc_device_t switch_horipad_desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_UNSPECIFIED,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x0F0D, // HORI
.idProduct = 0x00C1, // HORIPAD
.bcdDevice = 0x0100,
.iManufacturer = USBD_STR_MANUFACTURER,
.iProduct = USBD_STR_PRODUCT,
.iSerialNumber = USBD_STR_SERIAL,
.bNumConfigurations = 1,
};
enum {
USBD_ITF_HID,
USBD_ITF_MAX,
};
#define USBD_SWITCH_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
const uint8_t switch_desc_cfg[] = {
TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_LANGUAGE, USBD_SWITCH_DESC_LEN, 0, USBD_MAX_POWER_MAX),
TUD_HID_INOUT_DESCRIPTOR(USBD_ITF_HID, USBD_STR_SWITCH, 0, 86, 0x02, 0x81, CFG_TUD_HID_EP_BUFSIZE, 1),
};
const uint8_t switch_desc_hid_report[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x01, // Physical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x10, // Report Count (16)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x10, // Usage Maximum (0x10)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x25, 0x07, // Logical Maximum (7)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x65, 0x00, // Unit (None)
0x95, 0x01, // Report Count (1)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0x20, // Usage (0x20)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x0A, 0x21, 0x26, // Usage (0x2621)
0x95, 0x08, // Report Count (8)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
};
static hid_switch_report_t last_report = {};
bool send_hid_switch_report(usb_report_t report) {
bool result = false;
if (tud_hid_ready()) {
result = tud_hid_report(0, report.data, report.size);
}
memcpy(&last_report, report.data, tu_min16(report.size, sizeof(hid_switch_report_t)));
return result;
}
uint16_t hid_switch_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer,
uint16_t reqlen) {
(void)itf;
(void)report_id;
(void)reqlen;
if (report_type == HID_REPORT_TYPE_INPUT) {
memcpy(buffer, &last_report, sizeof(hid_switch_report_t));
return sizeof(hid_switch_report_t);
}
return 0;
}
void hid_switch_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer,
uint16_t bufsize) {
(void)itf;
(void)report_id;
(void)report_type;
(void)bufsize;
(void)buffer;
}

View File

@ -1,12 +1,13 @@
#include "usb/usb_driver.h"
#include "usb/debug_driver.h"
// #include "usb/hid_driver.h"
#include "usb/hid_driver.h"
#include "usb/xinput_driver.h"
#include "bsp/board.h"
#include "pico/unique_id.h"
static usb_mode_t usbd_mode = USB_MODE_DEBUG;
static usbd_player_led_cb_t usbd_player_led_cb = NULL;
static const tusb_desc_device_t *usbd_desc_device = NULL;
static const uint8_t *usbd_desc_cfg = NULL;
static const uint8_t *usbd_desc_hid_report = NULL;
@ -21,9 +22,9 @@ char *const usbd_desc_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_SWITCH] = USBD_SWITCH_NAME, //
[USBD_STR_PS3] = USBD_PS3_NAME, //
[USBD_STR_PS4] = USBD_PS4_NAME, //
[USBD_STR_XINPUT] = USBD_XINPUT_NAME, //
[USBD_STR_CDC] = USBD_DEBUG_CDC_NAME, //
[USBD_STR_RPI_RESET] = USBD_DEBUG_RESET_NAME, //
@ -33,38 +34,46 @@ void usb_driver_init(usb_mode_t mode) {
usbd_mode = mode;
switch (mode) {
// case USB_MODE_SWITCH_TATACON:
// usbd_desc_device = &switch_tatacon_desc_device;
// usbd_desc_cfg = switch_desc_cfg;
// usbd_desc_hid_report = switch_desc_hid_report;
// usbd_app_driver = &hid_app_driver;
// usbd_send_report = send_hid_switch_report;
// usbd_receive_report = NULL;
// break;
// case USB_MODE_SWITCH_HORIPAD:
// usbd_desc_device = &switch_horipad_desc_device;
// usbd_desc_cfg = switch_desc_cfg;
// usbd_desc_hid_report = switch_desc_hid_report;
// usbd_app_driver = &hid_app_driver;
// usbd_send_report = send_hid_switch_report;
// usbd_receive_report = NULL;
// break;
// case USB_MODE_DUALSHOCK3:
// usbd_desc_device = &ds3_desc_device;
// usbd_desc_cfg = ps3_desc_cfg;
// usbd_desc_hid_report = ps3_desc_hid_report;
// usbd_app_driver = &hid_app_driver;
// usbd_send_report = send_hid_ps3_report;
// usbd_receive_report = NULL;
// break;
// case USB_MODE_DUALSHOCK4:
// usbd_desc_device = &ds4_desc_device;
// usbd_desc_cfg = ps4_desc_cfg;
// usbd_desc_hid_report = ps4_desc_hid_report;
// usbd_app_driver = &hid_app_driver;
// usbd_send_report = send_hid_ps4_report;
// usbd_receive_report = NULL;
// break;
case USB_MODE_SWITCH_TATACON:
usbd_desc_device = &switch_tatacon_desc_device;
usbd_desc_cfg = switch_desc_cfg;
usbd_desc_hid_report = switch_desc_hid_report;
usbd_app_driver = &hid_app_driver;
usbd_send_report = send_hid_switch_report;
usbd_receive_report = NULL;
break;
case USB_MODE_SWITCH_HORIPAD:
usbd_desc_device = &switch_horipad_desc_device;
usbd_desc_cfg = switch_desc_cfg;
usbd_desc_hid_report = switch_desc_hid_report;
usbd_app_driver = &hid_app_driver;
usbd_send_report = send_hid_switch_report;
usbd_receive_report = NULL;
break;
case USB_MODE_DUALSHOCK3:
usbd_desc_device = &ds3_desc_device;
usbd_desc_cfg = ps3_desc_cfg;
usbd_desc_hid_report = ps3_desc_hid_report;
usbd_app_driver = &hid_app_driver;
usbd_send_report = send_hid_ps3_report;
usbd_receive_report = NULL;
break;
case USB_MODE_PS4_TATACON:
usbd_desc_device = &ps4_tatacon_desc_device;
usbd_desc_cfg = ps4_desc_cfg;
usbd_desc_hid_report = ps4_desc_hid_report;
usbd_app_driver = &hid_app_driver;
usbd_send_report = send_hid_ps4_report;
usbd_receive_report = NULL;
break;
case USB_MODE_DUALSHOCK4:
usbd_desc_device = &ds4_desc_device;
usbd_desc_cfg = ps4_desc_cfg;
usbd_desc_hid_report = ps4_desc_hid_report;
usbd_app_driver = &hid_app_driver;
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;
@ -110,6 +119,10 @@ void usb_driver_send_and_receive_report(usb_report_t report) {
}
}
void usb_driver_set_player_led_cb(usbd_player_led_cb_t cb) { usbd_player_led_cb = cb; };
usbd_player_led_cb_t usb_driver_get_player_led_cb() { return usbd_player_led_cb; };
const uint8_t *tud_descriptor_device_cb(void) { return (const uint8_t *)usbd_desc_device; }
const uint8_t *tud_descriptor_configuration_cb(uint8_t __unused index) { return usbd_desc_cfg; }

View File

@ -111,10 +111,34 @@ static bool xinput_control_xfer_callback(uint8_t rhport, uint8_t stage, tusb_con
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;
if (result == XFER_RESULT_SUCCESS && ep_addr == endpoint_out) {
if (xinput_out_buffer[0] == 0x01) { // 0x00 is rumble, 0x01 is led
usb_player_led_t player_led = {.type = USB_PLAYER_LED_ID, .id = 0};
switch (xinput_out_buffer[2]) {
case 0x02:
case 0x06:
player_led.id = 0x01;
break;
case 0x03:
case 0x07:
player_led.id = 0x02;
break;
case 0x04:
case 0x08:
player_led.id = 0x04;
break;
case 0x05:
case 0x09:
player_led.id = 0x08;
break;
default:
}
usb_driver_get_player_led_cb()(player_led);
}
}
return true;
}

View File

@ -9,18 +9,19 @@ InputState::InputState()
: drum({{false, 0}, {false, 0}, {false, 0}, {false, 0}}),
controller(
{{false, false, false, false}, {false, false, false, false, false, false, false, false, false, false}}),
m_switch_report({}), m_ps3_report({}), m_ps4_report({}),
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) {
// case USB_MODE_SWITCH_TATACON:
// case USB_MODE_SWITCH_HORIPAD:
// return getSwitchReport();
// case USB_MODE_DUALSHOCK3:
// return getPS3InputReport();
// case USB_MODE_PS4_DIVACON:
// case USB_MODE_DUALSHOCK4:
// return getPS4InputReport();
case USB_MODE_SWITCH_TATACON:
case USB_MODE_SWITCH_HORIPAD:
return getSwitchReport();
case USB_MODE_DUALSHOCK3:
return getPS3InputReport();
case USB_MODE_PS4_TATACON:
case USB_MODE_DUALSHOCK4:
return getPS4InputReport();
case USB_MODE_XBOX360:
return getXinputReport();
case USB_MODE_DEBUG:
@ -29,6 +30,180 @@ usb_report_t InputState::getReport(usb_mode_t mode) {
}
}
static uint8_t getHidHat(const InputState::Controller::DPad dpad) {
if (dpad.up && dpad.right) {
return 0x01;
} else if (dpad.down && dpad.right) {
return 0x03;
} else if (dpad.down && dpad.left) {
return 0x05;
} else if (dpad.up && dpad.left) {
return 0x07;
} else if (dpad.up) {
return 0x00;
} else if (dpad.right) {
return 0x02;
} else if (dpad.down) {
return 0x04;
} else if (dpad.left) {
return 0x06;
}
return 0x08;
}
usb_report_t InputState::getSwitchReport() {
m_switch_report.buttons = 0 //
| (controller.buttons.west ? (1 << 0) : 0) // Y
| (controller.buttons.south ? (1 << 1) : 0) // B
| (controller.buttons.east ? (1 << 2) : 0) // A
| (controller.buttons.north ? (1 << 3) : 0) // X
| (controller.buttons.l ? (1 << 4) : 0) // L
| (controller.buttons.r ? (1 << 5) : 0) // R
| (drum.ka_left.triggered ? (1 << 6) : 0) // ZL
| (drum.ka_right.triggered ? (1 << 7) : 0) // ZR
| (controller.buttons.select ? (1 << 8) : 0) // -
| (controller.buttons.start ? (1 << 9) : 0) // +
| (drum.don_left.triggered ? (1 << 10) : 0) // LS
| (drum.don_right.triggered ? (1 << 11) : 0) // RS
| (controller.buttons.home ? (1 << 12) : 0) // Home
| (controller.buttons.share ? (1 << 13) : 0); // Capture
m_switch_report.hat = getHidHat(controller.dpad);
// Center all sticks
m_switch_report.lx = 0x80;
m_switch_report.ly = 0x80;
m_switch_report.rx = 0x80;
m_switch_report.ry = 0x80;
return {(uint8_t *)&m_switch_report, sizeof(hid_switch_report_t)};
}
usb_report_t InputState::getPS3InputReport() {
memset(&m_ps3_report, 0, sizeof(m_ps3_report));
m_ps3_report.report_id = 0x01;
m_ps3_report.buttons1 = 0 //
| (controller.buttons.select ? (1 << 0) : 0) // Select
| (drum.don_left.triggered ? (1 << 1) : 0) // L3
| (drum.don_right.triggered ? (1 << 2) : 0) // R3
| (controller.buttons.start ? (1 << 3) : 0) // Start
| (controller.dpad.up ? (1 << 4) : 0) // Up
| (controller.dpad.right ? (1 << 5) : 0) // Right
| (controller.dpad.down ? (1 << 6) : 0) // Down
| (controller.dpad.left ? (1 << 7) : 0); // Left
m_ps3_report.buttons2 = 0 | (drum.ka_left.triggered ? (1 << 0) : 0) // L2
| (drum.ka_right.triggered ? (1 << 1) : 0) // R2
| (controller.buttons.l ? (1 << 2) : 0) // L1
| (controller.buttons.r ? (1 << 3) : 0) // R1
| (controller.buttons.north ? (1 << 4) : 0) // Triangle
| (controller.buttons.east ? (1 << 5) : 0) // Circle
| (controller.buttons.south ? (1 << 6) : 0) // Cross
| (controller.buttons.west ? (1 << 7) : 0); // Square
m_ps3_report.buttons3 = 0 | (controller.buttons.home ? (1 << 0) : 0); // Home
// Center all sticks
m_ps3_report.lx = 0x80;
m_ps3_report.ly = 0x80;
m_ps3_report.rx = 0x80;
m_ps3_report.ry = 0x80;
m_ps3_report.lt = (drum.ka_left.triggered ? 0xff : 0);
m_ps3_report.rt = (drum.ka_right.triggered ? 0xff : 0);
m_ps3_report.unknown_0x02_1 = 0x02;
m_ps3_report.battery = 0xef;
m_ps3_report.unknown_0x12 = 0x12;
m_ps3_report.unknown[0] = 0x12;
m_ps3_report.unknown[1] = 0xf8;
m_ps3_report.unknown[2] = 0x77;
m_ps3_report.unknown[3] = 0x00;
m_ps3_report.unknown[4] = 0x40;
m_ps3_report.acc_x = 511;
m_ps3_report.acc_y = 511;
m_ps3_report.acc_z = 511;
m_ps3_report.unknown_0x02_2 = 0x02;
return {(uint8_t *)&m_ps3_report, sizeof(hid_ps3_report_t)};
}
usb_report_t InputState::getPS4InputReport() {
static uint8_t report_counter = 0;
static uint8_t last_timestamp = 0;
memset(&m_ps4_report, 0, sizeof(m_ps4_report));
m_ps4_report.report_id = 0x01;
// Center all sticks
m_ps4_report.lx = 0x80;
m_ps4_report.ly = 0x80;
m_ps4_report.rx = 0x80;
m_ps4_report.ry = 0x80;
m_ps4_report.buttons1 = getHidHat(controller.dpad) //
| (controller.buttons.west ? (1 << 4) : 0) // Square
| (controller.buttons.south ? (1 << 5) : 0) // Cross
| (controller.buttons.east ? (1 << 6) : 0) // Circle
| (controller.buttons.north ? (1 << 7) : 0); // Triangle
m_ps4_report.buttons2 = 0 //
| (controller.buttons.l ? (1 << 0) : 0) // L1
| (controller.buttons.r ? (1 << 1) : 0) // R1
| (drum.ka_left.triggered ? (1 << 2) : 0) // L2
| (drum.ka_right.triggered ? (1 << 3) : 0) // R2
| (controller.buttons.share ? (1 << 4) : 0) // Share
| (controller.buttons.start ? (1 << 5) : 0) // Option
| (drum.don_left.triggered ? (1 << 6) : 0) // L3
| (drum.don_right.triggered ? (1 << 7) : 0); // R3
m_ps4_report.buttons3 = (report_counter << 2) //
| (controller.buttons.home ? (1 << 0) : 0) // PS
| (controller.buttons.select ? (1 << 1) : 0); // T-Pad
m_ps4_report.lt = (drum.ka_left.triggered ? 0xFF : 0);
m_ps4_report.rt = (drum.ka_right.triggered ? 0xFF : 0);
// Used for gyro/accel, so we don't need to be precise here.
m_ps4_report.timestamp = last_timestamp;
m_ps4_report.battery = 0 | (1 << 4) | 11;
m_ps4_report.gyrox = 0;
m_ps4_report.gyroy = 0;
m_ps4_report.gyroz = 0;
m_ps4_report.accelx = 0;
m_ps4_report.accely = 0;
m_ps4_report.accelz = 0;
m_ps4_report.extension = 0x01;
m_ps4_report.touchpad_event_active = 0;
m_ps4_report.touchpad_counter = 0;
m_ps4_report.touchpad1_touches = (1 << 7);
m_ps4_report.touchpad2_touches = (1 << 7);
m_ps4_report.unknown3[1] = 0x80;
m_ps4_report.unknown3[5] = 0x80;
m_ps4_report.unknown3[10] = 0x80;
m_ps4_report.unknown3[14] = 0x80;
m_ps4_report.unknown3[19] = 0x80;
// This method actually gets called more often than the report is sent,
// so counters are not consecutive ... let's see if this turns out to
// be a problem.
last_timestamp += 188;
report_counter++;
if (report_counter > (UINT8_MAX >> 2)) {
report_counter = 0;
}
return {(uint8_t *)&m_ps4_report, sizeof(hid_ps4_report_t)};
}
usb_report_t InputState::getXinputReport() {
m_xinput_report.buttons1 = 0 //
| (controller.dpad.up ? (1 << 0) : 0) // Dpad Up