Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88a84fe6f2 | ||
|
|
91906866a8 | ||
|
|
22cabd06dc | ||
|
|
bf52296e60 | ||
|
|
9bdc0dbb33 |
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,28 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**What type of controller do you have the issue with?**
|
||||
Please include the model, even better to include the PID/VID.
|
||||
You can get the PID and VID like this:
|
||||
- plug the controller into your computer
|
||||
- open Device Manager
|
||||
- right click the controller (probably listed under Human Interface Devices)
|
||||
- select Properties
|
||||
- go to the Details tab
|
||||
- select Hardware IDs from the dropdown menu
|
||||
It will look like this: HID\VID_046D&PID_C05A
|
||||
|
||||
**What platform are you using the OGX-Mini on?**
|
||||
OG Xbox, PS3, Switch, etc.
|
||||
|
||||
**What board are you using?**
|
||||
Pi Pico, Adafruit Feather, RP2040-Zero...
|
||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,17 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -4,9 +4,3 @@
|
||||
[submodule "lib/tinyusb"]
|
||||
path = lib/tinyusb
|
||||
url = https://github.com/hathach/tinyusb.git
|
||||
[submodule "src/usbd"]
|
||||
path = src/usbd
|
||||
url = https://github.com/wiredopposite/usbd.git
|
||||
[submodule "lib/tusb_xinput"]
|
||||
path = lib/tusb_xinput
|
||||
url = https://github.com/Ryzee119/tusb_xinput.git
|
||||
|
||||
@@ -24,8 +24,6 @@ endif()
|
||||
|
||||
set(PICO_PIO_USB_PATH "${CMAKE_CURRENT_LIST_DIR}/lib/Pico-PIO-USB")
|
||||
set(PICO_TINYUSB_PATH "${CMAKE_CURRENT_LIST_DIR}/lib/tinyusb")
|
||||
set(TUSB_XINPUT_PATH "${CMAKE_CURRENT_LIST_DIR}/lib/tusb_xinput")
|
||||
add_subdirectory(${TUSB_XINPUT_PATH} xinput_host)
|
||||
|
||||
pico_sdk_init()
|
||||
|
||||
@@ -33,44 +31,17 @@ add_subdirectory(lib/Pico-PIO-USB)
|
||||
add_subdirectory(lib)
|
||||
|
||||
file(GLOB_RECURSE SOURCES
|
||||
"src/main.cpp"
|
||||
"src/input_mode.cpp"
|
||||
"src/Gamepad.cpp"
|
||||
|
||||
"src/utilities/log.cpp"
|
||||
|
||||
"src/usbh/tusb_host_manager.cpp"
|
||||
"src/usbh/tusb_host.cpp"
|
||||
"src/usbh/n64usb/N64USB.cpp"
|
||||
"src/usbh/ps3/Dualshock3.cpp"
|
||||
"src/usbh/ps3/DInput.cpp"
|
||||
"src/usbh/ps4/Dualshock4.cpp"
|
||||
"src/usbh/ps5/Dualsense.cpp"
|
||||
"src/usbh/psclassic/PSClassic.cpp"
|
||||
"src/usbh/switch/SwitchPro.cpp"
|
||||
"src/usbh/switch/SwitchWired.cpp"
|
||||
"src/usbh/xinput/XInput.cpp"
|
||||
"src/usbh/shared/hid_class_driver.c"
|
||||
"src/usbh/shared/scaling.cpp"
|
||||
|
||||
"src/usbd/usbdriver.cpp"
|
||||
"src/usbd/drivermanager.cpp"
|
||||
"src/usbd/drivers/shared/driverhelper.h"
|
||||
"src/usbd/drivers/shared/scaling.cpp"
|
||||
"src/usbd/drivers/dinput/DInputDriver.cpp"
|
||||
# "src/usbd/drivers/hid/HIDDriver.cpp"
|
||||
# "src/usbd/drivers/ps3/PS3Driver.cpp"
|
||||
"src/usbd/drivers/psclassic/PSClassicDriver.cpp"
|
||||
"src/usbd/drivers/switch/SwitchDriver.cpp"
|
||||
"src/usbd/drivers/usbserial/USBSerialDriver.cpp"
|
||||
"src/usbd/drivers/xinput/XInputDriver.cpp"
|
||||
"src/usbd/drivers/xboxog/XboxOriginalDriver.cpp"
|
||||
"src/usbd/drivers/xboxog/xid/xid.c"
|
||||
"src/usbd/drivers/xboxog/xid/xid_driver.c"
|
||||
"src/usbd/drivers/xboxog/xid/xid_gamepad.c"
|
||||
"src/usbd/drivers/xboxog/xid/xid_remote.c"
|
||||
"src/usbd/drivers/xboxog/xid/xid_steelbattalion.c"
|
||||
)
|
||||
"src/usbh/*"
|
||||
"src/usbh/tusb_xinput/*"
|
||||
"src/usbh/tusb_hid/*"
|
||||
"src/*"
|
||||
"src/utilities/*"
|
||||
"src/usbd/*"
|
||||
"src/usbd/shared/*"
|
||||
"src/usbd/switch/*"
|
||||
"src/usbd/xboxog/*"
|
||||
"src/usbd/dinput/*"
|
||||
"src/usbd/xinput/*")
|
||||
|
||||
# Firmware
|
||||
add_executable(${NAME}
|
||||
@@ -83,6 +54,16 @@ target_include_directories(${NAME} PUBLIC
|
||||
include_directories(
|
||||
lib/)
|
||||
|
||||
#------- Comment out the following line if compiling for the normal Pi Pico -------#
|
||||
|
||||
add_compile_definitions(FEATHER_RP2040)
|
||||
|
||||
#------- USB host data +/- will be GPIO 0/1 ---------------------------------------#
|
||||
|
||||
#------- CDC MODE --------#
|
||||
# add_compile_definitions(HOST_DEBUG) # makes RP2040 a CDC device, include "utilities/log.h" and use log() as you would printf()
|
||||
|
||||
|
||||
target_link_libraries(${NAME}
|
||||
pico_stdlib
|
||||
pico_unique_id
|
||||
@@ -100,8 +81,8 @@ target_link_libraries(${NAME}
|
||||
tinyusb_host
|
||||
tinyusb_pico_pio_usb
|
||||
CRC32
|
||||
hid_parser
|
||||
cmsis_core
|
||||
xinput_host
|
||||
)
|
||||
|
||||
pico_add_extra_outputs(${NAME})
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 wiredOpposite (wiredopposite.com)
|
||||
Copyright (c) 2024 wiredopposite
|
||||
Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
|
||||
Copyright (c) 2020 Ryan Wendland
|
||||
|
||||
32
README.md
32
README.md
@@ -1,9 +1,7 @@
|
||||
# OGX-Mini
|
||||

|
||||

|
||||
|
||||
Firmware for the RP2040, capable of emulating gamepads for several consoles. The firmware now comes in 3 flavors, for the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723), the Pi Pico, and the Waveshare RP2040-Zero.
|
||||
|
||||
## Supported platforms
|
||||
Firmware for the RP2040, setup for the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723) (can be used with the Pi Pico as well), capable of emulating gamepads for
|
||||
- Original Xbox
|
||||
- Playstation 3
|
||||
- Nintendo Switch (docked)
|
||||
@@ -14,18 +12,16 @@ Firmware for the RP2040, capable of emulating gamepads for several consoles. The
|
||||
### Wired controllers
|
||||
- Original Xbox Duke and S
|
||||
- Xbox 360, One, Series, and Elite
|
||||
- Dualshock 3 (PS3)
|
||||
- Dualshock 4 (PS4)
|
||||
- Dualsense (PS5, Dualsense Edge should work but it's untested)
|
||||
- Nintendo Switch Pro
|
||||
- Nintendo Switch wired (tested with PowerA brand)
|
||||
- Nintendo 64 USB (experimental, tested with RetroLink brand)
|
||||
- Playstation Classic
|
||||
- Generic DInput
|
||||
|
||||
### Wireless adapters
|
||||
- Xbox 360 PC adapter (Microsoft or clones, syncs 1 controller)
|
||||
- 8Bitdo v1 and v2 Bluetooth adapters (set to XInput mode)
|
||||
- 8Bitdo v1 and v2 Bluetooth adapters
|
||||
- Most wireless adapters that present themselves as Switch/XInput/PlayStation controllers should work
|
||||
|
||||
Note: There are some third party controllers that can change their VID/PID, these might not work correctly.
|
||||
@@ -48,28 +44,16 @@ Start + A (Cross for PlayStation and B for Switch gamepads)
|
||||
|
||||
After a new mode is stored, the RP2040 will reset itself so you don't need to unplug it.
|
||||
|
||||
## Hardware
|
||||
I've designed a PCB for the RP2040-Zero so you can make a small form-factor adapter yourself. The gerber files, schematic, and BOM are in Hardware folder.
|
||||
|
||||

|
||||
|
||||
If you would like a prebuilt unit, you can purchase one, with cable and Xbox adapter included, from my website [wiredopposite.com](https://wiredopposite.com/product/ogx-mini-controller-adapter-for-original-xbox-playstation-3-and-switch-ogx360/) or my [Etsy store](https://www.etsy.com/listing/1426992904/ogx-mini-controller-adapter-for-original).
|
||||
|
||||
For the Pi Pico, this is a diagram of how you'd connect the extra USB port:
|
||||
|
||||

|
||||
|
||||
For the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723), no extra work is needed.
|
||||
|
||||
## Adding supported controllers
|
||||
If your third party controller isn't working, but the original version is listed above, send me the device's VID and PID and I'll add it so it's recognized properly.
|
||||
|
||||
## Compiling
|
||||
You can compile this for different boards by changing USBD_BOARD in the usbd_config.h file, you can also adjust USBD_MAX_GAMEPADS to enable more controllers on PlayStation 3 (this is experimental).
|
||||
You can compile this for the Pi Pico by commenting out this line in CMakeLists.txt
|
||||
`add_compile_definitions(FEATHER_RP2040)`
|
||||
That will set the D+ and D- host pins to GPIO 0 and 1.
|
||||
|
||||
Choosing OGXM_PI_PICO will set the D+ and D- host pins to GPIO 0 and 1.
|
||||
|
||||
You can also choose OGXM_RPZERO_INTERPOSER for the RP2040-Zero and that will set D+ and D- to GPIO 10 and 11, so connecting a USB port is easier. You can still use the Pi Pico firmware on the RP2040-Zero (the other way around has not been tested though).
|
||||
Here's a diagram of how you'd use the Pico:
|
||||

|
||||
|
||||
## Special thanks
|
||||
Thank you to Ryzee119 and the OpenStickCommunity, without their work this project would not exist.
|
||||
@@ -1,6 +0,0 @@
|
||||
References,Qty,Description,Manufacturer,MPN,Digikey,Mouser,RS,Newark,Farnell,LCSC,JLC Assembly
|
||||
USB1,1,USB TYPE-A SINGLE PORT RIGHT ANG,,CU01SAH0S00,2987-CU01SAH0S00-ND,,,,,,
|
||||
"R1,R2",2,RES 27 OHM 5% 1/8W 0805,,RC0805JR-0727RL,311-27ARCT-ND,,,,,,
|
||||
R3,1,RES 470 OHM 1% 1/8W 0805 (Optional),,RC0805FR-07470RL,311-470CRCT-ND,,,,,,
|
||||
LED1,1,LED GREEN CLEAR 0805 SMD (Optional),,150080GS75000,732-4983-1-ND,,,,,,
|
||||
U1,1,Waveshare RP2040-Zero,,Waveshare RP2040-Zero,,,,,,,
|
||||
|
Binary file not shown.
@@ -1,5 +0,0 @@
|
||||
Gerber, BOM, and schematic for an RP2040-Zero interposer board you can make yourself. LED1 and R3 are both optional.
|
||||
|
||||
The RP2040-Zero board can be found on Amazon and AliExpress.
|
||||
|
||||

|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 507 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 594 KiB |
BIN
images/ada_feather_rp2040_usb.jpg
Normal file
BIN
images/ada_feather_rp2040_usb.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
Submodule lib/tinyusb updated: 290f4bea91...938cae818f
Submodule lib/tusb_xinput deleted from 073d73c47a
@@ -1,39 +1,33 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "Gamepad.h"
|
||||
#include "usbd/board_config.h"
|
||||
#include "usbh/tusb_hid/shared.h"
|
||||
|
||||
void Gamepad::reset_pad()
|
||||
void Gamepad::reset_state()
|
||||
{
|
||||
buttons.up = buttons.down = buttons.left = buttons.right = false;
|
||||
buttons.a = buttons.b = buttons.x = buttons.y = false;
|
||||
buttons.l3 = buttons.r3 = buttons.back = buttons.start = false;
|
||||
buttons.rb = buttons.lb = buttons.sys = buttons.misc = false;
|
||||
|
||||
triggers.l = triggers.r = 0;
|
||||
|
||||
joysticks.lx = joysticks.ly = joysticks.rx = joysticks.ry = 0;
|
||||
state.up = state.down = state.left = state.right = false;
|
||||
state.a = state.b = state.x = state.y = false;
|
||||
state.l3 = state.r3 = state.back = state.start = false;
|
||||
state.rb = state.lb = state.sys = state.misc = false;
|
||||
state.lt = state.rt = 0;
|
||||
state.lx = state.ly = state.rx = state.ry = 0;
|
||||
}
|
||||
|
||||
void Gamepad::reset_rumble()
|
||||
void GamepadOut::update_gamepad_rumble(uint8_t left_rumble, uint8_t right_rumble)
|
||||
{
|
||||
rumble.r = rumble.l = 0;
|
||||
out_state.lrumble = left_rumble;
|
||||
out_state.rrumble = right_rumble;
|
||||
}
|
||||
|
||||
void Gamepad::reset_hid_rumble()
|
||||
void GamepadOut::rumble_hid_reset()
|
||||
{
|
||||
if (rumble.l != UINT8_MAX)
|
||||
if (out_state.lrumble != UINT8_MAX)
|
||||
{
|
||||
rumble.l = 0;
|
||||
out_state.lrumble = 0;
|
||||
}
|
||||
|
||||
if (rumble.r != UINT8_MAX)
|
||||
if (out_state.rrumble != UINT8_MAX)
|
||||
{
|
||||
rumble.r = 0;
|
||||
gamepadOut.out_state.rrumble = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Gamepad& gamepad(int idx)
|
||||
{
|
||||
static Gamepad gamepad[MAX_GAMEPADS];
|
||||
|
||||
return gamepad[idx];
|
||||
}
|
||||
@@ -4,13 +4,17 @@
|
||||
#define _GAMEPAD_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
struct GamepadButtons
|
||||
{
|
||||
// #include "usbh/tusb_hid/ps3.h"
|
||||
// #include "usbh/tusb_hid/ps4.h"
|
||||
// #include "usbh/tusb_hid/ps5.h"
|
||||
#include "usbh/tusb_xinput/xinput_host.h"
|
||||
|
||||
struct GamepadState{
|
||||
bool up {false};
|
||||
bool down {false};
|
||||
bool left {false};
|
||||
@@ -27,45 +31,40 @@ struct GamepadButtons
|
||||
bool lb {false};
|
||||
bool sys {false};
|
||||
bool misc {false};
|
||||
};
|
||||
|
||||
struct GamepadTriggers
|
||||
{
|
||||
uint8_t l {0};
|
||||
uint8_t r {0};
|
||||
};
|
||||
|
||||
struct GamepadJoysticks
|
||||
{
|
||||
uint8_t lt {0};
|
||||
uint8_t rt {0};
|
||||
int16_t lx {0};
|
||||
int16_t ly {0};
|
||||
int16_t rx {0};
|
||||
int16_t ry {0};
|
||||
};
|
||||
|
||||
struct GamepadRumble
|
||||
{
|
||||
uint8_t l {0};
|
||||
uint8_t r {0};
|
||||
struct GamepadOutState{
|
||||
uint8_t lrumble {0};
|
||||
uint8_t rrumble {0};
|
||||
};
|
||||
|
||||
class Gamepad
|
||||
{
|
||||
public:
|
||||
GamepadButtons buttons;
|
||||
GamepadTriggers triggers;
|
||||
GamepadJoysticks joysticks;
|
||||
GamepadRumble rumble;
|
||||
class Gamepad {
|
||||
public:
|
||||
GamepadState state;
|
||||
|
||||
void reset_pad();
|
||||
void reset_rumble();
|
||||
void reset_hid_rumble();
|
||||
void update_gamepad_state_from_xinput(const xinput_gamepad_t* xinput_data);
|
||||
void reset_state();
|
||||
};
|
||||
|
||||
Gamepad& gamepad(int idx);
|
||||
class GamepadOut {
|
||||
public:
|
||||
GamepadOutState out_state;
|
||||
|
||||
void update_gamepad_rumble(uint8_t left_rumble, uint8_t right_rumble);
|
||||
void rumble_hid_reset();
|
||||
};
|
||||
|
||||
extern Gamepad gamepad;
|
||||
extern GamepadOut gamepadOut;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _GAMEPAD_H_
|
||||
|
||||
298
src/descriptors/DInputDescriptors.h
Normal file
298
src/descriptors/DInputDescriptors.h
Normal file
@@ -0,0 +1,298 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define HID_ENDPOINT_SIZE 64
|
||||
|
||||
// Mac OS-X and Linux automatically load the correct drivers. On
|
||||
// Windows, even though the driver is supplied by Microsoft, an
|
||||
// INF file is needed to load the driver. These numbers need to
|
||||
// match the INF file.
|
||||
// #define VENDOR_ID 0x10C4
|
||||
// #define PRODUCT_ID 0x82C0
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Endpoint Buffer Configuration
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#define ENDPOINT0_SIZE 64
|
||||
|
||||
#define GAMEPAD_INTERFACE 0
|
||||
#define GAMEPAD_ENDPOINT 1
|
||||
#define GAMEPAD_SIZE 64
|
||||
|
||||
#define LSB(n) (n & 255)
|
||||
#define MSB(n) ((n >> 8) & 255)
|
||||
|
||||
#define DINPUT_HAT_UP 0x00
|
||||
#define DINPUT_HAT_UPRIGHT 0x01
|
||||
#define DINPUT_HAT_RIGHT 0x02
|
||||
#define DINPUT_HAT_DOWNRIGHT 0x03
|
||||
#define DINPUT_HAT_DOWN 0x04
|
||||
#define DINPUT_HAT_DOWNLEFT 0x05
|
||||
#define DINPUT_HAT_LEFT 0x06
|
||||
#define DINPUT_HAT_UPLEFT 0x07
|
||||
#define DINPUT_HAT_NOTHING 0x08
|
||||
|
||||
#define DINPUT_MASK_SQUARE (1U << 0)
|
||||
#define DINPUT_MASK_CROSS (1U << 1)
|
||||
#define DINPUT_MASK_CIRCLE (1U << 2)
|
||||
#define DINPUT_MASK_TRIANGLE (1U << 3)
|
||||
#define DINPUT_MASK_L1 (1U << 4)
|
||||
#define DINPUT_MASK_R1 (1U << 5)
|
||||
#define DINPUT_MASK_L2 (1U << 6)
|
||||
#define DINPUT_MASK_R2 (1U << 7)
|
||||
#define DINPUT_MASK_SELECT (1U << 8)
|
||||
#define DINPUT_MASK_START (1U << 9)
|
||||
#define DINPUT_MASK_L3 (1U << 10)
|
||||
#define DINPUT_MASK_R3 (1U << 11)
|
||||
#define DINPUT_MASK_PS (1U << 12)
|
||||
#define DINPUT_MASK_TP (1U << 13)
|
||||
|
||||
#define DINPUT_JOYSTICK_MIN 0x00
|
||||
#define DINPUT_JOYSTICK_MID 0x80
|
||||
#define DINPUT_JOYSTICK_MAX 0xFF
|
||||
|
||||
typedef struct __attribute((packed, aligned(1)))
|
||||
{
|
||||
// digital buttons, 0 = off, 1 = on
|
||||
// uint8_t report_id;
|
||||
// uint8_t padding;
|
||||
|
||||
uint8_t square_btn : 1;
|
||||
uint8_t cross_btn : 1;
|
||||
uint8_t circle_btn : 1;
|
||||
uint8_t triangle_btn : 1;
|
||||
|
||||
uint8_t l1_btn : 1;
|
||||
uint8_t r1_btn : 1;
|
||||
uint8_t l2_btn : 1;
|
||||
uint8_t r2_btn : 1;
|
||||
|
||||
uint8_t select_btn : 1;
|
||||
uint8_t start_btn : 1;
|
||||
uint8_t l3_btn : 1;
|
||||
uint8_t r3_btn : 1;
|
||||
|
||||
uint8_t ps_btn : 1;
|
||||
uint8_t tp_btn : 1;
|
||||
// uint8_t l2_btn_alt : 1;
|
||||
|
||||
// uint8_t r2_btn_alt : 1;
|
||||
uint8_t : 2;
|
||||
|
||||
// digital direction, use the dir_* constants(enum)
|
||||
// 8 = center, 0 = up, 1 = up/right, 2 = right, 3 = right/down
|
||||
// 4 = down, 5 = down/left, 6 = left, 7 = left/up
|
||||
|
||||
// uint8_t direction;
|
||||
uint8_t direction : 4;
|
||||
uint8_t : 4;
|
||||
|
||||
// left and right analog sticks, 0x00 left/up, 0x80 middle, 0xff right/down
|
||||
|
||||
uint8_t l_x_axis;
|
||||
uint8_t l_y_axis;
|
||||
uint8_t r_x_axis;
|
||||
uint8_t r_y_axis;
|
||||
|
||||
// Gonna assume these are button analog values for the d-pad.
|
||||
// NOTE: NOT EVEN SURE THIS IS RIGHT, OR IN THE CORRECT ORDER
|
||||
uint8_t right_axis;
|
||||
uint8_t left_axis;
|
||||
uint8_t up_axis;
|
||||
uint8_t down_axis;
|
||||
|
||||
// button axis, 0x00 = unpressed, 0xff = fully pressed
|
||||
|
||||
uint8_t triangle_axis;
|
||||
uint8_t circle_axis;
|
||||
uint8_t cross_axis;
|
||||
uint8_t square_axis;
|
||||
|
||||
uint8_t l1_axis;
|
||||
uint8_t r1_axis;
|
||||
uint8_t l2_axis;
|
||||
uint8_t r2_axis;
|
||||
} DInputReport;
|
||||
|
||||
struct DInputLed {
|
||||
uint8_t time_enabled; /* the total time the led is active (0xff means forever) */
|
||||
uint8_t duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
|
||||
uint8_t enabled;
|
||||
uint8_t duty_off; /* % of duty_length the led is off (0xff means 100%) */
|
||||
uint8_t duty_on; /* % of duty_length the led is on (0xff mean 100%) */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct DInputRumble {
|
||||
uint8_t padding;
|
||||
uint8_t right_duration; /* Right motor duration (0xff means forever) */
|
||||
uint8_t right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
|
||||
uint8_t left_duration; /* Left motor duration (0xff means forever) */
|
||||
uint8_t left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct DInputOutReport {
|
||||
struct DInputRumble rumble;
|
||||
uint8_t padding[4];
|
||||
uint8_t leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
|
||||
struct DInputLed led[4]; /* LEDx at (4 - x) */
|
||||
struct DInputLed _reserved; /* LED5, not actually soldered */
|
||||
} __attribute__((packed));
|
||||
|
||||
static const uint8_t dinput_string_language[] = { 0x09, 0x04 };
|
||||
static const uint8_t dinput_string_manufacturer[] = "SHANWAN";
|
||||
static const uint8_t dinput_string_product[] = "2In1 USB Joystick";
|
||||
static const uint8_t dinput_string_version[] = "1.0";
|
||||
|
||||
static const uint8_t *dinput_string_descriptors[] __attribute__((unused)) =
|
||||
{
|
||||
dinput_string_language,
|
||||
dinput_string_manufacturer,
|
||||
dinput_string_product,
|
||||
dinput_string_version
|
||||
};
|
||||
|
||||
static const uint8_t dinput_device_descriptor[] =
|
||||
{
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x10, 0x01, // bcdUSB 1.10
|
||||
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x40, // bMaxPacketSize0 64
|
||||
0x63, 0x25, // idVendor 0x2563
|
||||
0x75, 0x05, // idProduct 0x0575
|
||||
0x00, 0x02, // bcdDevice 4.00
|
||||
0x01, // iManufacturer (String Index)
|
||||
0x02, // iProduct (String Index)
|
||||
0x00, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
static const uint8_t dinput_report_descriptor[] =
|
||||
{
|
||||
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, 0x0D, // Report Count (13)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x0D, // Usage Maximum (0x0D)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x03, // Report Count (3)
|
||||
0x81, 0x01, // Input (Const,Array,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)
|
||||
0x09, 0x21, // Usage (0x21)
|
||||
0x09, 0x22, // Usage (0x22)
|
||||
0x09, 0x23, // Usage (0x23)
|
||||
0x09, 0x24, // Usage (0x24)
|
||||
0x09, 0x25, // Usage (0x25)
|
||||
0x09, 0x26, // Usage (0x26)
|
||||
0x09, 0x27, // Usage (0x27)
|
||||
0x09, 0x28, // Usage (0x28)
|
||||
0x09, 0x29, // Usage (0x29)
|
||||
0x09, 0x2A, // Usage (0x2A)
|
||||
0x09, 0x2B, // Usage (0x2B)
|
||||
0x95, 0x0C, // Report Count (12)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x0A, 0x21, 0x26, // Usage (0x2621)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x0A, 0x21, 0x26, // Usage (0x2621)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x26, 0xFF, 0x03, // Logical Maximum (1023)
|
||||
0x46, 0xFF, 0x03, // Physical Maximum (1023)
|
||||
0x09, 0x2C, // Usage (0x2C)
|
||||
0x09, 0x2D, // Usage (0x2D)
|
||||
0x09, 0x2E, // Usage (0x2E)
|
||||
0x09, 0x2F, // Usage (0x2F)
|
||||
0x75, 0x10, // Report Size (16)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
};
|
||||
|
||||
static const uint8_t dinput_hid_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x10, 0x01, // bcdHID 1.10
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0x89, 0x00, // wDescriptorLength[0] 137
|
||||
};
|
||||
|
||||
static const uint8_t dinput_configuration_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x29, 0x00, // wTotalLength 41
|
||||
0x01, // bNumInterfaces 1
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0x80, // bmAttributes
|
||||
0xFA, // bMaxPower 500mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x02, // bNumEndpoints 2
|
||||
0x03, // bInterfaceClass
|
||||
0x00, // bInterfaceSubClass
|
||||
0x00, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x10, 0x01, // bcdHID 1.10
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0x89, 0x00, // wDescriptorLength[0] 137
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x02, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x0A, // bInterval 10 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x81, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x0A, // bInterval 10 (unit depends on device speed)
|
||||
};
|
||||
264
src/descriptors/HIDDescriptors.h
Normal file
264
src/descriptors/HIDDescriptors.h
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define HID_ENDPOINT_SIZE 64
|
||||
|
||||
// Mac OS-X and Linux automatically load the correct drivers. On
|
||||
// Windows, even though the driver is supplied by Microsoft, an
|
||||
// INF file is needed to load the driver. These numbers need to
|
||||
// match the INF file.
|
||||
#define VENDOR_ID 0x10C4
|
||||
#define PRODUCT_ID 0x82C0
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Endpoint Buffer Configuration
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#define ENDPOINT0_SIZE 64
|
||||
|
||||
#define GAMEPAD_INTERFACE 0
|
||||
#define GAMEPAD_ENDPOINT 1
|
||||
#define GAMEPAD_SIZE 64
|
||||
|
||||
#define LSB(n) (n & 255)
|
||||
#define MSB(n) ((n >> 8) & 255)
|
||||
|
||||
// HAT report (4 bits)
|
||||
#define HID_HAT_UP 0x00
|
||||
#define HID_HAT_UPRIGHT 0x01
|
||||
#define HID_HAT_RIGHT 0x02
|
||||
#define HID_HAT_DOWNRIGHT 0x03
|
||||
#define HID_HAT_DOWN 0x04
|
||||
#define HID_HAT_DOWNLEFT 0x05
|
||||
#define HID_HAT_LEFT 0x06
|
||||
#define HID_HAT_UPLEFT 0x07
|
||||
#define HID_HAT_NOTHING 0x08
|
||||
|
||||
// Button report (16 bits)
|
||||
#define HID_MASK_SQUARE (1U << 0)
|
||||
#define HID_MASK_CROSS (1U << 1)
|
||||
#define HID_MASK_CIRCLE (1U << 2)
|
||||
#define HID_MASK_TRIANGLE (1U << 3)
|
||||
#define HID_MASK_L1 (1U << 4)
|
||||
#define HID_MASK_R1 (1U << 5)
|
||||
#define HID_MASK_L2 (1U << 6)
|
||||
#define HID_MASK_R2 (1U << 7)
|
||||
#define HID_MASK_SELECT (1U << 8)
|
||||
#define HID_MASK_START (1U << 9)
|
||||
#define HID_MASK_L3 (1U << 10)
|
||||
#define HID_MASK_R3 (1U << 11)
|
||||
#define HID_MASK_PS (1U << 12)
|
||||
#define HID_MASK_TP (1U << 13)
|
||||
|
||||
// HID analog sticks only report 8 bits
|
||||
#define HID_JOYSTICK_MIN 0x00
|
||||
#define HID_JOYSTICK_MID 0x80
|
||||
#define HID_JOYSTICK_MAX 0xFF
|
||||
|
||||
typedef struct __attribute((packed, aligned(1)))
|
||||
{
|
||||
// digital buttons, 0 = off, 1 = on
|
||||
|
||||
uint8_t square_btn : 1;
|
||||
uint8_t cross_btn : 1;
|
||||
uint8_t circle_btn : 1;
|
||||
uint8_t triangle_btn : 1;
|
||||
|
||||
uint8_t l1_btn : 1;
|
||||
uint8_t r1_btn : 1;
|
||||
uint8_t l2_btn : 1;
|
||||
uint8_t r2_btn : 1;
|
||||
|
||||
uint8_t select_btn : 1;
|
||||
uint8_t start_btn : 1;
|
||||
uint8_t l3_btn : 1;
|
||||
uint8_t r3_btn : 1;
|
||||
|
||||
uint8_t ps_btn : 1;
|
||||
uint8_t tp_btn : 1;
|
||||
// uint8_t l2_btn_alt : 1;
|
||||
|
||||
// uint8_t r2_btn_alt : 1;
|
||||
uint8_t : 2;
|
||||
|
||||
// digital direction, use the dir_* constants(enum)
|
||||
// 8 = center, 0 = up, 1 = up/right, 2 = right, 3 = right/down
|
||||
// 4 = down, 5 = down/left, 6 = left, 7 = left/up
|
||||
|
||||
uint8_t direction;
|
||||
|
||||
// left and right analog sticks, 0x00 left/up, 0x80 middle, 0xff right/down
|
||||
|
||||
uint8_t l_x_axis;
|
||||
uint8_t l_y_axis;
|
||||
uint8_t r_x_axis;
|
||||
uint8_t r_y_axis;
|
||||
|
||||
// Gonna assume these are button analog values for the d-pad.
|
||||
// NOTE: NOT EVEN SURE THIS IS RIGHT, OR IN THE CORRECT ORDER
|
||||
uint8_t right_axis;
|
||||
uint8_t left_axis;
|
||||
uint8_t up_axis;
|
||||
uint8_t down_axis;
|
||||
|
||||
// button axis, 0x00 = unpressed, 0xff = fully pressed
|
||||
|
||||
uint8_t triangle_axis;
|
||||
uint8_t circle_axis;
|
||||
uint8_t cross_axis;
|
||||
uint8_t square_axis;
|
||||
|
||||
uint8_t l1_axis;
|
||||
uint8_t r1_axis;
|
||||
uint8_t l2_axis;
|
||||
uint8_t r2_axis;
|
||||
} HIDReport;
|
||||
|
||||
static const uint8_t hid_string_language[] = { 0x09, 0x04 };
|
||||
static const uint8_t hid_string_manufacturer[] = "Raspberry Pi";
|
||||
static const uint8_t hid_string_product[] = "OGX-Mini";
|
||||
static const uint8_t hid_string_version[] = "1.0";
|
||||
|
||||
static const uint8_t *hid_string_descriptors[] __attribute__((unused)) =
|
||||
{
|
||||
hid_string_language,
|
||||
hid_string_manufacturer,
|
||||
hid_string_product,
|
||||
hid_string_version
|
||||
};
|
||||
|
||||
static const uint8_t hid_device_descriptor[] =
|
||||
{
|
||||
18, // bLength
|
||||
1, // bDescriptorType
|
||||
0x00, 0x02, // bcdUSB
|
||||
0, // bDeviceClass
|
||||
0, // bDeviceSubClass
|
||||
0, // bDeviceProtocol
|
||||
ENDPOINT0_SIZE, // bMaxPacketSize0
|
||||
LSB(VENDOR_ID), MSB(VENDOR_ID), // idVendor
|
||||
LSB(PRODUCT_ID), MSB(PRODUCT_ID), // idProduct
|
||||
0x00, 0x01, // bcdDevice
|
||||
1, // iManufacturer
|
||||
2, // iProduct
|
||||
0, // iSerialNumber
|
||||
1 // bNumConfigurations
|
||||
};
|
||||
|
||||
static const uint8_t hid_report_descriptor[] =
|
||||
{
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x05, // USAGE (Gamepad)
|
||||
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, 0x0e, // REPORT_COUNT (13)
|
||||
0x05, 0x09, // USAGE_PAGE (Button)
|
||||
0x19, 0x01, // USAGE_MINIMUM (Button 1)
|
||||
0x29, 0x0e, // USAGE_MAXIMUM (Button 13)
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0x95, 0x02, // REPORT_COUNT (3)
|
||||
0x81, 0x01, // INPUT (Cnst,Ary,Abs)
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x25, 0x07, // LOGICAL_MAXIMUM (7)
|
||||
0x46, 0x3b, 0x01, // PHYSICAL_MAXIMUM (315)
|
||||
0x75, 0x04, // REPORT_SIZE (4)
|
||||
0x95, 0x01, // REPORT_COUNT (1)
|
||||
0x65, 0x14, // UNIT (Eng Rot:Angular Pos)
|
||||
0x09, 0x39, // USAGE (Hat switch)
|
||||
0x81, 0x42, // INPUT (Data,Var,Abs,Null)
|
||||
0x65, 0x00, // UNIT (None)
|
||||
0x95, 0x01, // REPORT_COUNT (1)
|
||||
0x81, 0x01, // INPUT (Cnst,Ary,Abs)
|
||||
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 (6)
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0x06, 0x00, 0xff, // USAGE_PAGE (Vendor Specific)
|
||||
0x09, 0x20, // Unknown
|
||||
0x09, 0x21, // Unknown
|
||||
0x09, 0x22, // Unknown
|
||||
0x09, 0x23, // Unknown
|
||||
0x09, 0x24, // Unknown
|
||||
0x09, 0x25, // Unknown
|
||||
0x09, 0x26, // Unknown
|
||||
0x09, 0x27, // Unknown
|
||||
0x09, 0x28, // Unknown
|
||||
0x09, 0x29, // Unknown
|
||||
0x09, 0x2a, // Unknown
|
||||
0x09, 0x2b, // Unknown
|
||||
0x95, 0x0c, // REPORT_COUNT (12)
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0x0a, 0x21, 0x26, // Unknown
|
||||
0x95, 0x08, // REPORT_COUNT (8)
|
||||
0xb1, 0x02, // FEATURE (Data,Var,Abs)
|
||||
0xc0 // END_COLLECTION
|
||||
};
|
||||
|
||||
static const uint8_t hid_hid_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x11, 0x01, // bcdHID 1.11
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
sizeof(hid_report_descriptor), 0x00, // wDescriptorLength[0] 90
|
||||
};
|
||||
|
||||
#define CONFIG1_DESC_SIZE (9+9+9+7)
|
||||
static const uint8_t hid_configuration_descriptor[] =
|
||||
{
|
||||
// configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10
|
||||
9, // bLength;
|
||||
2, // bDescriptorType;
|
||||
LSB(CONFIG1_DESC_SIZE), // wTotalLength
|
||||
MSB(CONFIG1_DESC_SIZE),
|
||||
1, // bNumInterfaces
|
||||
1, // bConfigurationValue
|
||||
0, // iConfiguration
|
||||
0x80, // bmAttributes
|
||||
50, // bMaxPower
|
||||
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
|
||||
9, // bLength
|
||||
4, // bDescriptorType
|
||||
GAMEPAD_INTERFACE, // bInterfaceNumber
|
||||
0, // bAlternateSetting
|
||||
1, // bNumEndpoints
|
||||
0x03, // bInterfaceClass (0x03 = HID)
|
||||
0x00, // bInterfaceSubClass (0x00 = No Boot)
|
||||
0x00, // bInterfaceProtocol (0x00 = No Protocol)
|
||||
0, // iInterface
|
||||
// HID interface descriptor, HID 1.11 spec, section 6.2.1
|
||||
9, // bLength
|
||||
0x21, // bDescriptorType
|
||||
0x11, 0x01, // bcdHID
|
||||
0, // bCountryCode
|
||||
1, // bNumDescriptors
|
||||
0x22, // bDescriptorType
|
||||
sizeof(hid_report_descriptor), // wDescriptorLength
|
||||
0,
|
||||
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
|
||||
7, // bLength
|
||||
5, // bDescriptorType
|
||||
GAMEPAD_ENDPOINT | 0x80, // bEndpointAddress
|
||||
0x03, // bmAttributes (0x03=intr)
|
||||
GAMEPAD_SIZE, 0, // wMaxPacketSize
|
||||
1 // bInterval (1 ms)
|
||||
};
|
||||
298
src/descriptors/PS3Descriptors.h
Normal file
298
src/descriptors/PS3Descriptors.h
Normal file
@@ -0,0 +1,298 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define HID_ENDPOINT_SIZE 64
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Endpoint Buffer Configuration
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#define ENDPOINT0_SIZE 64
|
||||
|
||||
#define GAMEPAD_INTERFACE 0
|
||||
#define GAMEPAD_ENDPOINT 1
|
||||
#define GAMEPAD_SIZE 64
|
||||
|
||||
#define LSB(n) (n & 255)
|
||||
#define MSB(n) ((n >> 8) & 255)
|
||||
|
||||
#define PS3_HAT_UP 0x00
|
||||
#define PS3_HAT_UPRIGHT 0x01
|
||||
#define PS3_HAT_RIGHT 0x02
|
||||
#define PS3_HAT_DOWNRIGHT 0x03
|
||||
#define PS3_HAT_DOWN 0x04
|
||||
#define PS3_HAT_DOWNLEFT 0x05
|
||||
#define PS3_HAT_LEFT 0x06
|
||||
#define PS3_HAT_UPLEFT 0x07
|
||||
#define PS3_HAT_NOTHING 0x08
|
||||
|
||||
#define PS3_MASK_SQUARE (1U << 0)
|
||||
#define PS3_MASK_CROSS (1U << 1)
|
||||
#define PS3_MASK_CIRCLE (1U << 2)
|
||||
#define PS3_MASK_TRIANGLE (1U << 3)
|
||||
#define PS3_MASK_L1 (1U << 4)
|
||||
#define PS3_MASK_R1 (1U << 5)
|
||||
#define PS3_MASK_L2 (1U << 6)
|
||||
#define PS3_MASK_R2 (1U << 7)
|
||||
#define PS3_MASK_SELECT (1U << 8)
|
||||
#define PS3_MASK_START (1U << 9)
|
||||
#define PS3_MASK_L3 (1U << 10)
|
||||
#define PS3_MASK_R3 (1U << 11)
|
||||
#define PS3_MASK_PS (1U << 12)
|
||||
#define PS3_MASK_TP (1U << 13)
|
||||
|
||||
#define PS3_JOYSTICK_MIN 0x00
|
||||
#define PS3_JOYSTICK_MID 0x80
|
||||
#define PS3_JOYSTICK_MAX 0xFF
|
||||
|
||||
typedef struct __attribute((packed, aligned(1)))
|
||||
{
|
||||
uint8_t report_id;
|
||||
uint8_t unk0;
|
||||
|
||||
uint8_t select : 1;
|
||||
uint8_t l3 : 1;
|
||||
uint8_t r3 : 1;
|
||||
uint8_t start : 1;
|
||||
uint8_t up : 1;
|
||||
uint8_t right : 1;
|
||||
uint8_t down : 1;
|
||||
uint8_t left : 1;
|
||||
|
||||
uint8_t l2 : 1;
|
||||
uint8_t r2 : 1;
|
||||
uint8_t l1 : 1;
|
||||
uint8_t r1 : 1;
|
||||
uint8_t triangle : 1;
|
||||
uint8_t circle : 1;
|
||||
uint8_t cross : 1;
|
||||
uint8_t square : 1;
|
||||
|
||||
uint8_t ps : 1;
|
||||
uint8_t : 0;
|
||||
|
||||
uint8_t unknown1;
|
||||
|
||||
uint8_t left_x;
|
||||
uint8_t left_y;
|
||||
uint8_t right_x;
|
||||
uint8_t right_y;
|
||||
|
||||
uint8_t unknown2[4];
|
||||
|
||||
uint8_t up_axis;
|
||||
uint8_t right_axis;
|
||||
uint8_t down_axis;
|
||||
uint8_t left_axis;
|
||||
|
||||
uint8_t l2_axis;
|
||||
uint8_t r2_axis;
|
||||
uint8_t l1_axis;
|
||||
uint8_t r1_axis;
|
||||
|
||||
uint8_t triangle_axis;
|
||||
uint8_t circle_axis;
|
||||
uint8_t cross_axis;
|
||||
uint8_t square_axis;
|
||||
|
||||
uint8_t unknown3[15];
|
||||
|
||||
int16_t acceler_x;
|
||||
int16_t acceler_y;
|
||||
int16_t acceler_z;
|
||||
|
||||
int16_t velocity_z;
|
||||
} DualShock3Report;
|
||||
|
||||
struct sixaxis_led {
|
||||
uint8_t time_enabled; /* the total time the led is active (0xff means forever) */
|
||||
uint8_t duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
|
||||
uint8_t enabled;
|
||||
uint8_t duty_off; /* % of duty_length the led is off (0xff means 100%) */
|
||||
uint8_t duty_on; /* % of duty_length the led is on (0xff mean 100%) */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sixaxis_rumble {
|
||||
uint8_t padding;
|
||||
uint8_t right_duration; /* Right motor duration (0xff means forever) */
|
||||
uint8_t right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
|
||||
uint8_t left_duration; /* Left motor duration (0xff means forever) */
|
||||
uint8_t left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sixaxis_output_report {
|
||||
struct sixaxis_rumble rumble;
|
||||
uint8_t padding[4];
|
||||
uint8_t leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
|
||||
struct sixaxis_led led[4]; /* LEDx at (4 - x) */
|
||||
struct sixaxis_led _reserved; /* LED5, not actually soldered */
|
||||
} __attribute__((packed));
|
||||
|
||||
static const uint8_t ps3_string_language[] = { 0x09, 0x04 };
|
||||
static const uint8_t ps3_string_manufacturer[] = "Sony";
|
||||
static const uint8_t ps3_string_product[] = "PLAYSTATION(R)3 Controller";
|
||||
static const uint8_t ps3_string_version[] = "1.0";
|
||||
|
||||
static const uint8_t *ps3_string_descriptors[] __attribute__((unused)) =
|
||||
{
|
||||
ps3_string_language,
|
||||
ps3_string_manufacturer,
|
||||
ps3_string_product,
|
||||
ps3_string_version
|
||||
};
|
||||
|
||||
static const uint8_t ps3_device_descriptor[] =
|
||||
{
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x00, 0x02, // bcdUSB 2.00
|
||||
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x40, // bMaxPacketSize0 64
|
||||
0x4C, 0x05, // idVendor 0x054C
|
||||
0x68, 0x02, // idProduct 0x0268
|
||||
0x00, 0x01, // bcdDevice 2.00
|
||||
0x01, // iManufacturer (String Index)
|
||||
0x02, // iProduct (String Index)
|
||||
0x00, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
static const uint8_t ps3_report_descriptor[] =
|
||||
{
|
||||
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 const uint8_t ps3_hid_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x11, 0x01, // bcdHID 1.11
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0x94, 0x00, // wDescriptorLength[0] 148
|
||||
};
|
||||
|
||||
// #define CONFIG1_DESC_SIZE (9+9+9+7)
|
||||
static const uint8_t ps3_configuration_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x29, 0x00, // wTotalLength 41
|
||||
0x01, // bNumInterfaces 1
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0x80, // bmAttributes
|
||||
0xFA, // bMaxPower 500mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x02, // bNumEndpoints 2
|
||||
0x03, // bInterfaceClass
|
||||
0x00, // bInterfaceSubClass
|
||||
0x00, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x10, 0x01, // bcdHID 1.10
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0x94, 0x00, // wDescriptorLength[0] 148
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x81, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x0A, // bInterval 10 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x02, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x0A, // bInterval 10 (unit depends on device speed)
|
||||
};
|
||||
147
src/descriptors/PSClassicDescriptors.h
Normal file
147
src/descriptors/PSClassicDescriptors.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define PSCLASSIC_ENDPOINT_SIZE 64
|
||||
|
||||
// Button report (16 bits)
|
||||
#define PSCLASSIC_MASK_TRIANGLE (1U << 0)
|
||||
#define PSCLASSIC_MASK_CIRCLE (1U << 1)
|
||||
#define PSCLASSIC_MASK_CROSS (1U << 2)
|
||||
#define PSCLASSIC_MASK_SQUARE (1U << 3)
|
||||
#define PSCLASSIC_MASK_L2 (1U << 4)
|
||||
#define PSCLASSIC_MASK_R2 (1U << 5)
|
||||
#define PSCLASSIC_MASK_L1 (1U << 6)
|
||||
#define PSCLASSIC_MASK_R1 (1U << 7)
|
||||
#define PSCLASSIC_MASK_SELECT (1U << 8)
|
||||
#define PSCLASSIC_MASK_START (1U << 9)
|
||||
|
||||
#define PSCLASSIC_MASK_UP_LEFT 0x0000
|
||||
#define PSCLASSIC_MASK_UP 0x0400
|
||||
#define PSCLASSIC_MASK_UP_RIGHT 0x0800
|
||||
#define PSCLASSIC_MASK_LEFT 0x1000
|
||||
#define PSCLASSIC_MASK_CENTER 0x1400
|
||||
#define PSCLASSIC_MASK_RIGHT 0x1800
|
||||
#define PSCLASSIC_MASK_DOWN_LEFT 0x2000
|
||||
#define PSCLASSIC_MASK_DOWN 0x2400
|
||||
#define PSCLASSIC_MASK_DOWN_RIGHT 0x2800
|
||||
|
||||
#define PSCLASSIC_JOYSTICK_MID 0x7f
|
||||
|
||||
typedef struct __attribute((packed, aligned(1)))
|
||||
{
|
||||
uint16_t buttons; // 10 buttons (1 bit each)
|
||||
} PSClassicReport;
|
||||
|
||||
static const uint8_t psclassic_string_language[] = { 0x09, 0x04 };
|
||||
static const uint8_t psclassic_string_manufacturer[] = "Sony Interactive Entertainment";
|
||||
static const uint8_t psclassic_string_product[] = "Controller";
|
||||
static const uint8_t psclassic_string_version[] = { };
|
||||
|
||||
static const uint8_t *psclassic_string_descriptors[] __attribute__((unused)) =
|
||||
{
|
||||
psclassic_string_language,
|
||||
psclassic_string_manufacturer,
|
||||
psclassic_string_product,
|
||||
psclassic_string_version
|
||||
};
|
||||
|
||||
static const uint8_t psclassic_device_descriptor[] =
|
||||
{
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x00, 0x02, // bcdUSB 2.00
|
||||
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x40, // bMaxPacketSize0 64
|
||||
0x4C, 0x05, // idVendor 0x054C
|
||||
0xDA, 0x0C, // idProduct 0x0CDA
|
||||
0x00, 0x01, // bcdDevice 2.00
|
||||
0x01, // iManufacturer (String Index)
|
||||
0x02, // iProduct (String Index)
|
||||
0x00, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
static const uint8_t psclassic_hid_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x11, 0x01, // bcdHID 1.11
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0x31, 0x00, // wDescriptorLength[0] 49
|
||||
};
|
||||
|
||||
static const uint8_t psclassic_configuration_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x22, 0x00, // wTotalLength 34
|
||||
0x01, // bNumInterfaces 1
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0xA0, // bmAttributes Remote Wakeup
|
||||
0x32, // bMaxPower 100mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x01, // bNumEndpoints 1
|
||||
0x03, // bInterfaceClass
|
||||
0x00, // bInterfaceSubClass
|
||||
0x00, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x11, 0x01, // bcdHID 1.11
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0x31, 0x00, // wDescriptorLength[0] 49
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x81, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x0A, // bInterval 10 (unit depends on device speed)
|
||||
};
|
||||
|
||||
static const uint8_t psclassic_report_descriptor[] =
|
||||
{
|
||||
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)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x0A, // Report Count (10)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x0A, // Usage Maximum (0x0A)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x02, // Logical Maximum (2)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x45, 0x02, // Physical Maximum (2)
|
||||
0x75, 0x02, // Report Size (2)
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
};
|
||||
244
src/descriptors/SwitchDescriptors.h
Normal file
244
src/descriptors/SwitchDescriptors.h
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define SWITCH_ENDPOINT_SIZE 64
|
||||
|
||||
// HAT report (4 bits)
|
||||
#define SWITCH_HAT_UP 0x00
|
||||
#define SWITCH_HAT_UPRIGHT 0x01
|
||||
#define SWITCH_HAT_RIGHT 0x02
|
||||
#define SWITCH_HAT_DOWNRIGHT 0x03
|
||||
#define SWITCH_HAT_DOWN 0x04
|
||||
#define SWITCH_HAT_DOWNLEFT 0x05
|
||||
#define SWITCH_HAT_LEFT 0x06
|
||||
#define SWITCH_HAT_UPLEFT 0x07
|
||||
#define SWITCH_HAT_NOTHING 0x08
|
||||
|
||||
// Button report (16 bits)
|
||||
#define SWITCH_MASK_Y (1U << 0)
|
||||
#define SWITCH_MASK_B (1U << 1)
|
||||
#define SWITCH_MASK_A (1U << 2)
|
||||
#define SWITCH_MASK_X (1U << 3)
|
||||
#define SWITCH_MASK_L (1U << 4)
|
||||
#define SWITCH_MASK_R (1U << 5)
|
||||
#define SWITCH_MASK_ZL (1U << 6)
|
||||
#define SWITCH_MASK_ZR (1U << 7)
|
||||
#define SWITCH_MASK_MINUS (1U << 8)
|
||||
#define SWITCH_MASK_PLUS (1U << 9)
|
||||
#define SWITCH_MASK_L3 (1U << 10)
|
||||
#define SWITCH_MASK_R3 (1U << 11)
|
||||
#define SWITCH_MASK_HOME (1U << 12)
|
||||
#define SWITCH_MASK_CAPTURE (1U << 13)
|
||||
|
||||
// Switch analog sticks only report 8 bits
|
||||
#define SWITCH_JOYSTICK_MIN 0x00
|
||||
#define SWITCH_JOYSTICK_MID 0x80
|
||||
#define SWITCH_JOYSTICK_MAX 0xFF
|
||||
|
||||
struct SwitchProReport
|
||||
{
|
||||
uint8_t reportId;
|
||||
uint8_t timer;
|
||||
|
||||
uint8_t connInfo : 4;
|
||||
uint8_t battery : 4;
|
||||
|
||||
uint8_t y : 1;
|
||||
uint8_t x : 1;
|
||||
uint8_t b : 1;
|
||||
uint8_t a : 1;
|
||||
uint8_t : 2;
|
||||
uint8_t r : 1;
|
||||
uint8_t zr : 1;
|
||||
|
||||
uint8_t minus : 1;
|
||||
uint8_t plus : 1;
|
||||
uint8_t stickR : 1;
|
||||
uint8_t stickL : 1;
|
||||
uint8_t home : 1;
|
||||
uint8_t capture : 1;
|
||||
uint8_t : 0;
|
||||
|
||||
uint8_t down : 1;
|
||||
uint8_t up : 1;
|
||||
uint8_t right : 1;
|
||||
uint8_t left : 1;
|
||||
uint8_t : 2;
|
||||
uint8_t l : 1;
|
||||
uint8_t zl : 1;
|
||||
|
||||
uint16_t leftX : 12;
|
||||
uint16_t leftY : 12;
|
||||
uint16_t rightX : 12;
|
||||
uint16_t rightY : 12;
|
||||
|
||||
uint8_t vibrator;
|
||||
|
||||
uint16_t accelerX;
|
||||
uint16_t accelerY;
|
||||
uint16_t accelerZ;
|
||||
|
||||
uint16_t velocityX;
|
||||
uint16_t velocityY;
|
||||
uint16_t velocityZ;
|
||||
}
|
||||
__attribute__((packed));
|
||||
|
||||
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;
|
||||
} SwitchReport;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t buttons;
|
||||
uint8_t hat;
|
||||
uint8_t lx;
|
||||
uint8_t ly;
|
||||
uint8_t rx;
|
||||
uint8_t ry;
|
||||
} SwitchOutReport;
|
||||
|
||||
|
||||
static const uint8_t switch_string_language[] = { 0x09, 0x04 };
|
||||
static const uint8_t switch_string_manufacturer[] = "HORI CO.,LTD.";
|
||||
static const uint8_t switch_string_product[] = "POKKEN CONTROLLER";
|
||||
static const uint8_t switch_string_version[] = "1.0";
|
||||
|
||||
static const uint8_t *switch_string_descriptors[] __attribute__((unused)) =
|
||||
{
|
||||
switch_string_language,
|
||||
switch_string_manufacturer,
|
||||
switch_string_product,
|
||||
switch_string_version
|
||||
};
|
||||
|
||||
static const uint8_t switch_device_descriptor[] =
|
||||
{
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x00, 0x02, // bcdUSB 2.00
|
||||
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x40, // bMaxPacketSize0 64
|
||||
0x0D, 0x0F, // idVendor 0x0F0D
|
||||
0x92, 0x00, // idProduct 0x92
|
||||
0x00, 0x01, // bcdDevice 2.00
|
||||
0x01, // iManufacturer (String Index)
|
||||
0x02, // iProduct (String Index)
|
||||
0x00, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
static const uint8_t switch_hid_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x11, 0x01, // bcdHID 1.11
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0x56, 0x00, // wDescriptorLength[0] 86
|
||||
};
|
||||
|
||||
static const uint8_t switch_configuration_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x29, 0x00, // wTotalLength 41
|
||||
0x01, // bNumInterfaces 1
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0x80, // bmAttributes
|
||||
0xFA, // bMaxPower 500mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x02, // bNumEndpoints 2
|
||||
0x03, // bInterfaceClass
|
||||
0x00, // bInterfaceSubClass
|
||||
0x00, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x11, 0x01, // bcdHID 1.11
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0x56, 0x00, // wDescriptorLength[0] 86
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x02, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x01, // bInterval 1 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x81, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x01, // bInterval 1 (unit depends on device speed)
|
||||
};
|
||||
|
||||
static const uint8_t switch_report_descriptor[] =
|
||||
{
|
||||
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
|
||||
};
|
||||
76
src/descriptors/USBSerialDescriptors.h
Normal file
76
src/descriptors/USBSerialDescriptors.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "bsp/board_api.h"
|
||||
|
||||
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
|
||||
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
|
||||
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
|
||||
|
||||
#define USB_VID 0xCafe
|
||||
#define USB_BCD 0x0200
|
||||
|
||||
tusb_desc_device_t const usbserial_device_descriptor =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = USB_BCD,
|
||||
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = USB_VID,
|
||||
.idProduct = USB_PID,
|
||||
.bcdDevice = 0x0100,
|
||||
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ITF_NUM_CDC = 0,
|
||||
ITF_NUM_CDC_DATA,
|
||||
ITF_NUM_TOTAL
|
||||
};
|
||||
|
||||
#define EPNUM_CDC_NOTIF 0x81
|
||||
#define EPNUM_CDC_OUT 0x02
|
||||
#define EPNUM_CDC_IN 0x82
|
||||
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
|
||||
|
||||
uint8_t const usbserial_configuration_descriptor[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
|
||||
|
||||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
|
||||
};
|
||||
|
||||
enum {
|
||||
STRID_LANGID = 0,
|
||||
STRID_MANUFACTURER,
|
||||
STRID_PRODUCT,
|
||||
STRID_SERIAL,
|
||||
};
|
||||
|
||||
char const *string_desc_arr[] =
|
||||
{
|
||||
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||
"TinyUSB", // 1: Manufacturer
|
||||
"TinyUSB Device", // 2: Product
|
||||
NULL, // 3: Serials will use unique ID if possible
|
||||
"TinyUSB CDC", // 4: CDC Interface
|
||||
};
|
||||
|
||||
static uint16_t _desc_str[32 + 1];
|
||||
140
src/descriptors/XInputDescriptors.h
Normal file
140
src/descriptors/XInputDescriptors.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define XINPUT_ENDPOINT_SIZE 20
|
||||
|
||||
// Buttons 1 (8 bits)
|
||||
// TODO: Consider using an enum class here.
|
||||
#define XBOX_MASK_UP (1U << 0)
|
||||
#define XBOX_MASK_DOWN (1U << 1)
|
||||
#define XBOX_MASK_LEFT (1U << 2)
|
||||
#define XBOX_MASK_RIGHT (1U << 3)
|
||||
#define XBOX_MASK_START (1U << 4)
|
||||
#define XBOX_MASK_BACK (1U << 5)
|
||||
#define XBOX_MASK_LS (1U << 6)
|
||||
#define XBOX_MASK_RS (1U << 7)
|
||||
|
||||
// Buttons 2 (8 bits)
|
||||
// TODO: Consider using an enum class here.
|
||||
#define XBOX_MASK_LB (1U << 0)
|
||||
#define XBOX_MASK_RB (1U << 1)
|
||||
#define XBOX_MASK_HOME (1U << 2)
|
||||
//#define UNUSED (1U << 3)
|
||||
#define XBOX_MASK_A (1U << 4)
|
||||
#define XBOX_MASK_B (1U << 5)
|
||||
#define XBOX_MASK_X (1U << 6)
|
||||
#define XBOX_MASK_Y (1U << 7)
|
||||
|
||||
#define XBOX_REPORT_TYPE_RUMBLE 0x00
|
||||
#define XBOX_REPORT_TYPE_LED 0x01
|
||||
|
||||
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];
|
||||
} XInputReport;
|
||||
|
||||
typedef struct __attribute((packed, aligned(1)))
|
||||
{
|
||||
uint8_t report_type;
|
||||
uint8_t report_size;
|
||||
uint8_t led;
|
||||
uint8_t lrumble;
|
||||
uint8_t rrumble;
|
||||
uint8_t reserved[3];
|
||||
} XInputOutReport;
|
||||
|
||||
static const uint8_t xinput_string_language[] = { 0x09, 0x04 };
|
||||
static const uint8_t xinput_string_manfacturer[] = "Microsoft";
|
||||
static const uint8_t xinput_string_product[] = "XInput STANDARD GAMEPAD";
|
||||
static const uint8_t xinput_string_version[] = "1.0";
|
||||
|
||||
static const uint8_t *xinput_string_descriptors[] __attribute__((unused)) =
|
||||
{
|
||||
xinput_string_language,
|
||||
xinput_string_manfacturer,
|
||||
xinput_string_product,
|
||||
xinput_string_version
|
||||
};
|
||||
|
||||
static const uint8_t xinput_device_descriptor[] =
|
||||
{
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x00, 0x02, // bcdUSB 2.00
|
||||
0xFF, // bDeviceClass
|
||||
0xFF, // bDeviceSubClass
|
||||
0xFF, // bDeviceProtocol
|
||||
0x40, // bMaxPacketSize0 64
|
||||
0x5E, 0x04, // idVendor 0x045E
|
||||
0x8E, 0x02, // idProduct 0x028E
|
||||
0x14, 0x01, // bcdDevice 2.14
|
||||
0x01, // iManufacturer (String Index)
|
||||
0x02, // iProduct (String Index)
|
||||
0x03, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
static const uint8_t xinput_configuration_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x30, 0x00, // wTotalLength 48
|
||||
0x01, // bNumInterfaces 1
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0x80, // bmAttributes
|
||||
0xFA, // bMaxPower 500mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x02, // bNumEndpoints 2
|
||||
0xFF, // bInterfaceClass
|
||||
0x5D, // bInterfaceSubClass
|
||||
0x01, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x10, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x10, 0x01, // bcdHID 1.10
|
||||
0x01, // bCountryCode
|
||||
0x24, // bNumDescriptors
|
||||
0x81, // bDescriptorType[0] (Unknown 0x81)
|
||||
0x14, 0x03, // wDescriptorLength[0] 788
|
||||
0x00, // bDescriptorType[1] (Unknown 0x00)
|
||||
0x03, 0x13, // wDescriptorLength[1] 4867
|
||||
0x01, // bDescriptorType[2] (Unknown 0x02)
|
||||
0x00, 0x03, // wDescriptorLength[2] 768
|
||||
0x00, // bDescriptorType[3] (Unknown 0x00)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x81, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x01, // bInterval 1 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x01, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x08, // bInterval 8 (unit depends on device speed)
|
||||
};
|
||||
23
src/descriptors/XboxOriginalDescriptors.h
Normal file
23
src/descriptors/XboxOriginalDescriptors.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "usbd/xboxog/xid/xid_driver.h"
|
||||
|
||||
#define XboxOriginalReport USB_XboxGamepad_InReport_t
|
||||
|
||||
static const uint8_t xboxoriginal_string_language[] = { 0x09, 0x04 };
|
||||
static const uint8_t xboxoriginal_string_manufacturer[] = "";
|
||||
static const uint8_t xboxoriginal_string_product[] = "";
|
||||
static const uint8_t xboxoriginal_string_version[] = "1.0";
|
||||
|
||||
static const uint8_t *xboxoriginal_string_descriptors[] __attribute__((unused)) =
|
||||
{
|
||||
xboxoriginal_string_language,
|
||||
xboxoriginal_string_manufacturer,
|
||||
xboxoriginal_string_product,
|
||||
xboxoriginal_string_version
|
||||
};
|
||||
|
||||
static const uint8_t *xboxoriginal_device_descriptor = (const uint8_t*)&XID_DESC_DEVICE;
|
||||
|
||||
static const uint8_t *xboxoriginal_configuration_descriptor = (const uint8_t*)&XID_DESC_CONFIGURATION;
|
||||
@@ -1,125 +1,95 @@
|
||||
#include <pico/stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/multicore.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "hardware/flash.h"
|
||||
#include "hardware/sync.h"
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#include "usbd/board_config.h"
|
||||
#include "input_mode.h"
|
||||
|
||||
#define AIRCR_REG (*((volatile uint32_t *)(0xE000ED0C))) // Address of the AIRCR register
|
||||
#define AIRCR_SYSRESETREQ (1 << 2) // Position of SYSRESETREQ bit in AIRCR
|
||||
#define AIRCR_VECTKEY (0x5FA << 16) // VECTKEY value
|
||||
#define AIRCR_Register (*((volatile uint32_t*)(PPB_BASE + 0x0ED0C)))
|
||||
|
||||
#define FLASH_TARGET_OFFSET (256 * 1024)
|
||||
#define FLASH_SIZE_BYTES (2 * 1024 * 1024)
|
||||
#define EEPROM_SIZE 1
|
||||
#define EEPROM_FLASH_TARGET_OFFSET (2 * 1024 * 1024 - EEPROM_SIZE)
|
||||
|
||||
void system_reset()
|
||||
void store_input_mode(enum InputMode mode)
|
||||
{
|
||||
AIRCR_REG = AIRCR_VECTKEY | AIRCR_SYSRESETREQ;
|
||||
while(1);
|
||||
uint8_t data = (uint8_t)mode;
|
||||
|
||||
uint32_t flash_offset = EEPROM_FLASH_TARGET_OFFSET % FLASH_SECTOR_SIZE;
|
||||
uint32_t flash_sector_base = EEPROM_FLASH_TARGET_OFFSET - flash_offset;
|
||||
|
||||
uint8_t new_sector_content[FLASH_SECTOR_SIZE];
|
||||
|
||||
memcpy(new_sector_content, (const void *)(XIP_BASE + flash_sector_base), FLASH_SECTOR_SIZE);
|
||||
|
||||
new_sector_content[flash_offset] = data;
|
||||
|
||||
flash_range_erase(flash_sector_base, FLASH_SECTOR_SIZE);
|
||||
flash_range_program(flash_sector_base, new_sector_content, FLASH_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
bool store_input_mode(enum InputMode new_mode)
|
||||
void change_input_mode(Gamepad previous_gamepad)
|
||||
{
|
||||
int buf[FLASH_PAGE_SIZE/sizeof(int)];
|
||||
memset(buf, 0xFF, FLASH_PAGE_SIZE);
|
||||
int saved_mode = new_mode; // changed to uint8?
|
||||
|
||||
buf[0] = saved_mode;
|
||||
|
||||
uint32_t saved_interrupts = save_and_disable_interrupts();
|
||||
|
||||
flash_range_erase((FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE), FLASH_SECTOR_SIZE);
|
||||
flash_range_program((FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE), (uint8_t *)buf, FLASH_PAGE_SIZE);
|
||||
|
||||
restore_interrupts(saved_interrupts);
|
||||
|
||||
const uint8_t *flash_target_contents = (const uint8_t *)(XIP_BASE + FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE);
|
||||
|
||||
if ((uint8_t)saved_mode != *flash_target_contents)
|
||||
if (!previous_gamepad.state.start)
|
||||
{
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool change_input_mode(GamepadButtons buttons)
|
||||
{
|
||||
if (!buttons.start)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
InputMode new_mode;
|
||||
|
||||
if (buttons.up)
|
||||
if (previous_gamepad.state.up)
|
||||
{
|
||||
new_mode = INPUT_MODE_XINPUT;
|
||||
}
|
||||
else if (buttons.left)
|
||||
else if (previous_gamepad.state.left)
|
||||
{
|
||||
new_mode = INPUT_MODE_HID;
|
||||
}
|
||||
else if (buttons.right)
|
||||
else if (previous_gamepad.state.right)
|
||||
{
|
||||
new_mode = INPUT_MODE_XBOXORIGINAL;
|
||||
}
|
||||
else if (buttons.down)
|
||||
else if (previous_gamepad.state.down)
|
||||
{
|
||||
new_mode = INPUT_MODE_SWITCH;
|
||||
}
|
||||
else if (buttons.a)
|
||||
else if (previous_gamepad.state.a)
|
||||
{
|
||||
new_mode = INPUT_MODE_PSCLASSIC;
|
||||
}
|
||||
|
||||
bool mode_stored = false;
|
||||
|
||||
if (new_mode)
|
||||
// else if (previous_gamepad.state.b)
|
||||
// {
|
||||
// new_mode = INPUT_MODE_USBSERIAL;
|
||||
// }
|
||||
else
|
||||
{
|
||||
tud_disconnect();
|
||||
sleep_ms(300);
|
||||
multicore_reset_core1(); // stop tusb host
|
||||
|
||||
if (store_input_mode(new_mode))
|
||||
{
|
||||
system_reset(); // reset rp2040
|
||||
mode_stored = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return mode_stored;
|
||||
store_input_mode(new_mode);
|
||||
sleep_ms(200);
|
||||
|
||||
// restart the rp2040
|
||||
AIRCR_Register = 0x5FA0004;
|
||||
sleep_ms(200);
|
||||
}
|
||||
|
||||
enum InputMode get_input_mode()
|
||||
{
|
||||
#if (CDC_DEBUG > 0)
|
||||
#ifdef HOST_DEBUG
|
||||
return INPUT_MODE_USBSERIAL;
|
||||
#endif
|
||||
|
||||
const uint8_t *stored_value = (const uint8_t *)(XIP_BASE + FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE);
|
||||
const uint8_t* flash_addr = (const uint8_t*)(XIP_BASE + EEPROM_FLASH_TARGET_OFFSET);
|
||||
uint8_t stored_value = *flash_addr;
|
||||
|
||||
#if (MAX_GAMEPADS < 1)
|
||||
if ((*stored_value == INPUT_MODE_HID) || (*stored_value == INPUT_MODE_SWITCH))
|
||||
if (stored_value >= INPUT_MODE_XINPUT && stored_value <= INPUT_MODE_XBOXORIGINAL)
|
||||
{
|
||||
return(enum InputMode)*stored_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return INPUT_MODE_HID;
|
||||
}
|
||||
#else
|
||||
if (*stored_value >= INPUT_MODE_XINPUT && *stored_value <= INPUT_MODE_XBOXORIGINAL)
|
||||
{
|
||||
return(enum InputMode)*stored_value;
|
||||
return(enum InputMode)stored_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return INPUT_MODE_XBOXORIGINAL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -6,10 +6,21 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include "Gamepad.h"
|
||||
#include "usbd/inputmodes.h"
|
||||
|
||||
enum InputMode
|
||||
{
|
||||
INPUT_MODE_XINPUT,
|
||||
INPUT_MODE_SWITCH,
|
||||
INPUT_MODE_HID,
|
||||
// INPUT_MODE_KEYBOARD,
|
||||
INPUT_MODE_PSCLASSIC,
|
||||
INPUT_MODE_XBOXORIGINAL,
|
||||
INPUT_MODE_USBSERIAL,
|
||||
// INPUT_MODE_CONFIG,
|
||||
};
|
||||
|
||||
enum InputMode get_input_mode();
|
||||
bool change_input_mode(GamepadButtons buttons);
|
||||
void change_input_mode(Gamepad previous_gamepad_state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
36
src/main.cpp
36
src/main.cpp
@@ -1,6 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/multicore.h"
|
||||
|
||||
@@ -8,11 +9,14 @@
|
||||
#include "bsp/board_api.h"
|
||||
|
||||
#include "usbh/tusb_host.h"
|
||||
|
||||
#include "usbd/drivermanager.h"
|
||||
#include "usbd/drivers/gpdriver.h"
|
||||
#include "Gamepad.h"
|
||||
#include "usbd/gpdriver.h"
|
||||
|
||||
#include "input_mode.h"
|
||||
#include "usbd/board_config.h"
|
||||
|
||||
Gamepad gamepad;
|
||||
GamepadOut gamepadOut;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
@@ -29,35 +33,29 @@ int main(void)
|
||||
multicore_reset_core1();
|
||||
multicore_launch_core1(usbh_main);
|
||||
|
||||
GamepadButtons prev_gamepad_buttons = gamepad(0).buttons;
|
||||
Gamepad previous_gamepad = gamepad;
|
||||
absolute_time_t last_time_gamepad_changed = get_absolute_time();
|
||||
absolute_time_t last_time_gamepad_checked = get_absolute_time();
|
||||
|
||||
while (1)
|
||||
{
|
||||
for (int i = 0; i < MAX_GAMEPADS; i++)
|
||||
{
|
||||
uint8_t outBuffer[64];
|
||||
GPDriver* driver = driverManager.getDriver();
|
||||
driver->process(i, &gamepad(i), outBuffer);
|
||||
driver->update_rumble(i, &gamepad(i));
|
||||
}
|
||||
uint8_t outBuffer[64];
|
||||
GPDriver* driver = driverManager.getDriver();
|
||||
driver->process(&gamepad, outBuffer);
|
||||
|
||||
if (absolute_time_diff_us(last_time_gamepad_checked, get_absolute_time()) >= 500000)
|
||||
if (absolute_time_diff_us(last_time_gamepad_checked, get_absolute_time()) >= 200000)
|
||||
{
|
||||
// check if digital buttons have changed
|
||||
if (memcmp(&gamepad(0).buttons, &prev_gamepad_buttons, sizeof(GamepadButtons)) != 0)
|
||||
// check if digital buttons have changed (first 16 bytes of gamepad.state)
|
||||
if (memcmp(&gamepad.state, &previous_gamepad.state, 16) != 0)
|
||||
{
|
||||
memcpy(&prev_gamepad_buttons, &gamepad(0).buttons, sizeof(GamepadButtons));
|
||||
memcpy(&previous_gamepad.state, &gamepad.state, sizeof(gamepad.state));
|
||||
last_time_gamepad_changed = get_absolute_time();
|
||||
}
|
||||
// haven't changed for 3 seconds
|
||||
else if (absolute_time_diff_us(last_time_gamepad_changed, get_absolute_time()) >= 3000000)
|
||||
{
|
||||
if (!change_input_mode(prev_gamepad_buttons))
|
||||
{
|
||||
last_time_gamepad_changed = get_absolute_time();
|
||||
}
|
||||
change_input_mode(previous_gamepad);
|
||||
last_time_gamepad_changed = get_absolute_time();
|
||||
}
|
||||
|
||||
last_time_gamepad_checked = get_absolute_time();
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#include "usbd/board_config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -137,7 +135,7 @@
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_CDC 1
|
||||
#define CFG_TUD_ECM_RNDIS 0
|
||||
#define CFG_TUD_HID (MAX_GAMEPADS + 1)
|
||||
#define CFG_TUD_HID 2
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// HOST CONFIGURATION
|
||||
@@ -152,12 +150,12 @@
|
||||
// Size of buffer to hold descriptors and other data used for enumeration
|
||||
#define CFG_TUH_ENUMERATION_BUFSIZE 512
|
||||
|
||||
#define CFG_TUH_HUB MAX_GAMEPADS
|
||||
#define CFG_TUH_HUB 1
|
||||
#define CFG_TUH_CDC 0
|
||||
#define CFG_TUH_HID MAX_GAMEPADS // typical keyboard + mouse device can have 3-4 HID interfaces
|
||||
#define CFG_TUH_HID 1 // typical keyboard + mouse device can have 3-4 HID interfaces
|
||||
#define CFG_TUH_MSC 0
|
||||
#define CFG_TUH_VENDOR 0
|
||||
#define CFG_TUH_XINPUT MAX_GAMEPADS
|
||||
#define CFG_TUH_XINPUT 1
|
||||
|
||||
// max device support (excluding hub device)
|
||||
#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports
|
||||
|
||||
1
src/usbd
1
src/usbd
Submodule src/usbd deleted from f7bdb97a37
197
src/usbd/dinput/DInputDriver.cpp
Normal file
197
src/usbd/dinput/DInputDriver.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
*/
|
||||
#include "utilities/scaling.h"
|
||||
|
||||
#include "usbd/dinput/DInputDriver.h"
|
||||
#include "descriptors/DInputDescriptors.h"
|
||||
|
||||
#include "usbd/shared/driverhelper.h"
|
||||
|
||||
// Magic byte sequence to enable PS button on PS3
|
||||
static const uint8_t ps3_magic_init_bytes[8] = {0x21, 0x26, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
static bool hid_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||
{
|
||||
if ( request->bmRequestType == 0xA1 &&
|
||||
request->bRequest == HID_REQ_CONTROL_GET_REPORT &&
|
||||
request->wValue == 0x0300 )
|
||||
{
|
||||
return tud_control_xfer(rhport, request, (void *) ps3_magic_init_bytes, sizeof(ps3_magic_init_bytes));
|
||||
}
|
||||
else
|
||||
{
|
||||
return hidd_control_xfer_cb(rhport, stage, request);
|
||||
}
|
||||
}
|
||||
|
||||
void DInputDriver::initialize()
|
||||
{
|
||||
dinput_report = {
|
||||
// .report_id = 0,
|
||||
.square_btn = 0, .cross_btn = 0, .circle_btn = 0, .triangle_btn = 0,
|
||||
.l1_btn = 0, .r1_btn = 0, .l2_btn = 0, .r2_btn = 0,
|
||||
.select_btn = 0, .start_btn = 0, .l3_btn = 0, .r3_btn = 0, .ps_btn = 0, .tp_btn = 0,
|
||||
.direction = 0x08,
|
||||
.l_x_axis = DINPUT_JOYSTICK_MID,
|
||||
.l_y_axis = DINPUT_JOYSTICK_MID,
|
||||
.r_x_axis = DINPUT_JOYSTICK_MID,
|
||||
.r_y_axis = DINPUT_JOYSTICK_MID,
|
||||
.right_axis = 0x00, .left_axis = 0x00, .up_axis = 0x00, .down_axis = 0x00,
|
||||
.triangle_axis = 0x00, .circle_axis = 0x00, .cross_axis = 0x00, .square_axis = 0x00,
|
||||
.l1_axis = 0x00, .r1_axis = 0x00, .l2_axis = 0x00, .r2_axis = 0x00
|
||||
};
|
||||
|
||||
class_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
|
||||
};
|
||||
}
|
||||
|
||||
// Generate HID report from gamepad and send to TUSB Device
|
||||
void DInputDriver::process(Gamepad * gamepad, uint8_t * outBuffer)
|
||||
{
|
||||
if (gamepad->state.up) {
|
||||
if (gamepad->state.right) {
|
||||
dinput_report.direction = DINPUT_HAT_UPRIGHT;
|
||||
} else if (gamepad->state.left) {
|
||||
dinput_report.direction = DINPUT_HAT_UPLEFT;
|
||||
} else {
|
||||
dinput_report.direction = DINPUT_HAT_UP;
|
||||
}
|
||||
} else if (gamepad->state.down) {
|
||||
if (gamepad->state.right) {
|
||||
dinput_report.direction = DINPUT_HAT_DOWNRIGHT;
|
||||
} else if (gamepad->state.left) {
|
||||
dinput_report.direction = DINPUT_HAT_DOWNLEFT;
|
||||
} else {
|
||||
dinput_report.direction = DINPUT_HAT_DOWN;
|
||||
}
|
||||
} else if (gamepad->state.left) {
|
||||
dinput_report.direction = DINPUT_HAT_LEFT;
|
||||
} else if (gamepad->state.right) {
|
||||
dinput_report.direction = DINPUT_HAT_RIGHT;
|
||||
} else {
|
||||
dinput_report.direction = DINPUT_HAT_NOTHING;
|
||||
}
|
||||
|
||||
dinput_report.cross_btn = gamepad->state.a ? 1 : 0;
|
||||
dinput_report.circle_btn = gamepad->state.b ? 1 : 0;
|
||||
dinput_report.square_btn = gamepad->state.x ? 1 : 0;
|
||||
dinput_report.triangle_btn = gamepad->state.y ? 1 : 0;
|
||||
dinput_report.l1_btn = gamepad->state.lb ? 1 : 0;
|
||||
dinput_report.r1_btn = gamepad->state.rb ? 1 : 0;
|
||||
dinput_report.l2_btn = gamepad->state.lt > 0 ? 1 : 0;
|
||||
dinput_report.r2_btn = gamepad->state.rt > 0 ? 1 : 0;
|
||||
dinput_report.select_btn = gamepad->state.back ? 1 : 0;
|
||||
dinput_report.start_btn = gamepad->state.start ? 1 : 0;
|
||||
dinput_report.l3_btn = gamepad->state.l3 ? 1 : 0;
|
||||
dinput_report.r3_btn = gamepad->state.r3 ? 1 : 0;
|
||||
dinput_report.ps_btn = gamepad->state.sys ? 1 : 0;
|
||||
dinput_report.tp_btn = gamepad->state.misc ? 1 : 0;
|
||||
|
||||
dinput_report.cross_axis = gamepad->state.a ? 0xFF : 0x00;
|
||||
dinput_report.circle_axis = gamepad->state.b ? 0xFF : 0x00;
|
||||
dinput_report.square_axis = gamepad->state.x ? 0xFF : 0x00;
|
||||
dinput_report.triangle_axis = gamepad->state.y ? 0xFF : 0x00;
|
||||
dinput_report.l1_axis = gamepad->state.lb ? 0xFF : 0x00;
|
||||
dinput_report.r1_axis = gamepad->state.rb ? 0xFF : 0x00;
|
||||
|
||||
dinput_report.l2_axis = gamepad->state.lt;
|
||||
dinput_report.r2_axis = gamepad->state.rt;
|
||||
|
||||
dinput_report.l_x_axis = scale_int16_to_uint8(gamepad->state.lx, false);
|
||||
dinput_report.l_y_axis = scale_int16_to_uint8(gamepad->state.ly, true);
|
||||
dinput_report.r_x_axis = scale_int16_to_uint8(gamepad->state.rx, false);
|
||||
dinput_report.r_y_axis = scale_int16_to_uint8(gamepad->state.ry, true);
|
||||
|
||||
// Wake up TinyUSB device
|
||||
if (tud_suspended())
|
||||
tud_remote_wakeup();
|
||||
|
||||
void * report = &dinput_report;
|
||||
uint16_t report_size = sizeof(dinput_report);
|
||||
// if (memcmp(last_report, report, report_size) != 0)
|
||||
// {
|
||||
// HID ready + report sent, copy previous report
|
||||
if (tud_hid_ready() && tud_hid_report(0, report, report_size) == true ) {
|
||||
memcpy(last_report, report, report_size);
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
// tud_hid_get_report_cb
|
||||
uint16_t DInputDriver::get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
memcpy(buffer, &dinput_report, sizeof(DInputReport));
|
||||
return sizeof(DInputReport);
|
||||
}
|
||||
|
||||
// Only PS4 does anything with set report
|
||||
void DInputDriver::set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
|
||||
{
|
||||
// testing things, I'll have a usb sniffer soon so this will get figured out then
|
||||
|
||||
// DInputOutReport dinput_out_report = {0};
|
||||
// memcpy(&dinput_out_report, buffer, sizeof(dinput_out_report));
|
||||
|
||||
// // gamepadOut.out_state.rrumble = dinput_out_report.rumble.right_motor_on ? 0xFF : 0x00;
|
||||
// // gamepadOut.out_state.lrumble = gamepadOut.out_state.rrumble = dinput_out_report.rumble.left_motor_force;
|
||||
|
||||
// // if (dinput_out_report.rumble.left_motor_force > 0)
|
||||
// if (dinput_out_report.led->time_enabled > 0)
|
||||
// gamepadOut.out_state.lrumble = gamepadOut.out_state.rrumble = 0xFF;
|
||||
// if (dinput_out_report.led->duty_length > 0)
|
||||
// gamepadOut.out_state.lrumble = gamepadOut.out_state.rrumble = 0xFF;
|
||||
// if (dinput_out_report.led->enabled > 0)
|
||||
// gamepadOut.out_state.lrumble = gamepadOut.out_state.rrumble = 0xFF;
|
||||
// if (dinput_out_report.led->duty_off > 0)
|
||||
// gamepadOut.out_state.lrumble = gamepadOut.out_state.rrumble = 0xFF;
|
||||
// if (dinput_out_report.led->duty_on > 0)
|
||||
// gamepadOut.out_state.lrumble = gamepadOut.out_state.rrumble = 0xFF;
|
||||
}
|
||||
|
||||
// Only XboxOG and Xbox One use vendor control xfer cb
|
||||
bool DInputDriver::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t * DInputDriver::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
const char *value = (const char *)dinput_string_descriptors[index];
|
||||
return getStringDescriptor(value, index); // getStringDescriptor returns a static array
|
||||
}
|
||||
|
||||
const uint8_t * DInputDriver::get_descriptor_device_cb()
|
||||
{
|
||||
return dinput_device_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * DInputDriver::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return dinput_report_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * DInputDriver::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return dinput_configuration_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * DInputDriver::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16_t DInputDriver::GetJoystickMidValue()
|
||||
{
|
||||
return DINPUT_JOYSTICK_MID << 8;
|
||||
}
|
||||
32
src/usbd/dinput/DInputDriver.h
Normal file
32
src/usbd/dinput/DInputDriver.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
*/
|
||||
|
||||
#ifndef _HID_DRIVER_H_
|
||||
#define _HID_DRIVER_H_
|
||||
|
||||
#include "usbd/gpdriver.h"
|
||||
#include "descriptors/DInputDescriptors.h"
|
||||
#include "descriptors/PS3Descriptors.h"
|
||||
|
||||
class DInputDriver : public GPDriver {
|
||||
public:
|
||||
virtual void initialize();
|
||||
virtual void process(Gamepad * gamepad, uint8_t * outBuffer);
|
||||
virtual uint16_t get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen);
|
||||
virtual void set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize);
|
||||
virtual bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
virtual const uint16_t * get_descriptor_string_cb(uint8_t index, uint16_t langid);
|
||||
virtual const uint8_t * get_descriptor_device_cb();
|
||||
virtual const uint8_t * get_hid_descriptor_report_cb(uint8_t itf) ;
|
||||
virtual const uint8_t * get_descriptor_configuration_cb(uint8_t index);
|
||||
virtual const uint8_t * get_descriptor_device_qualifier_cb();
|
||||
virtual uint16_t GetJoystickMidValue();
|
||||
private:
|
||||
uint8_t last_report[CFG_TUD_ENDPOINT0_SIZE] = { };
|
||||
|
||||
DInputReport dinput_report;
|
||||
};
|
||||
|
||||
#endif // _HID_DRIVER_H_
|
||||
54
src/usbd/drivermanager.cpp
Normal file
54
src/usbd/drivermanager.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "usbd/drivermanager.h"
|
||||
|
||||
// #include "net/NetDriver.h"
|
||||
// #include "keyboard/KeyboardDriver.h"
|
||||
|
||||
// #include "usbd/dinput/DInputDriver.h"
|
||||
#include "usbd/hid/HIDDriver.h"
|
||||
#include "usbd/psclassic/PSClassicDriver.h"
|
||||
#include "usbd/switch/SwitchDriver.h"
|
||||
#include "usbd/xboxog/XboxOriginalDriver.h"
|
||||
#include "usbd/xinput/XInputDriver.h"
|
||||
#include "usbd/usbserial/USBSerialDriver.h"
|
||||
|
||||
void DriverManager::setup(InputMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
// case INPUT_MODE_CONFIG:
|
||||
// driver = new NetDriver();
|
||||
// break;
|
||||
// case INPUT_MODE_KEYBOARD:
|
||||
// driver = new KeyboardDriver();
|
||||
// break;
|
||||
case INPUT_MODE_HID:
|
||||
driver = new HIDDriver();
|
||||
break;
|
||||
// case INPUT_MODE_HID:
|
||||
// driver = new DInputDriver();
|
||||
// break;
|
||||
case INPUT_MODE_PSCLASSIC:
|
||||
driver = new PSClassicDriver();
|
||||
break;
|
||||
case INPUT_MODE_SWITCH:
|
||||
driver = new SwitchDriver();
|
||||
break;
|
||||
case INPUT_MODE_XBOXORIGINAL:
|
||||
driver = new XboxOriginalDriver();
|
||||
break;
|
||||
case INPUT_MODE_XINPUT:
|
||||
driver = new XInputDriver();
|
||||
break;
|
||||
case INPUT_MODE_USBSERIAL:
|
||||
driver = new USBSerialDriver();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize our chosen driver
|
||||
driver->initialize();
|
||||
|
||||
// Start the TinyUSB Device functionality
|
||||
tud_init(TUD_OPT_RHPORT);
|
||||
}
|
||||
24
src/usbd/drivermanager.h
Normal file
24
src/usbd/drivermanager.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef _DRIVERMANAGER_H
|
||||
#define _DRIVERMANAGER_H
|
||||
|
||||
#include "usbd/gpdriver.h"
|
||||
#include "input_mode.h"
|
||||
|
||||
class GPDriver;
|
||||
|
||||
class DriverManager {
|
||||
public:
|
||||
DriverManager(DriverManager const&) = delete;
|
||||
void operator=(DriverManager const&) = delete;
|
||||
static DriverManager& getInstance() {// Thread-safe storage ensures cross-thread talk
|
||||
static DriverManager instance; // Guaranteed to be destroyed. // Instantiated on first use.
|
||||
return instance;
|
||||
}
|
||||
GPDriver * getDriver() { return driver; }
|
||||
void setup(InputMode mode);
|
||||
private:
|
||||
DriverManager() {}
|
||||
GPDriver * driver;
|
||||
};
|
||||
|
||||
#endif
|
||||
40
src/usbd/gpdriver.h
Normal file
40
src/usbd/gpdriver.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
*/
|
||||
|
||||
#ifndef _GPDRIVER_H_
|
||||
#define _GPDRIVER_H_
|
||||
|
||||
#include "Gamepad.h"
|
||||
#include "tusb_config.h"
|
||||
|
||||
#include "tusb.h"
|
||||
#include "class/hid/hid.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
// Forward declare gamepad
|
||||
class Gamepad;
|
||||
|
||||
//
|
||||
// GP2040-CE USB Device Class Driver
|
||||
//
|
||||
class GPDriver {
|
||||
public:
|
||||
virtual void initialize() = 0;
|
||||
virtual void process(Gamepad * gamepad, uint8_t * outBuffer) = 0;
|
||||
virtual uint16_t get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) = 0;
|
||||
virtual void set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) = 0;
|
||||
virtual bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) = 0;
|
||||
virtual const uint16_t * get_descriptor_string_cb(uint8_t index, uint16_t langid) = 0;
|
||||
virtual const uint8_t * get_descriptor_device_cb() = 0;
|
||||
virtual const uint8_t * get_hid_descriptor_report_cb(uint8_t itf) = 0;
|
||||
virtual const uint8_t * get_descriptor_configuration_cb(uint8_t index) = 0;
|
||||
virtual const uint8_t * get_descriptor_device_qualifier_cb() = 0;
|
||||
virtual uint16_t GetJoystickMidValue() = 0;
|
||||
const usbd_class_driver_t * get_class_driver() { return &class_driver; }
|
||||
protected:
|
||||
usbd_class_driver_t class_driver;
|
||||
};
|
||||
|
||||
#endif
|
||||
174
src/usbd/hid/HIDDriver.cpp
Normal file
174
src/usbd/hid/HIDDriver.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
*/
|
||||
#include "utilities/scaling.h"
|
||||
|
||||
#include "usbd/hid/HIDDriver.h"
|
||||
#include "descriptors/HIDDescriptors.h"
|
||||
#include "descriptors/PS3Descriptors.h"
|
||||
#include "usbd/shared/driverhelper.h"
|
||||
|
||||
// Magic byte sequence to enable PS button on PS3
|
||||
static const uint8_t ps3_magic_init_bytes[8] = {0x21, 0x26, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
static bool hid_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||
{
|
||||
if ( request->bmRequestType == 0xA1 &&
|
||||
request->bRequest == HID_REQ_CONTROL_GET_REPORT &&
|
||||
request->wValue == 0x0300 ) {
|
||||
return tud_control_xfer(rhport, request, (void *) ps3_magic_init_bytes, sizeof(ps3_magic_init_bytes));
|
||||
} else {
|
||||
return hidd_control_xfer_cb(rhport, stage, request);
|
||||
}
|
||||
}
|
||||
|
||||
void HIDDriver::initialize()
|
||||
{
|
||||
hidReport = {
|
||||
.square_btn = 0, .cross_btn = 0, .circle_btn = 0, .triangle_btn = 0,
|
||||
.l1_btn = 0, .r1_btn = 0, .l2_btn = 0, .r2_btn = 0,
|
||||
.select_btn = 0, .start_btn = 0, .l3_btn = 0, .r3_btn = 0, .ps_btn = 0, .tp_btn = 0,
|
||||
.direction = 0x08,
|
||||
.l_x_axis = HID_JOYSTICK_MID,
|
||||
.l_y_axis = HID_JOYSTICK_MID,
|
||||
.r_x_axis = HID_JOYSTICK_MID,
|
||||
.r_y_axis = HID_JOYSTICK_MID,
|
||||
.right_axis = 0x00, .left_axis = 0x00, .up_axis = 0x00, .down_axis = 0x00,
|
||||
.triangle_axis = 0x00, .circle_axis = 0x00, .cross_axis = 0x00, .square_axis = 0x00,
|
||||
.l1_axis = 0x00, .r1_axis = 0x00, .l2_axis = 0x00, .r2_axis = 0x00
|
||||
};
|
||||
|
||||
class_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
|
||||
};
|
||||
}
|
||||
|
||||
// Generate HID report from gamepad and send to TUSB Device
|
||||
void HIDDriver::process(Gamepad * gamepad, uint8_t * outBuffer)
|
||||
{
|
||||
if (gamepad->state.up) {
|
||||
if (gamepad->state.right) {
|
||||
hidReport.direction = HID_HAT_UPRIGHT;
|
||||
} else if (gamepad->state.left) {
|
||||
hidReport.direction = HID_HAT_UPLEFT;
|
||||
} else {
|
||||
hidReport.direction = HID_HAT_UP;
|
||||
}
|
||||
} else if (gamepad->state.down) {
|
||||
if (gamepad->state.right) {
|
||||
hidReport.direction = HID_HAT_DOWNRIGHT;
|
||||
} else if (gamepad->state.left) {
|
||||
hidReport.direction = HID_HAT_DOWNLEFT;
|
||||
} else {
|
||||
hidReport.direction = HID_HAT_DOWN;
|
||||
}
|
||||
} else if (gamepad->state.left) {
|
||||
hidReport.direction = HID_HAT_LEFT;
|
||||
} else if (gamepad->state.right) {
|
||||
hidReport.direction = HID_HAT_RIGHT;
|
||||
} else {
|
||||
hidReport.direction = HID_HAT_NOTHING;
|
||||
}
|
||||
|
||||
hidReport.cross_btn = gamepad->state.a ? 1 : 0;
|
||||
hidReport.circle_btn = gamepad->state.b ? 1 : 0;
|
||||
hidReport.square_btn = gamepad->state.x ? 1 : 0;
|
||||
hidReport.triangle_btn = gamepad->state.y ? 1 : 0;
|
||||
hidReport.l1_btn = gamepad->state.lb ? 1 : 0;
|
||||
hidReport.r1_btn = gamepad->state.rb ? 1 : 0;
|
||||
hidReport.l2_btn = gamepad->state.lt > 0 ? 1 : 0;
|
||||
hidReport.r2_btn = gamepad->state.rt > 0 ? 1 : 0;
|
||||
hidReport.select_btn = gamepad->state.back ? 1 : 0;
|
||||
hidReport.start_btn = gamepad->state.start ? 1 : 0;
|
||||
hidReport.l3_btn = gamepad->state.l3 ? 1 : 0;
|
||||
hidReport.r3_btn = gamepad->state.r3 ? 1 : 0;
|
||||
hidReport.ps_btn = gamepad->state.sys ? 1 : 0;
|
||||
hidReport.tp_btn = gamepad->state.misc ? 1 : 0;
|
||||
|
||||
hidReport.cross_axis = gamepad->state.a ? 0xFF : 0x00;
|
||||
hidReport.circle_axis = gamepad->state.b ? 0xFF : 0x00;
|
||||
hidReport.square_axis = gamepad->state.x ? 0xFF : 0x00;
|
||||
hidReport.triangle_axis = gamepad->state.y ? 0xFF : 0x00;
|
||||
hidReport.l1_axis = gamepad->state.lb ? 0xFF : 0x00;
|
||||
hidReport.r1_axis = gamepad->state.rb ? 0xFF : 0x00;
|
||||
|
||||
hidReport.l2_axis = gamepad->state.lt;
|
||||
hidReport.r2_axis = gamepad->state.rt;
|
||||
|
||||
hidReport.l_x_axis = scale_int16_to_uint8(gamepad->state.lx, false);
|
||||
hidReport.l_y_axis = scale_int16_to_uint8(gamepad->state.ly, true);
|
||||
hidReport.r_x_axis = scale_int16_to_uint8(gamepad->state.rx, false);
|
||||
hidReport.r_y_axis = scale_int16_to_uint8(gamepad->state.ry, true);
|
||||
|
||||
// Wake up TinyUSB device
|
||||
if (tud_suspended())
|
||||
tud_remote_wakeup();
|
||||
|
||||
void * report = &hidReport;
|
||||
uint16_t report_size = sizeof(hidReport);
|
||||
if (memcmp(last_report, report, report_size) != 0)
|
||||
{
|
||||
// HID ready + report sent, copy previous report
|
||||
if (tud_hid_ready() && tud_hid_report(0, report, report_size) == true ) {
|
||||
memcpy(last_report, report, report_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tud_hid_get_report_cb
|
||||
uint16_t HIDDriver::get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
memcpy(buffer, &hidReport, sizeof(HIDReport));
|
||||
return sizeof(HIDReport);
|
||||
}
|
||||
|
||||
// Only PS4 does anything with set report
|
||||
void HIDDriver::set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
|
||||
{
|
||||
}
|
||||
|
||||
// Only XboxOG and Xbox One use vendor control xfer cb
|
||||
bool HIDDriver::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t * HIDDriver::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
const char *value = (const char *)hid_string_descriptors[index];
|
||||
return getStringDescriptor(value, index); // getStringDescriptor returns a static array
|
||||
}
|
||||
|
||||
const uint8_t * HIDDriver::get_descriptor_device_cb()
|
||||
{
|
||||
return hid_device_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * HIDDriver::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return hid_report_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * HIDDriver::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return hid_configuration_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * HIDDriver::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16_t HIDDriver::GetJoystickMidValue()
|
||||
{
|
||||
return HID_JOYSTICK_MID << 8;
|
||||
}
|
||||
30
src/usbd/hid/HIDDriver.h
Normal file
30
src/usbd/hid/HIDDriver.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
*/
|
||||
|
||||
#ifndef _HID_DRIVER_H_
|
||||
#define _HID_DRIVER_H_
|
||||
|
||||
#include "usbd/gpdriver.h"
|
||||
#include "descriptors/HIDDescriptors.h"
|
||||
|
||||
class HIDDriver : public GPDriver {
|
||||
public:
|
||||
virtual void initialize();
|
||||
virtual void process(Gamepad * gamepad, uint8_t * outBuffer);
|
||||
virtual uint16_t get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen);
|
||||
virtual void set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize);
|
||||
virtual bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
virtual const uint16_t * get_descriptor_string_cb(uint8_t index, uint16_t langid);
|
||||
virtual const uint8_t * get_descriptor_device_cb();
|
||||
virtual const uint8_t * get_hid_descriptor_report_cb(uint8_t itf) ;
|
||||
virtual const uint8_t * get_descriptor_configuration_cb(uint8_t index);
|
||||
virtual const uint8_t * get_descriptor_device_qualifier_cb();
|
||||
virtual uint16_t GetJoystickMidValue();
|
||||
private:
|
||||
uint8_t last_report[CFG_TUD_ENDPOINT0_SIZE] = { };
|
||||
HIDReport hidReport;
|
||||
};
|
||||
|
||||
#endif // _HID_DRIVER_H_
|
||||
108
src/usbd/psclassic/PSClassicDriver.cpp
Normal file
108
src/usbd/psclassic/PSClassicDriver.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "usbd/psclassic/PSClassicDriver.h"
|
||||
#include "usbd/shared/driverhelper.h"
|
||||
|
||||
void PSClassicDriver::initialize() {
|
||||
psClassicReport = {
|
||||
.buttons = 0x0014
|
||||
};
|
||||
|
||||
class_driver = {
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "PSCLASSIC",
|
||||
#endif
|
||||
.init = hidd_init,
|
||||
.reset = hidd_reset,
|
||||
.open = hidd_open,
|
||||
.control_xfer_cb = hidd_control_xfer_cb,
|
||||
.xfer_cb = hidd_xfer_cb,
|
||||
.sof = NULL
|
||||
};
|
||||
}
|
||||
|
||||
void PSClassicDriver::process(Gamepad * gamepad, uint8_t * outBuffer) {
|
||||
psClassicReport.buttons = PSCLASSIC_MASK_CENTER;
|
||||
|
||||
if (gamepad->state.up) {
|
||||
if (gamepad->state.right)
|
||||
psClassicReport.buttons = PSCLASSIC_MASK_UP_RIGHT;
|
||||
else if (gamepad->state.left)
|
||||
psClassicReport.buttons = PSCLASSIC_MASK_UP_LEFT;
|
||||
else
|
||||
psClassicReport.buttons = PSCLASSIC_MASK_UP;
|
||||
} else if (gamepad->state.down) {
|
||||
if (gamepad->state.right)
|
||||
psClassicReport.buttons = PSCLASSIC_MASK_DOWN_RIGHT;
|
||||
else if (gamepad->state.left)
|
||||
psClassicReport.buttons = PSCLASSIC_MASK_DOWN_LEFT;
|
||||
else
|
||||
psClassicReport.buttons = PSCLASSIC_MASK_DOWN;
|
||||
} else if (gamepad->state.left) {
|
||||
psClassicReport.buttons = PSCLASSIC_MASK_LEFT;
|
||||
} else if (gamepad->state.right) {
|
||||
psClassicReport.buttons = PSCLASSIC_MASK_RIGHT;
|
||||
}
|
||||
|
||||
psClassicReport.buttons |=
|
||||
(gamepad->state.start ? PSCLASSIC_MASK_START : 0) |
|
||||
(gamepad->state.back ? PSCLASSIC_MASK_SELECT : 0) |
|
||||
(gamepad->state.a ? PSCLASSIC_MASK_CROSS : 0) |
|
||||
(gamepad->state.b ? PSCLASSIC_MASK_CIRCLE : 0) |
|
||||
(gamepad->state.x ? PSCLASSIC_MASK_SQUARE : 0) |
|
||||
(gamepad->state.y ? PSCLASSIC_MASK_TRIANGLE : 0) |
|
||||
(gamepad->state.lb ? PSCLASSIC_MASK_L1 : 0) |
|
||||
(gamepad->state.rb ? PSCLASSIC_MASK_R1 : 0) |
|
||||
(gamepad->state.lt ? PSCLASSIC_MASK_L2 : 0) |
|
||||
(gamepad->state.rt ? PSCLASSIC_MASK_R2 : 0);
|
||||
|
||||
// Wake up TinyUSB device
|
||||
if (tud_suspended())
|
||||
tud_remote_wakeup();
|
||||
|
||||
void * report = &psClassicReport;
|
||||
uint16_t report_size = sizeof(psClassicReport);
|
||||
if (memcmp(last_report, report, report_size) != 0) {
|
||||
// HID ready + report sent, copy previous report
|
||||
if (tud_hid_ready() && tud_hid_report(0, report, report_size) == true ) {
|
||||
memcpy(last_report, report, report_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tud_hid_get_report_cb
|
||||
uint16_t PSClassicDriver::get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
|
||||
memcpy(buffer, &psClassicReport, sizeof(PSClassicReport));
|
||||
return sizeof(PSClassicReport);
|
||||
}
|
||||
|
||||
// Only PS4 does anything with set report
|
||||
void PSClassicDriver::set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
|
||||
|
||||
// Only XboxOG and Xbox One use vendor control xfer cb
|
||||
bool PSClassicDriver::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t * PSClassicDriver::get_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
const char *value = (const char *)psclassic_string_descriptors[index];
|
||||
return getStringDescriptor(value, index); // getStringDescriptor returns a static array
|
||||
}
|
||||
|
||||
const uint8_t * PSClassicDriver::get_descriptor_device_cb() {
|
||||
return psclassic_device_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * PSClassicDriver::get_hid_descriptor_report_cb(uint8_t itf) {
|
||||
return psclassic_report_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * PSClassicDriver::get_descriptor_configuration_cb(uint8_t index) {
|
||||
return psclassic_configuration_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * PSClassicDriver::get_descriptor_device_qualifier_cb() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16_t PSClassicDriver::GetJoystickMidValue() {
|
||||
return 0;
|
||||
}
|
||||
30
src/usbd/psclassic/PSClassicDriver.h
Normal file
30
src/usbd/psclassic/PSClassicDriver.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
*/
|
||||
|
||||
#ifndef _PSCLASSIC_DRIVER_H_
|
||||
#define _PSCLASSIC_DRIVER_H_
|
||||
|
||||
#include "usbd/gpdriver.h"
|
||||
#include "descriptors/PSClassicDescriptors.h"
|
||||
|
||||
class PSClassicDriver : public GPDriver {
|
||||
public:
|
||||
virtual void initialize();
|
||||
virtual void process(Gamepad * gamepad, uint8_t * outBuffer);
|
||||
virtual uint16_t get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen);
|
||||
virtual void set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize);
|
||||
virtual bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
virtual const uint16_t * get_descriptor_string_cb(uint8_t index, uint16_t langid);
|
||||
virtual const uint8_t * get_descriptor_device_cb();
|
||||
virtual const uint8_t * get_hid_descriptor_report_cb(uint8_t itf) ;
|
||||
virtual const uint8_t * get_descriptor_configuration_cb(uint8_t index);
|
||||
virtual const uint8_t * get_descriptor_device_qualifier_cb();
|
||||
virtual uint16_t GetJoystickMidValue();
|
||||
private:
|
||||
uint8_t last_report[CFG_TUD_ENDPOINT0_SIZE] = { };
|
||||
PSClassicReport psClassicReport;
|
||||
};
|
||||
|
||||
#endif // _PSCLASSIC_DRIVER_H_
|
||||
26
src/usbd/shared/driverhelper.h
Normal file
26
src/usbd/shared/driverhelper.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef _DRIVER_HELPER_H_
|
||||
#define _DRIVER_HELPER_H_
|
||||
|
||||
static uint16_t * getStringDescriptor(const char * value, uint8_t index)
|
||||
{
|
||||
static uint16_t descriptorStringBuffer[32]; // Max 64 bytes, 31 unicode characters
|
||||
size_t charCount;
|
||||
if ( index == 0 ) // language always has a character count of 1
|
||||
charCount = 1;
|
||||
else {
|
||||
charCount = strlen(value);
|
||||
if (charCount > 31)
|
||||
charCount = 31;
|
||||
}
|
||||
// Fill descriptionStringBuffer[1] .. [32]
|
||||
for (uint8_t i = 0; i < charCount; i++)
|
||||
descriptorStringBuffer[i + 1] = value[i];
|
||||
|
||||
// first byte (descriptionStringBuffer[0]) is length (including header), second byte is string type
|
||||
descriptorStringBuffer[0] = (0x03 << 8) | (2 * (uint8_t)charCount + 2);
|
||||
|
||||
// Cast temp buffer to final result
|
||||
return descriptorStringBuffer;
|
||||
}
|
||||
|
||||
#endif // _DRIVER_HELPER_H_
|
||||
151
src/usbd/switch/SwitchDriver.cpp
Normal file
151
src/usbd/switch/SwitchDriver.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "usbd/switch/SwitchDriver.h"
|
||||
#include "usbd/shared/driverhelper.h"
|
||||
|
||||
#include "utilities/scaling.h"
|
||||
|
||||
void SwitchDriver::initialize() {
|
||||
switchReport = {
|
||||
.buttons = 0,
|
||||
.hat = SWITCH_HAT_NOTHING,
|
||||
.lx = SWITCH_JOYSTICK_MID,
|
||||
.ly = SWITCH_JOYSTICK_MID,
|
||||
.rx = SWITCH_JOYSTICK_MID,
|
||||
.ry = SWITCH_JOYSTICK_MID,
|
||||
.vendor = 0,
|
||||
};
|
||||
|
||||
class_driver = {
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "SWITCH",
|
||||
#endif
|
||||
.init = hidd_init,
|
||||
.reset = hidd_reset,
|
||||
.open = hidd_open,
|
||||
.control_xfer_cb = hidd_control_xfer_cb,
|
||||
.xfer_cb = hidd_xfer_cb,
|
||||
.sof = NULL
|
||||
};
|
||||
}
|
||||
|
||||
void SwitchDriver::process(Gamepad * gamepad, uint8_t * outBuffer)
|
||||
{
|
||||
switchReport.hat = SWITCH_HAT_NOTHING;
|
||||
|
||||
if (gamepad->state.up)
|
||||
{
|
||||
if (gamepad->state.right)
|
||||
{
|
||||
switchReport.hat = SWITCH_HAT_UPRIGHT;
|
||||
}
|
||||
else if (gamepad->state.left)
|
||||
{
|
||||
switchReport.hat = SWITCH_HAT_UPLEFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
switchReport.hat = SWITCH_HAT_UP;
|
||||
}
|
||||
}
|
||||
else if (gamepad->state.down)
|
||||
{
|
||||
if (gamepad->state.right)
|
||||
{
|
||||
switchReport.hat = SWITCH_HAT_DOWNRIGHT;
|
||||
}
|
||||
else if (gamepad->state.left)
|
||||
{
|
||||
switchReport.hat = SWITCH_HAT_DOWNLEFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
switchReport.hat = SWITCH_HAT_DOWN;
|
||||
}
|
||||
}
|
||||
else if (gamepad->state.left)
|
||||
{
|
||||
switchReport.hat = SWITCH_HAT_LEFT;
|
||||
}
|
||||
else if (gamepad->state.right)
|
||||
{
|
||||
switchReport.hat = SWITCH_HAT_RIGHT;
|
||||
}
|
||||
|
||||
switchReport.buttons = 0
|
||||
| (gamepad->state.a ? SWITCH_MASK_B : 0)
|
||||
| (gamepad->state.b ? SWITCH_MASK_A : 0)
|
||||
| (gamepad->state.x ? SWITCH_MASK_Y : 0)
|
||||
| (gamepad->state.y ? SWITCH_MASK_X : 0)
|
||||
| (gamepad->state.lb ? SWITCH_MASK_L : 0)
|
||||
| (gamepad->state.rb ? SWITCH_MASK_R : 0)
|
||||
| (gamepad->state.lt ? SWITCH_MASK_ZL : 0)
|
||||
| (gamepad->state.rt ? SWITCH_MASK_ZR : 0)
|
||||
| (gamepad->state.back ? SWITCH_MASK_MINUS : 0)
|
||||
| (gamepad->state.start ? SWITCH_MASK_PLUS : 0)
|
||||
| (gamepad->state.l3 ? SWITCH_MASK_L3 : 0)
|
||||
| (gamepad->state.r3 ? SWITCH_MASK_R3 : 0)
|
||||
| (gamepad->state.sys ? SWITCH_MASK_HOME : 0)
|
||||
| (gamepad->state.misc ? SWITCH_MASK_CAPTURE : 0)
|
||||
;
|
||||
|
||||
switchReport.lx = scale_int16_to_uint8(gamepad->state.lx, false);
|
||||
switchReport.ly = scale_int16_to_uint8(gamepad->state.ly, true);
|
||||
switchReport.rx = scale_int16_to_uint8(gamepad->state.rx, false);
|
||||
switchReport.ry = scale_int16_to_uint8(gamepad->state.ry, true);
|
||||
|
||||
// switchReport.lx = static_cast<uint8_t>((gamepad->state.lx + 32768) >> 8);
|
||||
// switchReport.ly = static_cast<uint8_t>(((-gamepad->state.ly - 1) + 32768) >> 8);
|
||||
// switchReport.rx = static_cast<uint8_t>((gamepad->state.rx + 32768) >> 8);
|
||||
// switchReport.ry = static_cast<uint8_t>(((-gamepad->state.ry - 1) + 32768) >> 8);
|
||||
|
||||
// Wake up TinyUSB device
|
||||
if (tud_suspended())
|
||||
tud_remote_wakeup();
|
||||
|
||||
void * report = &switchReport;
|
||||
uint16_t report_size = sizeof(switchReport);
|
||||
if (memcmp(last_report, report, report_size) != 0) {
|
||||
// HID ready + report sent, copy previous report
|
||||
if (tud_hid_ready() && tud_hid_report(0, report, report_size) == true ) {
|
||||
memcpy(last_report, report, report_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tud_hid_get_report_cb
|
||||
uint16_t SwitchDriver::get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
|
||||
memcpy(buffer, &switchReport, sizeof(SwitchReport));
|
||||
return sizeof(SwitchReport);
|
||||
}
|
||||
|
||||
// Only PS4 does anything with set report
|
||||
void SwitchDriver::set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
|
||||
|
||||
// Only XboxOG and Xbox One use vendor control xfer cb
|
||||
bool SwitchDriver::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t * SwitchDriver::get_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
const char *value = (const char *)switch_string_descriptors[index];
|
||||
return getStringDescriptor(value, index); // getStringDescriptor returns a static array
|
||||
}
|
||||
|
||||
const uint8_t * SwitchDriver::get_descriptor_device_cb() {
|
||||
return switch_device_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * SwitchDriver::get_hid_descriptor_report_cb(uint8_t itf) {
|
||||
return switch_report_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * SwitchDriver::get_descriptor_configuration_cb(uint8_t index) {
|
||||
return switch_configuration_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * SwitchDriver::get_descriptor_device_qualifier_cb() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16_t SwitchDriver::GetJoystickMidValue() {
|
||||
return SWITCH_JOYSTICK_MID << 8;
|
||||
}
|
||||
30
src/usbd/switch/SwitchDriver.h
Normal file
30
src/usbd/switch/SwitchDriver.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
*/
|
||||
|
||||
#ifndef _SWITCH_DRIVER_H_
|
||||
#define _SWITCH_DRIVER_H_
|
||||
|
||||
#include "usbd/gpdriver.h"
|
||||
#include "descriptors/SwitchDescriptors.h"
|
||||
|
||||
class SwitchDriver : public GPDriver {
|
||||
public:
|
||||
virtual void initialize();
|
||||
virtual void process(Gamepad * gamepad, uint8_t * outBuffer);
|
||||
virtual uint16_t get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen);
|
||||
virtual void set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize);
|
||||
virtual bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
virtual const uint16_t * get_descriptor_string_cb(uint8_t index, uint16_t langid);
|
||||
virtual const uint8_t * get_descriptor_device_cb();
|
||||
virtual const uint8_t * get_hid_descriptor_report_cb(uint8_t itf) ;
|
||||
virtual const uint8_t * get_descriptor_configuration_cb(uint8_t index);
|
||||
virtual const uint8_t * get_descriptor_device_qualifier_cb();
|
||||
virtual uint16_t GetJoystickMidValue();
|
||||
private:
|
||||
uint8_t last_report[CFG_TUD_ENDPOINT0_SIZE] = { };
|
||||
SwitchReport switchReport;
|
||||
};
|
||||
|
||||
#endif // _SWITCH_DRIVER_H_
|
||||
114
src/usbd/usbdriver.cpp
Normal file
114
src/usbd/usbdriver.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 Open Stick Community (gp2040-ce.info)
|
||||
*/
|
||||
|
||||
#ifndef _USBDRIVER_CPP_
|
||||
#define _USBDRIVER_CPP_
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#include "usbd/drivermanager.h"
|
||||
|
||||
static bool usb_mounted;
|
||||
static bool usb_suspended;
|
||||
|
||||
bool get_usb_mounted(void)
|
||||
{
|
||||
return usb_mounted;
|
||||
}
|
||||
|
||||
bool get_usb_suspended(void)
|
||||
{
|
||||
return usb_suspended;
|
||||
}
|
||||
|
||||
const usbd_class_driver_t *usbd_app_driver_get_cb(uint8_t *driver_count)
|
||||
{
|
||||
*driver_count = 1;
|
||||
return DriverManager::getInstance().getDriver()->get_class_driver();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return DriverManager::getInstance().getDriver()->get_report(report_id, report_type, buffer, reqlen);
|
||||
}
|
||||
|
||||
// Invoked when received SET_REPORT control request or
|
||||
// received data on OUT endpoint ( Report ID = 0, Type = 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)
|
||||
{
|
||||
DriverManager::getInstance().getDriver()->set_report(report_id, report_type, buffer, bufsize);
|
||||
tud_hid_report(report_id, buffer, bufsize); // echo back anything we received from host
|
||||
}
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
usb_mounted = true;
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void)
|
||||
{
|
||||
usb_mounted = false;
|
||||
}
|
||||
|
||||
// Invoked when usb bus is suspended
|
||||
// remote_wakeup_en : if host allow us to perform remote wakeup
|
||||
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
|
||||
void tud_suspend_cb(bool remote_wakeup_en)
|
||||
{
|
||||
(void)remote_wakeup_en;
|
||||
usb_suspended = true;
|
||||
}
|
||||
|
||||
// Invoked when usb bus is resumed
|
||||
void tud_resume_cb(void)
|
||||
{
|
||||
usb_suspended = false;
|
||||
}
|
||||
|
||||
// Vendor Controlled XFER occured
|
||||
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return DriverManager::getInstance().getDriver()->vendor_control_xfer_cb(rhport, stage, request);
|
||||
}
|
||||
|
||||
|
||||
// Invoked when received GET STRING DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
return DriverManager::getInstance().getDriver()->get_descriptor_string_cb(index, langid);
|
||||
}
|
||||
|
||||
// Invoked when received GET DEVICE DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const *tud_descriptor_device_cb()
|
||||
{
|
||||
return DriverManager::getInstance().getDriver()->get_descriptor_device_cb();
|
||||
}
|
||||
|
||||
// Invoked when received GET HID REPORT DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return DriverManager::getInstance().getDriver()->get_hid_descriptor_report_cb(itf);
|
||||
}
|
||||
|
||||
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return DriverManager::getInstance().getDriver()->get_descriptor_configuration_cb(index);
|
||||
}
|
||||
|
||||
uint8_t const* tud_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return DriverManager::getInstance().getDriver()->get_descriptor_device_qualifier_cb();
|
||||
}
|
||||
|
||||
#endif
|
||||
7
src/usbd/usbdriver.h
Normal file
7
src/usbd/usbdriver.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef _USB_DRIVER_H_
|
||||
#define _USB_DRIVER_H_
|
||||
|
||||
bool get_usb_mounted(void);
|
||||
bool get_usb_suspended(void);
|
||||
|
||||
#endif // #ifndef _USB_DRIVER_H_
|
||||
120
src/usbd/usbserial/USBSerialDriver.cpp
Normal file
120
src/usbd/usbserial/USBSerialDriver.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "stdint.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <tusb.h>
|
||||
#include "device/usbd_pvt.h"
|
||||
#include "class/cdc/cdc_device.h"
|
||||
|
||||
#include "utilities/log.h"
|
||||
|
||||
#include "usbd/usbserial/USBSerialDriver.h"
|
||||
#include "descriptors/USBSerialDescriptors.h"
|
||||
|
||||
void USBSerialDriver::initialize()
|
||||
{
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "UART",
|
||||
#endif
|
||||
class_driver = {
|
||||
.init = cdcd_init,
|
||||
.reset = cdcd_reset,
|
||||
.open = cdcd_open,
|
||||
.control_xfer_cb = cdcd_control_xfer_cb,
|
||||
.xfer_cb = cdcd_xfer_cb,
|
||||
.sof = NULL
|
||||
};
|
||||
}
|
||||
|
||||
void USBSerialDriver::process(Gamepad * gamepad, uint8_t * outbuffer)
|
||||
{
|
||||
uint32_t bytes_written = tud_cdc_write_str(get_log());
|
||||
|
||||
if (bytes_written == strlen(get_log()))
|
||||
{
|
||||
tud_cdc_write_flush();
|
||||
clear_log();
|
||||
};
|
||||
|
||||
sleep_ms(10);
|
||||
}
|
||||
|
||||
uint16_t USBSerialDriver::get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
return sizeof(buffer);
|
||||
}
|
||||
|
||||
void USBSerialDriver::set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
|
||||
{}
|
||||
|
||||
bool USBSerialDriver::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t * USBSerialDriver::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
(void) langid;
|
||||
size_t chr_count;
|
||||
|
||||
switch ( index )
|
||||
{
|
||||
case STRID_LANGID:
|
||||
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||
chr_count = 1;
|
||||
break;
|
||||
|
||||
case STRID_SERIAL:
|
||||
chr_count = board_usb_get_serial(_desc_str + 1, 32);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
|
||||
|
||||
if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
|
||||
|
||||
const char *str = string_desc_arr[index];
|
||||
|
||||
// Cap at max char
|
||||
chr_count = strlen(str);
|
||||
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
|
||||
if ( chr_count > max_count ) chr_count = max_count;
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for ( size_t i = 0; i < chr_count; i++ ) {
|
||||
_desc_str[1 + i] = str[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// first byte is length (including header), second byte is string type
|
||||
_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
|
||||
|
||||
return _desc_str;
|
||||
}
|
||||
|
||||
const uint8_t * USBSerialDriver::get_descriptor_device_cb()
|
||||
{
|
||||
return (uint8_t const *) &usbserial_device_descriptor;;
|
||||
}
|
||||
|
||||
const uint8_t * USBSerialDriver::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t * USBSerialDriver::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return usbserial_configuration_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * USBSerialDriver::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16_t USBSerialDriver::GetJoystickMidValue()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
22
src/usbd/usbserial/USBSerialDriver.h
Normal file
22
src/usbd/usbserial/USBSerialDriver.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef _UART_PASSTHROUGH_DRIVER_H_
|
||||
#define _UART_PASSTHROUGH_DRIVER_H_
|
||||
|
||||
#include "usbd/gpdriver.h"
|
||||
|
||||
class USBSerialDriver : public GPDriver {
|
||||
public:
|
||||
virtual void initialize();
|
||||
virtual void process(Gamepad * gamepad, uint8_t * outBuffer);
|
||||
virtual uint16_t get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen);
|
||||
virtual void set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize);
|
||||
virtual bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
virtual const uint16_t * get_descriptor_string_cb(uint8_t index, uint16_t langid);
|
||||
virtual const uint8_t * get_descriptor_device_cb();
|
||||
virtual const uint8_t * get_hid_descriptor_report_cb(uint8_t itf);
|
||||
virtual const uint8_t * get_descriptor_configuration_cb(uint8_t index);
|
||||
virtual const uint8_t * get_descriptor_device_qualifier_cb();
|
||||
virtual uint16_t GetJoystickMidValue();
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
120
src/usbd/xboxog/XboxOriginalDriver.cpp
Normal file
120
src/usbd/xboxog/XboxOriginalDriver.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "usbd/xboxog/XboxOriginalDriver.h"
|
||||
#include "usbd/xboxog/xid/xid.h"
|
||||
#include "usbd/shared/driverhelper.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
void xid_process_rumble_data(USB_XboxGamepad_OutReport_t xid_rumble_data)
|
||||
{
|
||||
gamepadOut.out_state.lrumble = xid_rumble_data.lValue >> 8;
|
||||
gamepadOut.out_state.rrumble = xid_rumble_data.rValue >> 8;
|
||||
}
|
||||
|
||||
void XboxOriginalDriver::initialize() {
|
||||
xboxOriginalReport = {
|
||||
.dButtons = 0,
|
||||
.A = 0,
|
||||
.B = 0,
|
||||
.X = 0,
|
||||
.Y = 0,
|
||||
.BLACK = 0,
|
||||
.WHITE = 0,
|
||||
.L = 0,
|
||||
.R = 0,
|
||||
.leftStickX = 0,
|
||||
.leftStickY = 0,
|
||||
.rightStickX = 0,
|
||||
.rightStickY = 0,
|
||||
};
|
||||
|
||||
// Copy XID driver to local class driver
|
||||
memcpy(&class_driver, xid_get_driver(), sizeof(usbd_class_driver_t));
|
||||
}
|
||||
|
||||
void XboxOriginalDriver::process(Gamepad * gamepad, uint8_t * outBuffer) {
|
||||
// digital buttons
|
||||
xboxOriginalReport.dButtons = 0
|
||||
| (gamepad->state.up ? XID_DUP : 0)
|
||||
| (gamepad->state.down ? XID_DDOWN : 0)
|
||||
| (gamepad->state.left ? XID_DLEFT : 0)
|
||||
| (gamepad->state.right ? XID_DRIGHT : 0)
|
||||
| (gamepad->state.start ? XID_START : 0)
|
||||
| (gamepad->state.back ? XID_BACK : 0)
|
||||
| (gamepad->state.l3 ? XID_LS : 0)
|
||||
| (gamepad->state.r3 ? XID_RS : 0)
|
||||
;
|
||||
|
||||
// analog buttons - convert to digital
|
||||
xboxOriginalReport.A = (gamepad->state.a ? 0xFF : 0);
|
||||
xboxOriginalReport.B = (gamepad->state.b ? 0xFF : 0);
|
||||
xboxOriginalReport.X = (gamepad->state.x ? 0xFF : 0);
|
||||
xboxOriginalReport.Y = (gamepad->state.y ? 0xFF : 0);
|
||||
xboxOriginalReport.BLACK = (gamepad->state.rb ? 0xFF : 0);
|
||||
xboxOriginalReport.WHITE = (gamepad->state.lb ? 0xFF : 0);
|
||||
|
||||
// analog triggers
|
||||
xboxOriginalReport.L = gamepad->state.lt;
|
||||
xboxOriginalReport.R = gamepad->state.rt;
|
||||
|
||||
// analog sticks
|
||||
xboxOriginalReport.leftStickX = gamepad->state.lx;
|
||||
xboxOriginalReport.leftStickY = gamepad->state.ly;
|
||||
xboxOriginalReport.rightStickX = gamepad->state.rx;
|
||||
xboxOriginalReport.rightStickY = gamepad->state.ry;
|
||||
|
||||
if (tud_suspended())
|
||||
tud_remote_wakeup();
|
||||
|
||||
uint8_t xIndex = xid_get_index_by_type(0, XID_TYPE_GAMECONTROLLER);
|
||||
if (memcmp(last_report, &xboxOriginalReport, sizeof(XboxOriginalReport)) != 0) {
|
||||
if ( xid_send_report(xIndex, &xboxOriginalReport, sizeof(XboxOriginalReport)) == true ) {
|
||||
memcpy(last_report, &xboxOriginalReport, sizeof(XboxOriginalReport));
|
||||
}
|
||||
}
|
||||
|
||||
USB_XboxGamepad_OutReport_t xpad_rumble_data;
|
||||
|
||||
if (xid_get_report(xIndex, &xpad_rumble_data, sizeof(xpad_rumble_data)))
|
||||
{
|
||||
xid_process_rumble_data(xpad_rumble_data);
|
||||
}
|
||||
}
|
||||
|
||||
// tud_hid_get_report_cb
|
||||
uint16_t XboxOriginalDriver::get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
|
||||
memcpy(buffer, &xboxOriginalReport, sizeof(XboxOriginalReport));
|
||||
return sizeof(XboxOriginalReport);
|
||||
}
|
||||
|
||||
// Only PS4 does anything with set report
|
||||
void XboxOriginalDriver::set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
|
||||
|
||||
// Only XboxOG and Xbox One use vendor control xfer cb
|
||||
bool XboxOriginalDriver::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
|
||||
return class_driver.control_xfer_cb(rhport, stage, request);
|
||||
}
|
||||
|
||||
const uint16_t * XboxOriginalDriver::get_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
const char *value = (const char *)xboxoriginal_string_descriptors[index];
|
||||
return getStringDescriptor(value, index); // getStringDescriptor returns a static array
|
||||
}
|
||||
|
||||
const uint8_t * XboxOriginalDriver::get_descriptor_device_cb() {
|
||||
return xboxoriginal_device_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * XboxOriginalDriver::get_hid_descriptor_report_cb(uint8_t itf) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t * XboxOriginalDriver::get_descriptor_configuration_cb(uint8_t index) {
|
||||
return xboxoriginal_configuration_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * XboxOriginalDriver::get_descriptor_device_qualifier_cb() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16_t XboxOriginalDriver::GetJoystickMidValue() {
|
||||
return 0;
|
||||
}
|
||||
30
src/usbd/xboxog/XboxOriginalDriver.h
Normal file
30
src/usbd/xboxog/XboxOriginalDriver.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
*/
|
||||
|
||||
#ifndef _XBOX_ORIGINAL_DRIVER_H_
|
||||
#define _XBOX_ORIGINAL_DRIVER_H_
|
||||
|
||||
#include "usbd/gpdriver.h"
|
||||
#include "descriptors/XboxOriginalDescriptors.h"
|
||||
|
||||
class XboxOriginalDriver : public GPDriver {
|
||||
public:
|
||||
virtual void initialize();
|
||||
virtual void process(Gamepad * gamepad, uint8_t * outBuffer);
|
||||
virtual uint16_t get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen);
|
||||
virtual void set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize);
|
||||
virtual bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
virtual const uint16_t * get_descriptor_string_cb(uint8_t index, uint16_t langid);
|
||||
virtual const uint8_t * get_descriptor_device_cb();
|
||||
virtual const uint8_t * get_hid_descriptor_report_cb(uint8_t itf) ;
|
||||
virtual const uint8_t * get_descriptor_configuration_cb(uint8_t index);
|
||||
virtual const uint8_t * get_descriptor_device_qualifier_cb();
|
||||
virtual uint16_t GetJoystickMidValue();
|
||||
private:
|
||||
uint8_t last_report[CFG_TUD_ENDPOINT0_SIZE] = { };
|
||||
XboxOriginalReport xboxOriginalReport;
|
||||
};
|
||||
|
||||
#endif // _XBOX_ORIGINAL_DRIVER_H_
|
||||
21
src/usbd/xboxog/xid/LICENSE
Normal file
21
src/usbd/xboxog/xid/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Ryzee119
|
||||
|
||||
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.
|
||||
257
src/usbd/xboxog/xid/xid.c
Normal file
257
src/usbd/xboxog/xid/xid.c
Normal file
@@ -0,0 +1,257 @@
|
||||
#include "usbd/xboxog/xid/xid.h"
|
||||
|
||||
bool duke_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, xid_interface_t *p_xid);
|
||||
bool steelbattalion_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, xid_interface_t *p_xid);
|
||||
bool xremote_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, xid_interface_t *p_xid);
|
||||
|
||||
#define MAX_XIDS (XID_DUKE + XID_STEELBATTALION + XID_XREMOTE)
|
||||
|
||||
CFG_TUSB_MEM_SECTION static xid_interface_t _xid_itf[MAX_XIDS];
|
||||
|
||||
static inline int8_t get_index_by_itfnum(uint8_t itf_num)
|
||||
{
|
||||
for (uint8_t i = 0; i < MAX_XIDS; i++)
|
||||
{
|
||||
if (itf_num == _xid_itf[i].itf_num)
|
||||
return i;
|
||||
//Xremote has two interfaces. Handle is separately.
|
||||
if (_xid_itf[i].type == XID_TYPE_XREMOTE)
|
||||
{
|
||||
if (itf_num == _xid_itf[i].itf_num + 1)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int8_t get_index_by_ep_addr(uint8_t ep_addr)
|
||||
{
|
||||
for (uint8_t i = 0; i < MAX_XIDS; i++)
|
||||
{
|
||||
if (ep_addr == _xid_itf[i].ep_in)
|
||||
return i;
|
||||
|
||||
if (ep_addr == _xid_itf[i].ep_out)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline xid_interface_t *find_available_interface()
|
||||
{
|
||||
for (uint8_t i = 0; i < MAX_XIDS; i++)
|
||||
{
|
||||
if (_xid_itf[i].ep_in == 0)
|
||||
return &_xid_itf[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void xid_init(void)
|
||||
{
|
||||
|
||||
//#define MAX_XIDS (XID_DUKE + XID_STEELBATTALION + XID_XREMOTE)
|
||||
tu_memclr(_xid_itf, sizeof(_xid_itf));
|
||||
for (uint8_t i = 0; i < MAX_XIDS; i++)
|
||||
{
|
||||
_xid_itf[i].type = (i < (XID_DUKE)) ? XID_TYPE_GAMECONTROLLER :
|
||||
(i < (XID_DUKE + XID_STEELBATTALION)) ? XID_TYPE_STEELBATTALION :
|
||||
(i < (XID_DUKE + XID_STEELBATTALION + XID_XREMOTE)) ? XID_TYPE_XREMOTE : 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
static void xid_reset(uint8_t rhport)
|
||||
{
|
||||
xid_init();
|
||||
}
|
||||
|
||||
static uint16_t xid_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
|
||||
{
|
||||
TU_VERIFY(itf_desc->bInterfaceClass == XID_INTERFACE_CLASS, 0);
|
||||
TU_VERIFY(itf_desc->bInterfaceSubClass == XID_INTERFACE_SUBCLASS, 0);
|
||||
|
||||
xid_interface_t *p_xid = find_available_interface();
|
||||
TU_ASSERT(p_xid != NULL, 0);
|
||||
|
||||
uint16_t const drv_len = (p_xid->type == XID_TYPE_GAMECONTROLLER) ? TUD_XID_DUKE_DESC_LEN :
|
||||
(p_xid->type == XID_TYPE_STEELBATTALION) ? TUD_XID_SB_DESC_LEN :
|
||||
(p_xid->type == XID_TYPE_XREMOTE) ? TUD_XID_XREMOTE_DESC_LEN : 0;
|
||||
TU_ASSERT(max_len >= drv_len, 0);
|
||||
|
||||
p_xid->itf_num = itf_desc->bInterfaceNumber;
|
||||
|
||||
tusb_desc_endpoint_t *ep_desc;
|
||||
ep_desc = (tusb_desc_endpoint_t *)tu_desc_next(itf_desc);
|
||||
if (tu_desc_type(ep_desc) == TUSB_DESC_ENDPOINT)
|
||||
{
|
||||
usbd_edpt_open(rhport, ep_desc);
|
||||
(ep_desc->bEndpointAddress & 0x80) ? (p_xid->ep_in = ep_desc->bEndpointAddress) :
|
||||
(p_xid->ep_out = ep_desc->bEndpointAddress);
|
||||
}
|
||||
|
||||
TU_VERIFY(itf_desc->bNumEndpoints >= 2, drv_len);
|
||||
ep_desc = (tusb_desc_endpoint_t *)tu_desc_next(ep_desc);
|
||||
if (tu_desc_type(ep_desc) == TUSB_DESC_ENDPOINT)
|
||||
{
|
||||
usbd_edpt_open(rhport, ep_desc);
|
||||
(ep_desc->bEndpointAddress & 0x80) ? (p_xid->ep_in = ep_desc->bEndpointAddress) :
|
||||
(p_xid->ep_out = ep_desc->bEndpointAddress);
|
||||
}
|
||||
|
||||
return drv_len;
|
||||
}
|
||||
|
||||
int8_t xid_get_index_by_type(uint8_t type_index, xid_type_t type)
|
||||
{
|
||||
uint8_t _type_index = 0;
|
||||
for (uint8_t i = 0; i < MAX_XIDS; i++)
|
||||
{
|
||||
if (_xid_itf[i].type == type)
|
||||
{
|
||||
if (_type_index == type_index)
|
||||
return i;
|
||||
_type_index++;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool xid_get_report(uint8_t index, void *report, uint16_t len)
|
||||
{
|
||||
TU_VERIFY(index < MAX_XIDS, false);
|
||||
TU_VERIFY(_xid_itf[index].ep_out != 0, false);
|
||||
TU_VERIFY(len < XID_MAX_PACKET_SIZE, false);
|
||||
|
||||
memcpy(report, _xid_itf[index].out, len);
|
||||
|
||||
//Queue request on out endpoint
|
||||
//Most games send to control pipe, but some send to out pipe. THPSX2 atleast
|
||||
if (tud_ready() && !usbd_edpt_busy(TUD_OPT_RHPORT, _xid_itf[index].ep_out))
|
||||
{
|
||||
usbd_edpt_xfer(TUD_OPT_RHPORT, _xid_itf[index].ep_out, _xid_itf[index].ep_out_buff, len);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool xid_send_report_ready(uint8_t index)
|
||||
{
|
||||
TU_VERIFY(index < MAX_XIDS, false);
|
||||
TU_VERIFY(_xid_itf[index].ep_in != 0, false);
|
||||
return (tud_ready() && !usbd_edpt_busy(TUD_OPT_RHPORT, _xid_itf[index].ep_in));
|
||||
}
|
||||
bool xid_send_report(uint8_t index, void *report, uint16_t len)
|
||||
{
|
||||
TU_VERIFY(len < XID_MAX_PACKET_SIZE, false);
|
||||
TU_VERIFY(index < MAX_XIDS, false);
|
||||
TU_VERIFY(_xid_itf[index].ep_in != 0, false);
|
||||
TU_VERIFY(xid_send_report_ready(index), false);
|
||||
|
||||
if (tud_suspended())
|
||||
tud_remote_wakeup();
|
||||
|
||||
//Maintain a local copy of the report
|
||||
memcpy(_xid_itf[index].in, report, len);
|
||||
|
||||
//Send it to the host
|
||||
return usbd_edpt_xfer(TUD_OPT_RHPORT, _xid_itf[index].ep_in, _xid_itf[index].in, len);
|
||||
}
|
||||
|
||||
static bool xid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
(void)rhport;
|
||||
uint8_t index = get_index_by_ep_addr(ep_addr);
|
||||
|
||||
TU_VERIFY(result == XFER_RESULT_SUCCESS, true);
|
||||
TU_VERIFY(index != -1, true);
|
||||
TU_VERIFY(xferred_bytes < XID_MAX_PACKET_SIZE, true);
|
||||
|
||||
if (ep_addr == _xid_itf[index].ep_out)
|
||||
{
|
||||
memcpy(_xid_itf[index].out, _xid_itf[index].ep_out_buff, MIN(xferred_bytes, sizeof( _xid_itf[index].ep_out_buff)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool xid_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
|
||||
|
||||
uint8_t index = get_index_by_itfnum((uint8_t)request->wIndex);
|
||||
TU_VERIFY(index != -1, false);
|
||||
|
||||
bool ret = false;
|
||||
|
||||
//Get HID Report
|
||||
if (request->bmRequestType == 0xA1 && request->bRequest == 0x01 && request->wValue == 0x0100)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
TU_LOG1("Sending HID report on control pipe for index %02x\n", request->wIndex);
|
||||
tud_control_xfer(rhport, request, _xid_itf[index].in, MIN(request->wLength, sizeof(_xid_itf[index].in)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//Set HID Report
|
||||
if (request->bmRequestType == 0x21 && request->bRequest == 0x09 && request->wValue == 0x0200 && request->wLength == 0x06)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
//Host is sending a rumble command to control pipe. Queue receipt.
|
||||
tud_control_xfer(rhport, request, _xid_itf[index].ep_out_buff, MIN(request->wLength, sizeof(_xid_itf[index].ep_out_buff)));
|
||||
}
|
||||
else if (stage == CONTROL_STAGE_ACK)
|
||||
{
|
||||
//Receipt complete. Copy data to rumble struct
|
||||
TU_LOG1("Got HID report from control pipe for index %02x\n", request->wIndex);
|
||||
memcpy(_xid_itf[index].out, _xid_itf[index].ep_out_buff, MIN(request->wLength, sizeof(_xid_itf[index].out)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (_xid_itf[index].type)
|
||||
{
|
||||
case XID_TYPE_GAMECONTROLLER:
|
||||
ret = duke_control_xfer(rhport, stage, request, &_xid_itf[index]);
|
||||
break;
|
||||
case XID_TYPE_STEELBATTALION:
|
||||
ret = steelbattalion_control_xfer(rhport, stage, request, &_xid_itf[index]);
|
||||
break;
|
||||
case XID_TYPE_XREMOTE:
|
||||
ret = xremote_control_xfer(rhport, stage, request, &_xid_itf[index]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == false)
|
||||
{
|
||||
TU_LOG1("STALL: wIndex: %02x bmRequestType: %02x, bRequest: %02x, wValue: %04x\n",
|
||||
request->wIndex,
|
||||
request->bmRequestType,
|
||||
request->bRequest,
|
||||
request->wValue);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const usbd_class_driver_t xid_driver =
|
||||
{
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "XID DRIVER (DUKE,SB OR XREMOTE)",
|
||||
#endif
|
||||
.init = xid_init,
|
||||
.reset = xid_reset,
|
||||
.open = xid_open,
|
||||
.control_xfer_cb = xid_control_xfer_cb,
|
||||
.xfer_cb = xid_xfer_cb,
|
||||
.sof = NULL
|
||||
};
|
||||
|
||||
const usbd_class_driver_t *xid_get_driver()
|
||||
{
|
||||
return &xid_driver;
|
||||
}
|
||||
54
src/usbd/xboxog/xid/xid.h
Normal file
54
src/usbd/xboxog/xid/xid.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef XID_H_
|
||||
#define XID_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tusb.h>
|
||||
#include <device/usbd_pvt.h>
|
||||
|
||||
#include "usbd/xboxog/xid/xid_gamepad.h"
|
||||
#include "usbd/xboxog/xid/xid_remote.h"
|
||||
#include "usbd/xboxog/xid/xid_steelbattalion.h"
|
||||
|
||||
#define XID_DUKE 1
|
||||
#define XID_STEELBATTALION 0
|
||||
#define XID_XREMOTE 0
|
||||
#define MSC_XMU 0
|
||||
|
||||
#define XID_INTERFACE_CLASS 0x58
|
||||
#define XID_INTERFACE_SUBCLASS 0x42
|
||||
#define XID_MAX_PACKET_SIZE 32
|
||||
|
||||
typedef enum
|
||||
{
|
||||
XID_TYPE_GAMECONTROLLER,
|
||||
XID_TYPE_STEELBATTALION,
|
||||
XID_TYPE_XREMOTE,
|
||||
} xid_type_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t itf_num;
|
||||
xid_type_t type;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out;
|
||||
CFG_TUSB_MEM_ALIGN uint8_t ep_out_buff[XID_MAX_PACKET_SIZE];
|
||||
CFG_TUSB_MEM_ALIGN uint8_t in[XID_MAX_PACKET_SIZE];
|
||||
CFG_TUSB_MEM_ALIGN uint8_t out[XID_MAX_PACKET_SIZE];
|
||||
} xid_interface_t;
|
||||
|
||||
int8_t xid_get_index_by_type(uint8_t type_index, xid_type_t type);
|
||||
bool xid_get_report(uint8_t index, void *report, uint16_t len);
|
||||
bool xid_send_report_ready(uint8_t index);
|
||||
bool xid_send_report(uint8_t index, void *report, uint16_t len);
|
||||
const usbd_class_driver_t *xid_get_driver();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //XID_H_
|
||||
6
src/usbd/xboxog/xid/xid_driver.c
Normal file
6
src/usbd/xboxog/xid/xid_driver.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "usbd/xboxog/xid/xid.h"
|
||||
|
||||
uint8_t *xremote_get_rom()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
87
src/usbd/xboxog/xid/xid_driver.h
Normal file
87
src/usbd/xboxog/xid/xid_driver.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#ifndef XID_DRIVER_H_
|
||||
#define XID_DRIVER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tusb.h>
|
||||
#include <device/usbd_pvt.h>
|
||||
#include "usbd/xboxog/xid/xid.h"
|
||||
|
||||
static const tusb_desc_device_t XID_DESC_DEVICE =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0110,
|
||||
.bDeviceClass = 0x00,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = 0x045E,
|
||||
.idProduct = 0x0289,
|
||||
.bcdDevice = 0x0121,
|
||||
|
||||
.iManufacturer = 0x00,
|
||||
.iProduct = 0x00,
|
||||
.iSerialNumber = 0x00,
|
||||
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
#if (XID_DUKE >= 1)
|
||||
ITF_NUM_XID_DUKE,
|
||||
#endif
|
||||
#if (XID_STEELBATTALION >= 1)
|
||||
ITF_NUM_XID_STEELBATTALION,
|
||||
#endif
|
||||
#if (XID_XREMOTE >= 1)
|
||||
ITF_NUM_XID_XREMOTE,
|
||||
ITF_NUM_XID_XREMOTE_ROM,
|
||||
#endif
|
||||
#if (MSC_XMU >= 1)
|
||||
ITF_NUM_MSC,
|
||||
#endif
|
||||
XID_ITF_NUM_TOTAL
|
||||
};
|
||||
|
||||
#define XID_CONFIG_TOTAL_LEN \
|
||||
(TUD_CONFIG_DESC_LEN) + \
|
||||
(TUD_XID_DUKE_DESC_LEN * XID_DUKE) + \
|
||||
(TUD_XID_SB_DESC_LEN * XID_STEELBATTALION) + \
|
||||
(TUD_XID_XREMOTE_DESC_LEN * XID_XREMOTE) + \
|
||||
(TUD_MSC_DESC_LEN * MSC_XMU)
|
||||
|
||||
static uint8_t const XID_DESC_CONFIGURATION[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, XID_ITF_NUM_TOTAL, 0, XID_CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 500),
|
||||
|
||||
#if (XID_DUKE >= 1)
|
||||
TUD_XID_DUKE_DESCRIPTOR(ITF_NUM_XID_DUKE, ITF_NUM_XID_DUKE + 1, 0x80 | (ITF_NUM_XID_DUKE + 1)),
|
||||
#endif
|
||||
|
||||
#if (XID_STEELBATTALION >= 1)
|
||||
TUD_XID_SB_DESCRIPTOR(ITF_NUM_XID_STEELBATTALION, ITF_NUM_XID_STEELBATTALION + 1, 0x80 | (ITF_NUM_XID_STEELBATTALION + 1)),
|
||||
#endif
|
||||
|
||||
#if (XID_XREMOTE >= 1)
|
||||
TUD_XID_XREMOTE_DESCRIPTOR(ITF_NUM_XID_XREMOTE, 0x80 | (ITF_NUM_XID_XREMOTE + 1)),
|
||||
#endif
|
||||
|
||||
#if (CFG_TUD_MSC >= 1)
|
||||
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, ITF_NUM_MSC + 1, 0x80 | (ITF_NUM_MSC + 1), 64),
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //XID_DRIVER_H_
|
||||
33
src/usbd/xboxog/xid/xid_gamepad.c
Normal file
33
src/usbd/xboxog/xid/xid_gamepad.c
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "usbd/xboxog/xid/xid_driver.h"
|
||||
|
||||
bool duke_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, xid_interface_t *p_xid)
|
||||
{
|
||||
if (request->bmRequestType == 0xC1 && request->bRequest == 0x06 && request->wValue == 0x4200)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
TU_LOG1("Sending DUKE_DESC_XID\n");
|
||||
tud_control_xfer(rhport, request, (void *)DUKE_DESC_XID, sizeof(DUKE_DESC_XID));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (request->bmRequestType == 0xC1 && request->bRequest == 0x01 && request->wValue == 0x0100)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
TU_LOG1("Sending DUKE_CAPABILITIES_IN\n");
|
||||
tud_control_xfer(rhport, request, (void *)DUKE_CAPABILITIES_IN, sizeof(DUKE_CAPABILITIES_IN));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (request->bmRequestType == 0xC1 && request->bRequest == 0x01 && request->wValue == 0x0200)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
TU_LOG1("Sending DUKE_CAPABILITIES_OUT\n");
|
||||
tud_control_xfer(rhport, request, (void *)DUKE_CAPABILITIES_OUT, sizeof(DUKE_CAPABILITIES_OUT));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
94
src/usbd/xboxog/xid/xid_gamepad.h
Normal file
94
src/usbd/xboxog/xid/xid_gamepad.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#ifndef XID_DUKE_H_
|
||||
#define XID_DUKE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tusb.h>
|
||||
|
||||
/* Digital Button Masks */
|
||||
#define XID_DUP (1 << 0)
|
||||
#define XID_DDOWN (1 << 1)
|
||||
#define XID_DLEFT (1 << 2)
|
||||
#define XID_DRIGHT (1 << 3)
|
||||
#define XID_START (1 << 4)
|
||||
#define XID_BACK (1 << 5)
|
||||
#define XID_LS (1 << 6)
|
||||
#define XID_RS (1 << 7)
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
uint8_t zero;
|
||||
uint8_t bLength;
|
||||
uint8_t dButtons;
|
||||
uint8_t reserved;
|
||||
uint8_t A;
|
||||
uint8_t B;
|
||||
uint8_t X;
|
||||
uint8_t Y;
|
||||
uint8_t BLACK;
|
||||
uint8_t WHITE;
|
||||
uint8_t L;
|
||||
uint8_t R;
|
||||
int16_t leftStickX;
|
||||
int16_t leftStickY;
|
||||
int16_t rightStickX;
|
||||
int16_t rightStickY;
|
||||
} USB_XboxGamepad_InReport_t;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
uint8_t zero;
|
||||
uint8_t bLength;
|
||||
uint16_t lValue;
|
||||
uint16_t rValue;
|
||||
} USB_XboxGamepad_OutReport_t;
|
||||
|
||||
#define TUD_XID_DUKE_DESC_LEN (9+7+7)
|
||||
|
||||
#define TUD_XID_DUKE_DESCRIPTOR(_itfnum, _epout, _epin) \
|
||||
/* Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, XID_INTERFACE_CLASS, XID_INTERFACE_SUBCLASS, 0x00, 0x00,\
|
||||
/* Endpoint In */\
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(32), 4, \
|
||||
/* Endpoint Out */\
|
||||
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(32), 4
|
||||
|
||||
static const uint8_t DUKE_DESC_XID[] = {
|
||||
0x10,
|
||||
0x42,
|
||||
0x00, 0x01,
|
||||
0x01,
|
||||
0x02,
|
||||
0x14,
|
||||
0x06,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
static const uint8_t DUKE_CAPABILITIES_IN[] = {
|
||||
0x00,
|
||||
0x14,
|
||||
0xFF,
|
||||
0x00,
|
||||
0xFF,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
static const uint8_t DUKE_CAPABILITIES_OUT[] = {
|
||||
0x00,
|
||||
0x06,
|
||||
0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
47
src/usbd/xboxog/xid/xid_remote.c
Normal file
47
src/usbd/xboxog/xid/xid_remote.c
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "usbd/xboxog/xid/xid_driver.h"
|
||||
|
||||
uint8_t *xremote_get_rom();
|
||||
|
||||
bool xremote_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, xid_interface_t *p_xid)
|
||||
{
|
||||
if (request->bmRequestType == 0xC1 && request->bRequest == 0x06 && request->wValue == 0x4200)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
TU_LOG1("Sending REMOTE_DESC_XID\r\n");
|
||||
tud_control_xfer(rhport, request, (void *)REMOTE_DESC_XID, sizeof(REMOTE_DESC_XID));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//INFO PACKET (Interface 1)
|
||||
else if (request->bmRequestType == 0xC1 && request->bRequest == 0x01 && request->wIndex == 1 && request->wValue == 0x0000)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
TU_LOG1("Sending XREMOTE INFO\r\n");
|
||||
uint8_t *rom = xremote_get_rom();
|
||||
if (rom == NULL)
|
||||
{
|
||||
return false; //STALL
|
||||
}
|
||||
tud_control_xfer(rhport, request, &rom[0], request->wLength);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//ROM DATA (Interface 1)
|
||||
else if (request->bmRequestType == 0xC1 && request->bRequest == 0x02 && request->wIndex == 1)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
uint8_t *rom = xremote_get_rom();
|
||||
if (rom == NULL)
|
||||
{
|
||||
return false; //STALL
|
||||
}
|
||||
tud_control_xfer(rhport, request, &rom[request->wValue * 1024], request->wLength);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
77
src/usbd/xboxog/xid/xid_remote.h
Normal file
77
src/usbd/xboxog/xid/xid_remote.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef XID_REMOTE_H_
|
||||
#define XID_REMOTE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tusb.h>
|
||||
|
||||
#define XID_XREMOTE_ROM_CLASS 0x59
|
||||
|
||||
typedef enum
|
||||
{
|
||||
XREMOTE_SELECT = 0x0A0B,
|
||||
XREMOTE_UP = 0x0AA6,
|
||||
XREMOTE_DOWN = 0x0AA7,
|
||||
XREMOTE_RIGHT = 0x0AA8,
|
||||
XREMOTE_LEFT = 0x0AA9,
|
||||
XREMOTE_INFO = 0x0AC3,
|
||||
XREMOTE_NINE = 0x0AC6,
|
||||
XREMOTE_EIGHT = 0x0AC7,
|
||||
XREMOTE_SEVEN = 0x0AC8,
|
||||
XREMOTE_SIX = 0x0AC9,
|
||||
XREMOTE_FIVE = 0x0ACA,
|
||||
XREMOTE_FOUR = 0x0ACB,
|
||||
XREMOTE_THREE = 0x0ACC,
|
||||
XREMOTE_TWO = 0x0ACD,
|
||||
XREMOTE_ONE = 0x0ACE,
|
||||
XREMOTE_ZERO = 0x0ACF,
|
||||
XREMOTE_DISPLAY = 0x0AD5,
|
||||
XREMOTE_BACK = 0x0AD8,
|
||||
XREMOTE_SKIP_MINUS = 0x0ADD,
|
||||
XREMOTE_SKIP_PLUS = 0x0ADF,
|
||||
XREMOTE_STOP = 0x0AE0,
|
||||
XREMOTE_REVERSE = 0x0AE2,
|
||||
XREMOTE_FORWARD = 0x0AE3,
|
||||
XREMOTE_TITLE = 0x0AE5,
|
||||
XREMOTE_PAUSE = 0x0AE6,
|
||||
XREMOTE_PLAY = 0x0AEA,
|
||||
XREMOTE_MENU = 0x0AF7,
|
||||
} xremote_buttoncode_t;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
uint8_t zero;
|
||||
uint8_t bLength;
|
||||
xremote_buttoncode_t buttonCode;
|
||||
uint16_t timeElapsed; //ms since last button press
|
||||
} USB_XboxRemote_InReport_t;
|
||||
|
||||
#define TUD_XID_XREMOTE_DESC_LEN (9+7+9)
|
||||
|
||||
#define TUD_XID_XREMOTE_DESCRIPTOR(_itfnum, _epin) \
|
||||
/* Interface 0 (HID DATA)*/\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, XID_INTERFACE_CLASS, XID_INTERFACE_SUBCLASS, 0x00, 0x00,\
|
||||
/* Endpoint In */\
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(8), 16, \
|
||||
/* Interface 1 (ROM DATA)*/\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum + 1, 0, 0, XID_XREMOTE_ROM_CLASS, 0x00, 0x00, 0x00
|
||||
|
||||
static const uint8_t REMOTE_DESC_XID[] = {
|
||||
0x08,
|
||||
0x42,
|
||||
0x00, 0x01,
|
||||
0x03,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
33
src/usbd/xboxog/xid/xid_steelbattalion.c
Normal file
33
src/usbd/xboxog/xid/xid_steelbattalion.c
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "usbd/xboxog/xid/xid_driver.h"
|
||||
|
||||
bool steelbattalion_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, xid_interface_t *p_xid)
|
||||
{
|
||||
if (request->bmRequestType == 0xC1 && request->bRequest == 0x06 && request->wValue == 0x4200)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
TU_LOG1("Sending STEELBATTALION_DESC_XID\n");
|
||||
tud_control_xfer(rhport, request, (void *)STEELBATTALION_DESC_XID, sizeof(STEELBATTALION_DESC_XID));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (request->bmRequestType == 0xC1 && request->bRequest == 0x01 && request->wValue == 0x0100)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
TU_LOG1("Sending STEELBATTALION_CAPABILITIES_IN\n");
|
||||
tud_control_xfer(rhport, request, (void *)STEELBATTALION_CAPABILITIES_IN, sizeof(STEELBATTALION_CAPABILITIES_IN));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (request->bmRequestType == 0xC1 && request->bRequest == 0x01 && request->wValue == 0x0200)
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
TU_LOG1("Sending STEELBATTALION_CAPABILITIES_OUT\n");
|
||||
tud_control_xfer(rhport, request, (void *)STEELBATTALION_CAPABILITIES_OUT, sizeof(STEELBATTALION_CAPABILITIES_OUT));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
155
src/usbd/xboxog/xid/xid_steelbattalion.h
Normal file
155
src/usbd/xboxog/xid/xid_steelbattalion.h
Normal file
@@ -0,0 +1,155 @@
|
||||
#ifndef XID_STEELBATTALION_H_
|
||||
#define XID_STEELBATTALION_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tusb.h>
|
||||
|
||||
/* https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/blob/master/src/core/hle/XAPI/XapiCxbxr.h */
|
||||
#define CXBX_SBC_GAMEPAD_W0_RIGHTJOYMAINWEAPON 0x0001
|
||||
#define CXBX_SBC_GAMEPAD_W0_RIGHTJOYFIRE 0x0002
|
||||
#define CXBX_SBC_GAMEPAD_W0_RIGHTJOYLOCKON 0x0004
|
||||
#define CXBX_SBC_GAMEPAD_W0_EJECT 0x0008
|
||||
#define CXBX_SBC_GAMEPAD_W0_COCKPITHATCH 0x0010
|
||||
#define CXBX_SBC_GAMEPAD_W0_IGNITION 0x0020
|
||||
#define CXBX_SBC_GAMEPAD_W0_START 0x0040
|
||||
#define CXBX_SBC_GAMEPAD_W0_MULTIMONOPENCLOSE 0x0080
|
||||
#define CXBX_SBC_GAMEPAD_W0_MULTIMONMAPZOOMINOUT 0x0100
|
||||
#define CXBX_SBC_GAMEPAD_W0_MULTIMONMODESELECT 0x0200
|
||||
#define CXBX_SBC_GAMEPAD_W0_MULTIMONSUBMONITOR 0x0400
|
||||
#define CXBX_SBC_GAMEPAD_W0_MAINMONZOOMIN 0x0800
|
||||
#define CXBX_SBC_GAMEPAD_W0_MAINMONZOOMOUT 0x1000
|
||||
#define CXBX_SBC_GAMEPAD_W0_FUNCTIONFSS 0x2000
|
||||
#define CXBX_SBC_GAMEPAD_W0_FUNCTIONMANIPULATOR 0x4000
|
||||
#define CXBX_SBC_GAMEPAD_W0_FUNCTIONLINECOLORCHANGE 0x8000
|
||||
|
||||
#define CXBX_SBC_GAMEPAD_W1_WASHING 0x0001
|
||||
#define CXBX_SBC_GAMEPAD_W1_EXTINGUISHER 0x0002
|
||||
#define CXBX_SBC_GAMEPAD_W1_CHAFF 0x0004
|
||||
#define CXBX_SBC_GAMEPAD_W1_FUNCTIONTANKDETACH 0x0008
|
||||
#define CXBX_SBC_GAMEPAD_W1_FUNCTIONOVERRIDE 0x0010
|
||||
#define CXBX_SBC_GAMEPAD_W1_FUNCTIONNIGHTSCOPE 0x0020
|
||||
#define CXBX_SBC_GAMEPAD_W1_FUNCTIONF1 0x0040
|
||||
#define CXBX_SBC_GAMEPAD_W1_FUNCTIONF2 0x0080
|
||||
#define CXBX_SBC_GAMEPAD_W1_FUNCTIONF3 0x0100
|
||||
#define CXBX_SBC_GAMEPAD_W1_WEAPONCONMAIN 0x0200
|
||||
#define CXBX_SBC_GAMEPAD_W1_WEAPONCONSUB 0x0400
|
||||
#define CXBX_SBC_GAMEPAD_W1_WEAPONCONMAGAZINE 0x0800
|
||||
#define CXBX_SBC_GAMEPAD_W1_COMM1 0x1000
|
||||
#define CXBX_SBC_GAMEPAD_W1_COMM2 0x2000
|
||||
#define CXBX_SBC_GAMEPAD_W1_COMM3 0x4000
|
||||
#define CXBX_SBC_GAMEPAD_W1_COMM4 0x8000
|
||||
|
||||
#define CXBX_SBC_GAMEPAD_W2_COMM5 0x0001
|
||||
#define CXBX_SBC_GAMEPAD_W2_LEFTJOYSIGHTCHANGE 0x0002
|
||||
#define CXBX_SBC_GAMEPAD_W2_TOGGLEFILTERCONTROL 0x0004
|
||||
#define CXBX_SBC_GAMEPAD_W2_TOGGLEOXYGENSUPPLY 0x0008
|
||||
#define CXBX_SBC_GAMEPAD_W2_TOGGLEFUELFLOWRATE 0x0010
|
||||
#define CXBX_SBC_GAMEPAD_W2_TOGGLEBUFFREMATERIAL 0x0020
|
||||
#define CXBX_SBC_GAMEPAD_W2_TOGGLEVTLOCATION 0x0040
|
||||
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
uint8_t zero;
|
||||
uint8_t bLength;
|
||||
uint16_t dButtons[3];
|
||||
uint16_t aimingX; //0 to 2^16 left to right
|
||||
uint16_t aimingY; //0 to 2^16 top to bottom
|
||||
int16_t rotationLever;
|
||||
int16_t sightChangeX;
|
||||
int16_t sightChangeY;
|
||||
uint16_t leftPedal; //Sidestep, 0x0000 to 0xFF00
|
||||
uint16_t middlePedal; //Brake, 0x0000 to 0xFF00
|
||||
uint16_t rightPedal; //Acceleration, 0x0000 to oxFF00
|
||||
int8_t tunerDial; //0-15 is from 9oclock, around clockwise
|
||||
int8_t gearLever; //7-13 is gears R,1,2,3,4,5
|
||||
} USB_SteelBattalion_InReport_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t zero;
|
||||
uint8_t bLength;
|
||||
uint8_t CockpitHatch_EmergencyEject;
|
||||
uint8_t Start_Ignition;
|
||||
uint8_t MapZoomInOut_OpenClose;
|
||||
uint8_t SubMonitorModeSelect_ModeSelect;
|
||||
uint8_t MainMonitorZoomOut_MainMonitorZoomIn;
|
||||
uint8_t Manipulator_ForecastShootingSystem;
|
||||
uint8_t Washing_LineColorChange;
|
||||
uint8_t Chaff_Extinguisher;
|
||||
uint8_t Override_TankDetach;
|
||||
uint8_t F1_NightScope;
|
||||
uint8_t F3_F2;
|
||||
uint8_t SubWeaponControl_MainWeaponControl;
|
||||
uint8_t Comm1_MagazineChange;
|
||||
uint8_t Comm3_Comm2;
|
||||
uint8_t Comm5_Comm4;
|
||||
uint8_t GearR_;
|
||||
uint8_t Gear1_GearN;
|
||||
uint8_t Gear3_Gear2;
|
||||
uint8_t Gear5_Gear4;
|
||||
uint8_t dummy;
|
||||
} USB_SteelBattalion_OutReport_t;
|
||||
|
||||
#define TUD_XID_SB_DESC_LEN (9+7+7)
|
||||
|
||||
#define TUD_XID_SB_DESCRIPTOR(_itfnum, _epout, _epin) \
|
||||
/* Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, XID_INTERFACE_CLASS, XID_INTERFACE_SUBCLASS, 0x00, 0x00,\
|
||||
/* Endpoint In */\
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(32), 4, \
|
||||
/* Endpoint Out */\
|
||||
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(32), 4
|
||||
|
||||
static const uint8_t STEELBATTALION_DESC_XID[] = {
|
||||
0x10,
|
||||
0x42,
|
||||
0x00, 0x01,
|
||||
0x80,
|
||||
0x01,
|
||||
0x1A,
|
||||
0x16,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
static const uint8_t STEELBATTALION_CAPABILITIES_IN[] = {
|
||||
0x00,
|
||||
0x1A,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
static const uint8_t STEELBATTALION_CAPABILITIES_OUT[] = {
|
||||
0x00,
|
||||
0x16,
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
217
src/usbd/xinput/XInputDriver.cpp
Normal file
217
src/usbd/xinput/XInputDriver.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
*/
|
||||
|
||||
#include "usbd/xinput/XInputDriver.h"
|
||||
#include "usbd/shared/driverhelper.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
#define XINPUT_OUT_SIZE 32
|
||||
|
||||
uint8_t endpoint_in = 0;
|
||||
uint8_t endpoint_out = 0;
|
||||
uint8_t xinput_out_buffer[XINPUT_OUT_SIZE] = {};
|
||||
|
||||
void xinput_process_rumble_data(const uint8_t* outBuffer)
|
||||
{
|
||||
XInputOutReport out_report;
|
||||
|
||||
memcpy(&out_report, outBuffer, sizeof(XInputOutReport));
|
||||
|
||||
if (out_report.report_type == XBOX_REPORT_TYPE_RUMBLE)
|
||||
{
|
||||
gamepadOut.out_state.lrumble = out_report.lrumble;
|
||||
gamepadOut.out_state.rrumble = out_report.rrumble;
|
||||
}
|
||||
}
|
||||
|
||||
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_device_control_request(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
(void)rhport;
|
||||
(void)stage;
|
||||
(void)request;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool xinput_control_complete(uint8_t rhport, tusb_control_request_t const *request)
|
||||
{
|
||||
(void)rhport;
|
||||
(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)result;
|
||||
(void)xferred_bytes;
|
||||
|
||||
if (ep_addr == endpoint_out) usbd_edpt_xfer(0, endpoint_out, xinput_out_buffer, XINPUT_OUT_SIZE);
|
||||
|
||||
xinput_process_rumble_data(xinput_out_buffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void XInputDriver::initialize() {
|
||||
xinputReport = {
|
||||
.report_id = 0,
|
||||
.report_size = XINPUT_ENDPOINT_SIZE,
|
||||
.buttons1 = 0,
|
||||
.buttons2 = 0,
|
||||
.lt = 0,
|
||||
.rt = 0,
|
||||
.lx = 0,
|
||||
.ly = 0,
|
||||
.rx = 0,
|
||||
.ry = 0,
|
||||
._reserved = { },
|
||||
};
|
||||
|
||||
class_driver = {
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "XINPUT",
|
||||
#endif
|
||||
.init = xinput_init,
|
||||
.reset = xinput_reset,
|
||||
.open = xinput_open,
|
||||
.control_xfer_cb = xinput_device_control_request,
|
||||
.xfer_cb = xinput_xfer_callback,
|
||||
.sof = NULL
|
||||
};
|
||||
}
|
||||
|
||||
void XInputDriver::process(Gamepad * gamepad, uint8_t * outBuffer) {
|
||||
xinputReport.buttons1 = 0
|
||||
| (gamepad->state.up ? XBOX_MASK_UP : 0)
|
||||
| (gamepad->state.down ? XBOX_MASK_DOWN : 0)
|
||||
| (gamepad->state.left ? XBOX_MASK_LEFT : 0)
|
||||
| (gamepad->state.right ? XBOX_MASK_RIGHT : 0)
|
||||
| (gamepad->state.start ? XBOX_MASK_START : 0)
|
||||
| (gamepad->state.back ? XBOX_MASK_BACK : 0)
|
||||
| (gamepad->state.l3 ? XBOX_MASK_LS : 0)
|
||||
| (gamepad->state.r3 ? XBOX_MASK_RS : 0)
|
||||
;
|
||||
|
||||
xinputReport.buttons2 = 0
|
||||
| (gamepad->state.rb ? XBOX_MASK_RB : 0)
|
||||
| (gamepad->state.lb ? XBOX_MASK_LB : 0)
|
||||
| (gamepad->state.sys ? XBOX_MASK_HOME : 0)
|
||||
| (gamepad->state.a ? XBOX_MASK_A : 0)
|
||||
| (gamepad->state.b ? XBOX_MASK_B : 0)
|
||||
| (gamepad->state.x ? XBOX_MASK_X : 0)
|
||||
| (gamepad->state.y ? XBOX_MASK_Y : 0)
|
||||
;
|
||||
|
||||
xinputReport.lt = gamepad->state.lt;
|
||||
xinputReport.rt = gamepad->state.rt;
|
||||
|
||||
xinputReport.lx = gamepad->state.lx;
|
||||
xinputReport.ly = gamepad->state.ly;
|
||||
xinputReport.rx = gamepad->state.rx;
|
||||
xinputReport.ry = gamepad->state.ry;
|
||||
|
||||
// printf("processing rumble\n");
|
||||
|
||||
// compare against previous report and send new
|
||||
if ( memcmp(last_report, &xinputReport, sizeof(XInputReport)) != 0) {
|
||||
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
|
||||
usbd_edpt_xfer(0, endpoint_in, (uint8_t *)&xinputReport, sizeof(XInputReport)); // Send report buffer
|
||||
usbd_edpt_release(0, endpoint_in); // Release control of IN endpoint
|
||||
memcpy(last_report, &xinputReport, sizeof(XInputReport)); // save if we sent it
|
||||
}
|
||||
}
|
||||
|
||||
// check for player LEDs
|
||||
if (tud_ready() &&
|
||||
(endpoint_out != 0) && (!usbd_edpt_busy(0, endpoint_out)))
|
||||
{
|
||||
usbd_edpt_claim(0, endpoint_out); // Take control of OUT endpoint
|
||||
usbd_edpt_xfer(0, endpoint_out, outBuffer, XINPUT_OUT_SIZE); // Retrieve report buffer
|
||||
usbd_edpt_release(0, endpoint_out); // Release control of OUT endpoint
|
||||
}
|
||||
}
|
||||
|
||||
// tud_hid_get_report_cb
|
||||
uint16_t XInputDriver::get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
|
||||
memcpy(buffer, &xinputReport, sizeof(XInputReport));
|
||||
return sizeof(XInputReport);
|
||||
}
|
||||
|
||||
// Only PS4 does anything with set report
|
||||
void XInputDriver::set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
|
||||
|
||||
// Only XboxOG and Xbox One use vendor control xfer cb
|
||||
bool XInputDriver::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t * XInputDriver::get_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
const char *value = (const char *)xinput_string_descriptors[index];
|
||||
return getStringDescriptor(value, index); // getStringDescriptor returns a static array
|
||||
}
|
||||
|
||||
const uint8_t * XInputDriver::get_descriptor_device_cb() {
|
||||
return xinput_device_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * XInputDriver::get_hid_descriptor_report_cb(uint8_t itf) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t * XInputDriver::get_descriptor_configuration_cb(uint8_t index) {
|
||||
return xinput_configuration_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * XInputDriver::get_descriptor_device_qualifier_cb() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16_t XInputDriver::GetJoystickMidValue() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
30
src/usbd/xinput/XInputDriver.h
Normal file
30
src/usbd/xinput/XInputDriver.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
*/
|
||||
|
||||
#ifndef _XINPUT_DRIVER_H_
|
||||
#define _XINPUT_DRIVER_H_
|
||||
|
||||
#include "usbd/gpdriver.h"
|
||||
#include "descriptors/XInputDescriptors.h"
|
||||
|
||||
class XInputDriver : public GPDriver {
|
||||
public:
|
||||
virtual void initialize();
|
||||
virtual void process(Gamepad * gamepad, uint8_t * outBuffer);
|
||||
virtual uint16_t get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen);
|
||||
virtual void set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize);
|
||||
virtual bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
virtual const uint16_t * get_descriptor_string_cb(uint8_t index, uint16_t langid);
|
||||
virtual const uint8_t * get_descriptor_device_cb();
|
||||
virtual const uint8_t * get_hid_descriptor_report_cb(uint8_t itf) ;
|
||||
virtual const uint8_t * get_descriptor_configuration_cb(uint8_t index);
|
||||
virtual const uint8_t * get_descriptor_device_qualifier_cb();
|
||||
virtual uint16_t GetJoystickMidValue();
|
||||
private:
|
||||
uint8_t last_report[CFG_TUD_ENDPOINT0_SIZE] = { };
|
||||
XInputReport xinputReport;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef USBD_CONFIG_H_
|
||||
#define USBD_CONFIG_H_
|
||||
|
||||
#include "usbd/usbd_boards.h"
|
||||
|
||||
// Boards
|
||||
// OGXM_PI_PICO
|
||||
// OGXM_ADA_FEATHER_USBH
|
||||
// OGXM_RPZERO_INTERPOSER
|
||||
|
||||
#define USBD_BOARD OGXM_RPZERO_INTERPOSER
|
||||
#define USBD_MAX_GAMEPADS 1 // This is set by idf.py menuconfig for wireless boards, number here is ignored in that case.
|
||||
#define CDC_DEBUG 0 // Set to 1 for CDC device, helpful for debugging USB host. Include utilities/log.h and use log() as you would printf()
|
||||
|
||||
#endif // USBD_CONFIG_H_
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef _GPHOSTDRIVER_H_
|
||||
#define _GPHOSTDRIVER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "host/usbh.h" // needed so xinput_host will build
|
||||
|
||||
#include "usbh/shared/shared.h"
|
||||
#include "xinput_host.h"
|
||||
#include "Gamepad.h"
|
||||
|
||||
class GPHostDriver
|
||||
{
|
||||
public:
|
||||
virtual void init(uint8_t player_id, uint8_t dev_addr, uint8_t instance) = 0;
|
||||
virtual void process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) = 0;
|
||||
virtual void process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len) = 0;
|
||||
virtual void hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) = 0;
|
||||
virtual bool send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance) = 0;
|
||||
};
|
||||
|
||||
#endif // _GPHOSTDRIVER_H_
|
||||
@@ -1,93 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#include "usbh/shared/scaling.h"
|
||||
#include "usbh/n64usb/N64USB.h"
|
||||
|
||||
void N64USB::init(uint8_t player_id, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
n64usb.player_id = player_id;
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void N64USB::update_gamepad(Gamepad& gamepad, const N64USBReport* n64_data)
|
||||
{
|
||||
gamepad.reset_pad();
|
||||
|
||||
uint8_t n64_dpad = n64_data->buttons & N64_DPAD_MASK;
|
||||
|
||||
switch(n64_dpad)
|
||||
{
|
||||
case N64_DPAD_MASK_UP:
|
||||
gamepad.buttons.up = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_UP_RIGHT:
|
||||
gamepad.buttons.up = true;
|
||||
gamepad.buttons.right = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_RIGHT:
|
||||
gamepad.buttons.right = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_RIGHT_DOWN:
|
||||
gamepad.buttons.right = true;
|
||||
gamepad.buttons.down = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_DOWN:
|
||||
gamepad.buttons.down = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_DOWN_LEFT:
|
||||
gamepad.buttons.down = true;
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_LEFT:
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_LEFT_UP:
|
||||
gamepad.buttons.left = true;
|
||||
gamepad.buttons.up = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (n64_data->buttons & N64_C_UP_MASK) gamepad.joysticks.ry = INT16_MAX;
|
||||
if (n64_data->buttons & N64_C_DOWN_MASK) gamepad.joysticks.ry = INT16_MIN;
|
||||
if (n64_data->buttons & N64_C_LEFT_MASK) gamepad.joysticks.rx = INT16_MIN;
|
||||
if (n64_data->buttons & N64_C_RIGHT_MASK) gamepad.joysticks.rx = INT16_MAX;
|
||||
|
||||
if (n64_data->buttons & N64_A_MASK) gamepad.buttons.a = true;
|
||||
if (n64_data->buttons & N64_B_MASK) gamepad.buttons.b = true;
|
||||
if (n64_data->buttons & N64_START_MASK) gamepad.buttons.start = true;
|
||||
if (n64_data->buttons & N64_L_MASK) gamepad.buttons.lb = true;
|
||||
if (n64_data->buttons & N64_R_MASK) gamepad.buttons.rb = true;
|
||||
|
||||
if (n64_data->buttons & N64_Z_MASK) gamepad.triggers.r = 0xFF;
|
||||
|
||||
gamepad.joysticks.ly = scale_uint8_to_int16(n64_data->y, true);
|
||||
gamepad.joysticks.lx = scale_uint8_to_int16(n64_data->x, false);
|
||||
}
|
||||
|
||||
void N64USB::process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
|
||||
{
|
||||
static N64USBReport prev_report = { 0 };
|
||||
|
||||
N64USBReport n64_report;
|
||||
memcpy(&n64_report, report, sizeof(n64_report));
|
||||
|
||||
if (memcmp(&n64_report, &prev_report, sizeof(n64_report)) != 0)
|
||||
{
|
||||
update_gamepad(gamepad, &n64_report);
|
||||
|
||||
prev_report = n64_report;
|
||||
}
|
||||
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void N64USB::process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len) {}
|
||||
|
||||
void N64USB::hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {}
|
||||
|
||||
bool N64USB::send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
#include "class/hid/hid_host.h"
|
||||
|
||||
#include "usbh/ps3/DInput.h"
|
||||
#include "usbh/shared/scaling.h"
|
||||
|
||||
void DInput::init(uint8_t player_id, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
dinput.player_id = player_id;
|
||||
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void DInput::update_gamepad(Gamepad& gamepad, const DInputReport* dinput_report)
|
||||
{
|
||||
gamepad.reset_pad();
|
||||
|
||||
switch (dinput_report->direction)
|
||||
{
|
||||
case DINPUT_HAT_UP:
|
||||
gamepad.buttons.up = true;
|
||||
break;
|
||||
case DINPUT_HAT_UPRIGHT:
|
||||
gamepad.buttons.up = true;
|
||||
gamepad.buttons.right = true;
|
||||
break;
|
||||
case DINPUT_HAT_RIGHT:
|
||||
gamepad.buttons.right = true;
|
||||
break;
|
||||
case DINPUT_HAT_DOWNRIGHT:
|
||||
gamepad.buttons.right = true;
|
||||
gamepad.buttons.down = true;
|
||||
break;
|
||||
case DINPUT_HAT_DOWN:
|
||||
gamepad.buttons.down = true;
|
||||
break;
|
||||
case DINPUT_HAT_DOWNLEFT:
|
||||
gamepad.buttons.down = true;
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
case DINPUT_HAT_LEFT:
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
case DINPUT_HAT_UPLEFT:
|
||||
gamepad.buttons.up = true;
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dinput_report->square_btn) gamepad.buttons.x = true;
|
||||
if (dinput_report->triangle_btn) gamepad.buttons.y = true;
|
||||
if (dinput_report->cross_btn) gamepad.buttons.a = true;
|
||||
if (dinput_report->circle_btn) gamepad.buttons.b = true;
|
||||
|
||||
if (dinput_report->select_btn) gamepad.buttons.back = true;
|
||||
if (dinput_report->start_btn) gamepad.buttons.start = true;
|
||||
if (dinput_report->ps_btn) gamepad.buttons.sys = true;
|
||||
|
||||
if (dinput_report->l3_btn) gamepad.buttons.l3 = true;
|
||||
if (dinput_report->r3_btn) gamepad.buttons.r3 = true;
|
||||
|
||||
if (dinput_report->l1_btn) gamepad.buttons.lb = true;
|
||||
if (dinput_report->r1_btn) gamepad.buttons.rb = true;
|
||||
|
||||
if (dinput_report->l2_axis > 0)
|
||||
{
|
||||
gamepad.triggers.l = dinput_report->l2_axis;
|
||||
}
|
||||
else if (dinput_report->l2_btn)
|
||||
{
|
||||
gamepad.triggers.l = 0xFF;
|
||||
}
|
||||
|
||||
if (dinput_report->r2_axis > 0)
|
||||
{
|
||||
gamepad.triggers.r = dinput_report->r2_axis;
|
||||
}
|
||||
else if (dinput_report->r2_btn)
|
||||
{
|
||||
gamepad.triggers.r = 0xFF;
|
||||
}
|
||||
|
||||
gamepad.joysticks.lx = scale_uint8_to_int16(dinput_report->l_x_axis, false);
|
||||
gamepad.joysticks.ly = scale_uint8_to_int16(dinput_report->l_y_axis, true);
|
||||
gamepad.joysticks.rx = scale_uint8_to_int16(dinput_report->r_x_axis, false);
|
||||
gamepad.joysticks.ry = scale_uint8_to_int16(dinput_report->r_y_axis, true);
|
||||
}
|
||||
|
||||
void DInput::process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
|
||||
{
|
||||
static DInputReport prev_report = {0};
|
||||
DInputReport dinput_report;
|
||||
memcpy(&dinput_report, report, sizeof(dinput_report));
|
||||
|
||||
if (memcmp(&dinput_report, &prev_report, sizeof(dinput_report)) != 0)
|
||||
{
|
||||
update_gamepad(gamepad, &dinput_report);
|
||||
prev_report = dinput_report;
|
||||
}
|
||||
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void DInput::process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len) {}
|
||||
|
||||
void DInput::hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {}
|
||||
|
||||
bool DInput::send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#ifndef _DINPUT_H_
|
||||
#define _DINPUT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usbh/GPHostDriver.h"
|
||||
#include "usbd/descriptors/DInputDescriptors.h"
|
||||
|
||||
const usb_vid_pid_t dinput_devices[] =
|
||||
{
|
||||
{0x044F, 0xB324}, // ThrustMaster Dual Trigger (PS3 mode)
|
||||
{0x0738, 0x8818}, // MadCatz Street Fighter IV Arcade FightStick
|
||||
{0x0810, 0x0003}, // Personal Communication Systems, Inc. Generic
|
||||
{0x146B, 0x0902}, // BigBen Interactive Wired Mini PS3 Game Controller
|
||||
{0x2563, 0x0575} // SHANWAN 2In1 USB Joystick
|
||||
};
|
||||
|
||||
struct DInputState
|
||||
{
|
||||
uint8_t player_id = {0};
|
||||
};
|
||||
|
||||
class DInput : public GPHostDriver
|
||||
{
|
||||
public:
|
||||
virtual void init(uint8_t player_id, uint8_t dev_addr, uint8_t instance);
|
||||
virtual void process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
|
||||
virtual void process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len);
|
||||
virtual void hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
virtual bool send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance);
|
||||
private:
|
||||
DInputState dinput;
|
||||
void update_gamepad(Gamepad& gp, const DInputReport* dinput_report);
|
||||
};
|
||||
|
||||
#endif // _DINPUT_H_
|
||||
@@ -1,312 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
#include "class/hid/hid_host.h"
|
||||
|
||||
#include "usbh/ps3/Dualshock3.h"
|
||||
|
||||
#include "usbh/shared/scaling.h"
|
||||
|
||||
void Dualshock3::init(uint8_t player_id, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
dualshock3.player_id = player_id;
|
||||
dualshock3.response_count = 0;
|
||||
dualshock3.reports_enabled = false;
|
||||
|
||||
// tuh_hid_get_report not working correctly?
|
||||
tusb_control_request_t setup_packet =
|
||||
{
|
||||
.bmRequestType = 0xA1,
|
||||
.bRequest = 0x01, // GET_REPORT
|
||||
.wValue = (HID_REPORT_TYPE_FEATURE << 8) | 0xF2,
|
||||
.wIndex = 0x0000,
|
||||
.wLength = 17
|
||||
};
|
||||
|
||||
tuh_xfer_s transfer =
|
||||
{
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0x00,
|
||||
.setup = &setup_packet,
|
||||
.buffer = (uint8_t*)&dualshock3.en_buffer,
|
||||
.complete_cb = NULL,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
if (tuh_control_xfer(&transfer))
|
||||
{
|
||||
get_report_complete_cb(dev_addr, instance);
|
||||
}
|
||||
|
||||
// tuh_hid_get_report(dev_addr, instance, 0xF2, HID_REPORT_TYPE_FEATURE, &dualshock3.en_buffer, 17);
|
||||
}
|
||||
|
||||
void Dualshock3::get_report_complete_cb(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
if (dualshock3.response_count == 0)
|
||||
{
|
||||
tusb_control_request_t setup_packet =
|
||||
{
|
||||
.bmRequestType = 0xA1,
|
||||
.bRequest = 0x01, // GET_REPORT
|
||||
.wValue = (HID_REPORT_TYPE_FEATURE << 8) | 0xF2,
|
||||
.wIndex = 0x0000,
|
||||
.wLength = 17
|
||||
};
|
||||
|
||||
tuh_xfer_s transfer =
|
||||
{
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0x00,
|
||||
.setup = &setup_packet,
|
||||
.buffer = (uint8_t*)&dualshock3.en_buffer,
|
||||
.complete_cb = NULL,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
if (tuh_control_xfer(&transfer))
|
||||
{
|
||||
dualshock3.response_count++;
|
||||
get_report_complete_cb(dev_addr, instance);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (dualshock3.response_count == 1)
|
||||
{
|
||||
tusb_control_request_t setup_packet =
|
||||
{
|
||||
.bmRequestType = 0xA1,
|
||||
.bRequest = 0x01, // GET_REPORT
|
||||
.wValue = (HID_REPORT_TYPE_FEATURE << 8) | 0xF2,
|
||||
.wIndex = 0x0000,
|
||||
.wLength = 8
|
||||
};
|
||||
|
||||
tuh_xfer_s transfer =
|
||||
{
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0x00,
|
||||
.setup = &setup_packet,
|
||||
.buffer = (uint8_t*)&dualshock3.en_buffer,
|
||||
.complete_cb = NULL,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
if (tuh_control_xfer(&transfer))
|
||||
{
|
||||
dualshock3.response_count++;
|
||||
get_report_complete_cb(dev_addr, instance);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (dualshock3.response_count == 2)
|
||||
{
|
||||
dualshock3.response_count++;
|
||||
|
||||
uint8_t default_report[] =
|
||||
{
|
||||
0x01, 0xff, 0x00, 0xff, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
memcpy(&dualshock3.out_report, &default_report, sizeof(Dualshock3OutReport));
|
||||
|
||||
dualshock3.out_report.leds_bitmap = 0x1 << (instance + 1);
|
||||
dualshock3.out_report.led[instance].time_enabled = UINT8_MAX;
|
||||
|
||||
tusb_control_request_t setup_packet =
|
||||
{
|
||||
.bmRequestType = 0x21,
|
||||
.bRequest = 0x09, // SET_REPORT
|
||||
.wValue = 0x0201,
|
||||
.wIndex = 0x0000,
|
||||
.wLength = sizeof(Dualshock3OutReport)
|
||||
};
|
||||
|
||||
tuh_xfer_s transfer =
|
||||
{
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0x00,
|
||||
.setup = &setup_packet,
|
||||
.buffer = (uint8_t*)&dualshock3.out_report,
|
||||
.complete_cb = NULL,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
if (tuh_control_xfer(&transfer))
|
||||
{
|
||||
dualshock3.reports_enabled = true;
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Dualshock3::hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len)
|
||||
{
|
||||
// if (dualshock3.response_count == 0)
|
||||
// {
|
||||
// if (tuh_hid_get_report(dev_addr, instance, 0xF2, HID_REPORT_TYPE_FEATURE, &dualshock3.en_buffer, 17))
|
||||
// {
|
||||
// dualshock3.response_count++;
|
||||
// }
|
||||
// }
|
||||
// else if (dualshock3.response_count == 1)
|
||||
// {
|
||||
// if (tuh_hid_get_report(dev_addr, instance, 0xF5, HID_REPORT_TYPE_FEATURE, &dualshock3.en_buffer, 8))
|
||||
// {
|
||||
// dualshock3.response_count++;
|
||||
// }
|
||||
// }
|
||||
// else if (dualshock3.response_count == 2)
|
||||
// {
|
||||
// dualshock3.response_count++;
|
||||
|
||||
// uint8_t default_report[] =
|
||||
// {
|
||||
// 0x01, 0xff, 0x00, 0xff, 0x00,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
// 0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
// 0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
// 0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
// };
|
||||
|
||||
// memcpy(&dualshock3.out_report, &default_report, sizeof(Dualshock3OutReport));
|
||||
|
||||
// dualshock3.out_report.leds_bitmap = 0x1 << (instance + 1);
|
||||
// dualshock3.out_report.led[instance].time_enabled = UINT8_MAX;
|
||||
|
||||
// tusb_control_request_t setup_packet =
|
||||
// {
|
||||
// .bmRequestType = 0x21,
|
||||
// .bRequest = 0x09, // SET_REPORT
|
||||
// .wValue = 0x0201,
|
||||
// .wIndex = 0x0000,
|
||||
// .wLength = sizeof(Dualshock3OutReport)
|
||||
// };
|
||||
|
||||
// tuh_xfer_s transfer =
|
||||
// {
|
||||
// .daddr = dev_addr,
|
||||
// .ep_addr = 0x00,
|
||||
// .setup = &setup_packet,
|
||||
// .buffer = (uint8_t*)&dualshock3.out_report,
|
||||
// .complete_cb = NULL,
|
||||
// .user_data = 0
|
||||
// };
|
||||
|
||||
// if (tuh_control_xfer(&transfer))
|
||||
// {
|
||||
// dualshock3.reports_enabled = true;
|
||||
// tuh_hid_receive_report(dev_addr, instance);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void Dualshock3::update_gamepad(Gamepad& gamepad, const Dualshock3Report* ds3_data)
|
||||
{
|
||||
gamepad.reset_pad();
|
||||
|
||||
if (ds3_data->up) gamepad.buttons.up =true;
|
||||
if (ds3_data->down) gamepad.buttons.down =true;
|
||||
if (ds3_data->left) gamepad.buttons.left =true;
|
||||
if (ds3_data->right) gamepad.buttons.right =true;
|
||||
|
||||
if (ds3_data->square) gamepad.buttons.x = true;
|
||||
if (ds3_data->triangle) gamepad.buttons.y = true;
|
||||
if (ds3_data->cross) gamepad.buttons.a = true;
|
||||
if (ds3_data->circle) gamepad.buttons.b = true;
|
||||
|
||||
if (ds3_data->select) gamepad.buttons.back = true;
|
||||
if (ds3_data->start) gamepad.buttons.start = true;
|
||||
if (ds3_data->ps) gamepad.buttons.sys = true;
|
||||
|
||||
if (ds3_data->l3) gamepad.buttons.l3 = true;
|
||||
if (ds3_data->r3) gamepad.buttons.r3 = true;
|
||||
|
||||
if (ds3_data->l1) gamepad.buttons.lb = true;
|
||||
if (ds3_data->r1) gamepad.buttons.rb = true;
|
||||
|
||||
gamepad.triggers.l = ds3_data->l2_axis;
|
||||
gamepad.triggers.r = ds3_data->r2_axis;
|
||||
|
||||
gamepad.joysticks.lx = scale_uint8_to_int16(ds3_data->left_x, false);
|
||||
gamepad.joysticks.ly = scale_uint8_to_int16(ds3_data->left_y, true);
|
||||
gamepad.joysticks.rx = scale_uint8_to_int16(ds3_data->right_x, false);
|
||||
gamepad.joysticks.ry = scale_uint8_to_int16(ds3_data->right_y, true);
|
||||
}
|
||||
|
||||
void Dualshock3::process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
|
||||
{
|
||||
static Dualshock3Report prev_report = { 0 };
|
||||
Dualshock3Report ds3_report;
|
||||
memcpy(&ds3_report, report, sizeof(ds3_report));
|
||||
|
||||
if (memcmp(&ds3_report, &prev_report, sizeof(ds3_report)) != 0)
|
||||
{
|
||||
update_gamepad(gamepad, &ds3_report);
|
||||
prev_report = ds3_report;
|
||||
}
|
||||
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void Dualshock3::process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len) {}
|
||||
|
||||
bool Dualshock3::send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
static absolute_time_t next_allowed_time = {0};
|
||||
absolute_time_t current_time = get_absolute_time();
|
||||
|
||||
if (!dualshock3.reports_enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
dualshock3.out_report.rumble.right_duration = (gamepad.rumble.r > 0) ? 20: 0;
|
||||
dualshock3.out_report.rumble.right_motor_on = (gamepad.rumble.r > 0) ? 1 : 0;
|
||||
|
||||
dualshock3.out_report.rumble.left_duration = (gamepad.rumble.l > 0) ? 20 : 0;
|
||||
dualshock3.out_report.rumble.left_motor_force = gamepad.rumble.l;
|
||||
|
||||
if (gamepad.rumble.l > 0 || gamepad.rumble.r > 0 ||
|
||||
absolute_time_diff_us(current_time, next_allowed_time) < 0)
|
||||
{
|
||||
tusb_control_request_t setup_packet =
|
||||
{
|
||||
.bmRequestType = 0x21,
|
||||
.bRequest = 0x09,
|
||||
.wValue = 0x0201,
|
||||
.wIndex = 0x0000,
|
||||
.wLength = sizeof(Dualshock3OutReport)
|
||||
};
|
||||
|
||||
tuh_xfer_s transfer =
|
||||
{
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0x00,
|
||||
.setup = &setup_packet,
|
||||
.buffer = (uint8_t*)&dualshock3.out_report,
|
||||
.complete_cb = NULL,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
if (tuh_control_xfer(&transfer))
|
||||
{
|
||||
if (gamepad.rumble.l == 0 && gamepad.rumble.r == 0)
|
||||
{
|
||||
next_allowed_time = delayed_by_us(get_absolute_time(), 500000);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _DUALSHOCK3_H_
|
||||
#define _DUALSHOCK3_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usbh/GPHostDriver.h"
|
||||
#include "usbd/descriptors/PS3Descriptors.h"
|
||||
#include "usbd/descriptors/DInputDescriptors.h"
|
||||
|
||||
const usb_vid_pid_t ps3_devices[] =
|
||||
{
|
||||
{0x054C, 0x0268}, // Sony Batoh (Dualshock 3)
|
||||
};
|
||||
|
||||
struct Dualshock3State
|
||||
{
|
||||
uint8_t player_id {0};
|
||||
bool reports_enabled {false};
|
||||
uint8_t en_buffer[17];
|
||||
Dualshock3OutReport out_report;
|
||||
int response_count {0};
|
||||
};
|
||||
|
||||
class Dualshock3 : public GPHostDriver
|
||||
{
|
||||
public:
|
||||
virtual void init(uint8_t player_id, uint8_t dev_addr, uint8_t instance);
|
||||
virtual void process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
|
||||
virtual void process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len);
|
||||
virtual void hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
virtual bool send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance);
|
||||
private:
|
||||
Dualshock3State dualshock3;
|
||||
void update_gamepad(Gamepad& gp, const Dualshock3Report* ds3_data);
|
||||
void get_report_complete_cb(uint8_t dev_addr, uint8_t instance);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,136 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
#include "CRC32.h"
|
||||
|
||||
#include "usbh/ps4/Dualshock4.h"
|
||||
#include "usbh/shared/scaling.h"
|
||||
|
||||
#define REPORT_ID_GAMEPAD_STATE 0x11
|
||||
|
||||
void Dualshock4::init(uint8_t player_id, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
dualshock4.player_id = player_id;
|
||||
// set_leds();
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
/* this DCs the controller, come back to it */
|
||||
bool Dualshock4::set_leds(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
// see: https://github.com/Hydr8gon/VitaControl
|
||||
|
||||
static uint8_t buffer[79] = {};
|
||||
buffer[0] = 0xA2;
|
||||
buffer[1] = 0x11;
|
||||
buffer[2] = 0xC0;
|
||||
buffer[3] = 0x20;
|
||||
buffer[4] = 0xF3;
|
||||
buffer[5] = 0x04;
|
||||
buffer[9] = led_colors[instance][0];
|
||||
buffer[10] = led_colors[instance][1];
|
||||
buffer[11] = led_colors[instance][2];
|
||||
|
||||
// Calculate the CRC of the data (including the 0xA2 byte) and append it to the end
|
||||
uint32_t crc = CRC32::calculate(buffer, 75);
|
||||
buffer[75] = crc >> 0;
|
||||
buffer[76] = crc >> 8;
|
||||
buffer[77] = crc >> 16;
|
||||
buffer[78] = crc >> 24;
|
||||
|
||||
// Send the write request, omitting the 0xA2 byte
|
||||
dualshock4.leds_set = tuh_hid_send_report(dev_addr, instance, 0, &buffer + 1, sizeof(buffer) - 1);
|
||||
|
||||
return dualshock4.leds_set;
|
||||
}
|
||||
|
||||
void Dualshock4::update_gamepad(Gamepad& gamepad, const Dualshock4Report* ds4_data)
|
||||
{
|
||||
gamepad.reset_pad();
|
||||
|
||||
switch(ds4_data->dpad)
|
||||
{
|
||||
case PS4_DPAD_MASK_UP:
|
||||
gamepad.buttons.up = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_UP_RIGHT:
|
||||
gamepad.buttons.up = true;
|
||||
gamepad.buttons.right = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_RIGHT:
|
||||
gamepad.buttons.right = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_RIGHT_DOWN:
|
||||
gamepad.buttons.right = true;
|
||||
gamepad.buttons.down = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_DOWN:
|
||||
gamepad.buttons.down = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_DOWN_LEFT:
|
||||
gamepad.buttons.down = true;
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_LEFT:
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_LEFT_UP:
|
||||
gamepad.buttons.left = true;
|
||||
gamepad.buttons.up = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ds4_data->square) gamepad.buttons.x = true;
|
||||
if (ds4_data->cross) gamepad.buttons.a = true;
|
||||
if (ds4_data->circle) gamepad.buttons.b = true;
|
||||
if (ds4_data->triangle) gamepad.buttons.y = true;
|
||||
|
||||
if (ds4_data->share) gamepad.buttons.back = true;
|
||||
if (ds4_data->option) gamepad.buttons.start = true;
|
||||
if (ds4_data->ps) gamepad.buttons.sys = true;
|
||||
if (ds4_data->tpad) gamepad.buttons.misc = true;
|
||||
|
||||
if (ds4_data->l1) gamepad.buttons.lb = true;
|
||||
if (ds4_data->r1) gamepad.buttons.rb = true;
|
||||
|
||||
if (ds4_data->l3) gamepad.buttons.l3 = true;
|
||||
if (ds4_data->r3) gamepad.buttons.r3 = true;
|
||||
|
||||
gamepad.triggers.l = ds4_data->l2_trigger;
|
||||
gamepad.triggers.r = ds4_data->r2_trigger;
|
||||
|
||||
gamepad.joysticks.lx = scale_uint8_to_int16(ds4_data->lx, false);
|
||||
gamepad.joysticks.ly = scale_uint8_to_int16(ds4_data->ly, true);
|
||||
gamepad.joysticks.rx = scale_uint8_to_int16(ds4_data->rx, false);
|
||||
gamepad.joysticks.ry = scale_uint8_to_int16(ds4_data->ry, true);
|
||||
}
|
||||
|
||||
void Dualshock4::process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
|
||||
{
|
||||
static Dualshock4Report prev_report = { 0 };
|
||||
Dualshock4Report ds4_report;
|
||||
memcpy(&ds4_report, report, sizeof(ds4_report));
|
||||
|
||||
if (memcmp(&ds4_report, &prev_report, sizeof(ds4_report)) != 0)
|
||||
{
|
||||
update_gamepad(gamepad, &ds4_report);
|
||||
|
||||
prev_report = ds4_report;
|
||||
}
|
||||
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void Dualshock4::process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len) {}
|
||||
|
||||
void Dualshock4::hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {}
|
||||
|
||||
bool Dualshock4::send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
Dualshock4OutReport out_report = {0};
|
||||
out_report.set_rumble = 1;
|
||||
out_report.motor_left = gamepad.rumble.l;
|
||||
out_report.motor_right = gamepad.rumble.r;
|
||||
|
||||
return tuh_hid_send_report(dev_addr, instance, 5, &out_report, sizeof(out_report));
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#include "usbh/ps5/Dualsense.h"
|
||||
|
||||
#include "usbh/shared/scaling.h"
|
||||
|
||||
void Dualsense::init(uint8_t player_id, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
dualsense.player_id = player_id;
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void Dualsense::update_gamepad(Gamepad& gamepad, const DualsenseReport* ds_report)
|
||||
{
|
||||
gamepad.reset_pad();
|
||||
|
||||
switch(ds_report->dpad)
|
||||
{
|
||||
case PS5_MASK_DPAD_UP:
|
||||
gamepad.buttons.up = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_UP_RIGHT:
|
||||
gamepad.buttons.up = true;
|
||||
gamepad.buttons.right = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_RIGHT:
|
||||
gamepad.buttons.right = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_RIGHT_DOWN:
|
||||
gamepad.buttons.right = true;
|
||||
gamepad.buttons.down = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_DOWN:
|
||||
gamepad.buttons.down = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_DOWN_LEFT:
|
||||
gamepad.buttons.down = true;
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_LEFT:
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_LEFT_UP:
|
||||
gamepad.buttons.left = true;
|
||||
gamepad.buttons.up = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ds_report->square) gamepad.buttons.x = true;
|
||||
if (ds_report->cross) gamepad.buttons.a = true;
|
||||
if (ds_report->circle) gamepad.buttons.b = true;
|
||||
if (ds_report->triangle) gamepad.buttons.y = true;
|
||||
|
||||
if (ds_report->buttons[0] & PS5_MASK_L1) gamepad.buttons.lb = true;
|
||||
if (ds_report->buttons[0] & PS5_MASK_R1) gamepad.buttons.rb = true;
|
||||
|
||||
if (ds_report->buttons[0] & PS5_MASK_SHARE) gamepad.buttons.back = true;
|
||||
if (ds_report->buttons[0] & PS5_MASK_OPTIONS) gamepad.buttons.start = true;
|
||||
|
||||
if (ds_report->buttons[0] & PS5_MASK_L3) gamepad.buttons.l3 = true;
|
||||
if (ds_report->buttons[0] & PS5_MASK_R3) gamepad.buttons.r3 = true;
|
||||
|
||||
if (ds_report->buttons[1] & PS5_MASK_PS) gamepad.buttons.sys = true;
|
||||
if (ds_report->buttons[1] & PS5_MASK_MIC) gamepad.buttons.misc = true;
|
||||
|
||||
gamepad.triggers.l = ds_report->lt;
|
||||
gamepad.triggers.r = ds_report->rt;
|
||||
|
||||
gamepad.joysticks.lx = scale_uint8_to_int16(ds_report->lx, false);
|
||||
gamepad.joysticks.ly = scale_uint8_to_int16(ds_report->ly, true);
|
||||
gamepad.joysticks.rx = scale_uint8_to_int16(ds_report->rx, false);
|
||||
gamepad.joysticks.ry = scale_uint8_to_int16(ds_report->ry, true);
|
||||
}
|
||||
|
||||
void Dualsense::process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
|
||||
{
|
||||
DualsenseReport ds_report;
|
||||
memcpy(&ds_report, report, sizeof(ds_report));
|
||||
update_gamepad(gamepad, &ds_report);
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void Dualsense::process_xinput_report(Gamepad& gp, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len) {}
|
||||
|
||||
void Dualsense::hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {}
|
||||
|
||||
bool Dualsense::send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
// need to figure out if the flags are necessary and how the LEDs work
|
||||
DualsenseOutReport out_report = {0};
|
||||
out_report.valid_flag0 = 0x02; // idk what this means
|
||||
out_report.valid_flag1 = 0x02; // this one either
|
||||
out_report.valid_flag2 = 0x04; // uhhhhh
|
||||
out_report.motor_left = gamepad.rumble.l;
|
||||
out_report.motor_right = gamepad.rumble.r;
|
||||
|
||||
return tuh_hid_send_report(dev_addr, instance, 5, &out_report, sizeof(out_report));
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#include "usbh/psclassic/PSClassic.h"
|
||||
|
||||
void PSClassic::init(uint8_t player_id, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
psclassic.player_id = player_id;
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void PSClassic::update_gamepad(Gamepad& gamepad, const PSClassicReport* psc_data)
|
||||
{
|
||||
gamepad.reset_pad();
|
||||
|
||||
switch (psc_data->buttons & 0x3C00) {
|
||||
case PSCLASSIC_MASK_UP_LEFT:
|
||||
gamepad.buttons.up = true;
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_UP:
|
||||
gamepad.buttons.up = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_UP_RIGHT:
|
||||
gamepad.buttons.up = true;
|
||||
gamepad.buttons.right = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_LEFT:
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_RIGHT:
|
||||
gamepad.buttons.right = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_DOWN_LEFT:
|
||||
gamepad.buttons.down = true;
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_DOWN:
|
||||
gamepad.buttons.down = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_DOWN_RIGHT:
|
||||
gamepad.buttons.down = true;
|
||||
gamepad.buttons.right = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_TRIANGLE) gamepad.buttons.y = true;
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_CIRCLE) gamepad.buttons.b = true;
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_CROSS) gamepad.buttons.a = true;
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_SQUARE) gamepad.buttons.x = true;
|
||||
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_L2) gamepad.triggers.l = 0xFF;
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_R2) gamepad.triggers.r = 0xFF;
|
||||
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_L1) gamepad.buttons.lb = true;
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_R1) gamepad.buttons.rb = true;
|
||||
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_SELECT) gamepad.buttons.back = true;
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_START) gamepad.buttons.start = true;
|
||||
}
|
||||
|
||||
void PSClassic::process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
|
||||
{
|
||||
static PSClassicReport prev_report = { 0 };
|
||||
|
||||
PSClassicReport psc_report;
|
||||
memcpy(&psc_report, report, sizeof(psc_report));
|
||||
|
||||
if (memcmp(&psc_report, &prev_report, sizeof(psc_report)) != 0)
|
||||
{
|
||||
update_gamepad(gamepad, &psc_report);
|
||||
|
||||
prev_report = psc_report;
|
||||
}
|
||||
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void PSClassic::process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len) {}
|
||||
|
||||
void PSClassic::hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {}
|
||||
|
||||
bool PSClassic::send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _PSCLASSIC_H_
|
||||
#define _PSCLASSIC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usbh/GPHostDriver.h"
|
||||
#include "usbd/descriptors/PSClassicDescriptors.h"
|
||||
|
||||
const usb_vid_pid_t psc_devices[] =
|
||||
{
|
||||
{0x054C, 0x0CDA} // psclassic
|
||||
};
|
||||
|
||||
struct PSClassicState
|
||||
{
|
||||
uint8_t player_id = {0};
|
||||
};
|
||||
|
||||
class PSClassic : public GPHostDriver
|
||||
{
|
||||
public:
|
||||
virtual void init(uint8_t player_id, uint8_t dev_addr, uint8_t instance);
|
||||
virtual void process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
|
||||
virtual void process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len);
|
||||
virtual void hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
virtual bool send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance);
|
||||
private:
|
||||
PSClassicState psclassic;
|
||||
void update_gamepad(Gamepad& gp, const PSClassicReport* psc_data);
|
||||
};
|
||||
|
||||
#endif // _PSCLASSIC_H_
|
||||
@@ -1,13 +0,0 @@
|
||||
#include "host/usbh.h"
|
||||
#include "host/usbh_pvt.h"
|
||||
#include "class/hid/hid_host.h"
|
||||
#include "usbh/shared/hid_class_driver.h"
|
||||
|
||||
usbh_class_driver_t const usbh_hid_driver =
|
||||
{
|
||||
.init = hidh_init,
|
||||
.open = hidh_open,
|
||||
.set_config = hidh_set_config,
|
||||
.xfer_cb = hidh_xfer_cb,
|
||||
.close = hidh_close
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _HID_CLASS_DRIVER_H_
|
||||
#define _HID_CLASS_DRIVER_H_
|
||||
|
||||
extern usbh_class_driver_t const usbh_hid_driver;
|
||||
|
||||
#endif
|
||||
@@ -1,26 +0,0 @@
|
||||
#include "usbh/shared/scaling.h"
|
||||
|
||||
int16_t scale_uint8_to_int16(uint8_t value, bool invert)
|
||||
{
|
||||
const uint32_t scaling_factor = UINT16_MAX;
|
||||
const int32_t bias = INT16_MIN;
|
||||
|
||||
int32_t scaled_value = ((uint32_t)value * scaling_factor) >> 8;
|
||||
scaled_value += bias;
|
||||
|
||||
if (invert)
|
||||
{
|
||||
scaled_value = -scaled_value - 1;
|
||||
}
|
||||
|
||||
if (scaled_value < INT16_MIN)
|
||||
{
|
||||
scaled_value = INT16_MIN;
|
||||
}
|
||||
else if (scaled_value > INT16_MAX)
|
||||
{
|
||||
scaled_value = INT16_MAX;
|
||||
}
|
||||
|
||||
return (int16_t)scaled_value;
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <cmath>
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#include "usbh/switch/SwitchPro.h"
|
||||
|
||||
void SwitchPro::init(uint8_t player_id, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
switch_pro.player_id = player_id;
|
||||
}
|
||||
|
||||
void SwitchPro::send_handshake(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
if (tuh_hid_send_ready)
|
||||
{
|
||||
uint8_t handshake_command[2] = {CMD_HID, SUBCMD_HANDSHAKE};
|
||||
switch_pro.handshake_sent = tuh_hid_send_report(dev_addr, instance, 0, handshake_command, sizeof(handshake_command));
|
||||
}
|
||||
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
uint8_t SwitchPro::get_output_sequence_counter()
|
||||
{
|
||||
// increments each report, resets to 0 after 15
|
||||
uint8_t counter = switch_pro.output_sequence_counter;
|
||||
switch_pro.output_sequence_counter = (switch_pro.output_sequence_counter + 1) & 0x0F;
|
||||
return counter;
|
||||
}
|
||||
|
||||
void SwitchPro::disable_timeout(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
if (tuh_hid_send_ready)
|
||||
{
|
||||
uint8_t disable_timeout_cmd[2] = {CMD_HID, SUBCMD_DISABLE_TIMEOUT};
|
||||
switch_pro.timeout_disabled = tuh_hid_send_report(dev_addr, instance, 0, disable_timeout_cmd, sizeof(disable_timeout_cmd));
|
||||
}
|
||||
}
|
||||
|
||||
void SwitchPro::process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
|
||||
{
|
||||
if (!switch_pro.handshake_sent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (!switch_pro.timeout_disabled)
|
||||
{
|
||||
disable_timeout(dev_addr, instance); // response to handshake
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
return;
|
||||
}
|
||||
|
||||
static SwitchProReport prev_report = {0};
|
||||
|
||||
SwitchProReport switch_report;
|
||||
memcpy(&switch_report, report, sizeof(switch_report));
|
||||
|
||||
if (memcmp(&switch_report, &prev_report, sizeof(switch_report)) != 0)
|
||||
{
|
||||
update_gamepad(gamepad, &switch_report);
|
||||
|
||||
prev_report = switch_report;
|
||||
}
|
||||
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
int16_t SwitchPro::normalize_axes(uint16_t value)
|
||||
{
|
||||
/* 12bit value from the controller doesnt cover the full 12bit range seemingly
|
||||
doesn't seem completely centered at 2047 so I may be missing something here
|
||||
tried to get as close as possible with the multiplier */
|
||||
|
||||
int32_t normalized_value = (value - 2047) * 22;
|
||||
|
||||
if (normalized_value < INT16_MIN)
|
||||
{
|
||||
normalized_value = INT16_MIN;
|
||||
}
|
||||
else if (normalized_value > INT16_MAX)
|
||||
{
|
||||
normalized_value = INT16_MAX;
|
||||
}
|
||||
|
||||
return (int16_t)normalized_value;
|
||||
}
|
||||
|
||||
void SwitchPro::update_gamepad(Gamepad& gamepad, const SwitchProReport* switch_report)
|
||||
{
|
||||
gamepad.reset_pad();
|
||||
|
||||
if (switch_report->up) gamepad.buttons.up =true;
|
||||
if (switch_report->down) gamepad.buttons.down =true;
|
||||
if (switch_report->left) gamepad.buttons.left =true;
|
||||
if (switch_report->right) gamepad.buttons.right =true;
|
||||
|
||||
if (switch_report->y) gamepad.buttons.x = true;
|
||||
if (switch_report->x) gamepad.buttons.y = true;
|
||||
if (switch_report->b) gamepad.buttons.a = true;
|
||||
if (switch_report->a) gamepad.buttons.b = true;
|
||||
|
||||
if (switch_report->minus) gamepad.buttons.back = true;
|
||||
if (switch_report->plus) gamepad.buttons.start = true;
|
||||
if (switch_report->home) gamepad.buttons.sys = true;
|
||||
if (switch_report->capture) gamepad.buttons.misc = true;
|
||||
|
||||
if (switch_report->stickL) gamepad.buttons.l3 = true;
|
||||
if (switch_report->stickR) gamepad.buttons.r3 = true;
|
||||
|
||||
if (switch_report->l) gamepad.buttons.lb = true;
|
||||
if (switch_report->r) gamepad.buttons.rb = true;
|
||||
|
||||
if (switch_report->zl) gamepad.triggers.l = 0xFF;
|
||||
if (switch_report->zr) gamepad.triggers.r = 0xFF;
|
||||
|
||||
gamepad.joysticks.lx = normalize_axes(switch_report->leftX );
|
||||
gamepad.joysticks.ly = normalize_axes(switch_report->leftY );
|
||||
gamepad.joysticks.rx = normalize_axes(switch_report->rightX);
|
||||
gamepad.joysticks.ry = normalize_axes(switch_report->rightY);
|
||||
}
|
||||
|
||||
void SwitchPro::process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len) {}
|
||||
|
||||
void SwitchPro::hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {}
|
||||
|
||||
bool SwitchPro::send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
if (!switch_pro.handshake_sent)
|
||||
{
|
||||
send_handshake(dev_addr, instance);
|
||||
}
|
||||
else if (!switch_pro.timeout_disabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// See: https://github.com/Dan611/hid-procon
|
||||
// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
|
||||
// https://github.com/HisashiKato/USB_Host_Shield_Library_2.0
|
||||
|
||||
SwitchProOutReport report = {};
|
||||
uint8_t report_size = 10;
|
||||
|
||||
report.command = CMD_RUMBLE_ONLY;
|
||||
report.sequence_counter = get_output_sequence_counter();
|
||||
|
||||
if (gamepad.rumble.l > 0)
|
||||
{
|
||||
uint8_t amplitude_l = static_cast<uint8_t>(((gamepad.rumble.l / 255.0f) * 0.8f + 0.5f) * (0xC0 - 0x40) + 0x40);
|
||||
|
||||
report.rumble_l[0] = amplitude_l;
|
||||
report.rumble_l[1] = 0x88;
|
||||
report.rumble_l[2] = amplitude_l / 2;
|
||||
report.rumble_l[3] = 0x61;
|
||||
}
|
||||
else
|
||||
{
|
||||
report.rumble_l[0] = 0x00;
|
||||
report.rumble_l[1] = 0x01;
|
||||
report.rumble_l[2] = 0x40;
|
||||
report.rumble_l[3] = 0x40;
|
||||
}
|
||||
|
||||
if (gamepad.rumble.r > 0)
|
||||
{
|
||||
uint8_t amplitude_r = static_cast<uint8_t>(((gamepad.rumble.r / 255.0f) * 0.8f + 0.5f) * (0xC0 - 0x40) + 0x40);
|
||||
|
||||
report.rumble_r[0] = amplitude_r;
|
||||
report.rumble_r[1] = 0x88;
|
||||
report.rumble_r[2] = amplitude_r / 2;
|
||||
report.rumble_r[3] = 0x61;
|
||||
}
|
||||
else
|
||||
{
|
||||
report.rumble_r[0] = 0x00;
|
||||
report.rumble_r[1] = 0x01;
|
||||
report.rumble_r[2] = 0x40;
|
||||
report.rumble_r[3] = 0x40;
|
||||
}
|
||||
|
||||
if (!switch_pro.commands_sent)
|
||||
{
|
||||
if (!switch_pro.led_set)
|
||||
{
|
||||
report_size = 12;
|
||||
|
||||
report.command = CMD_AND_RUMBLE;
|
||||
report.sub_command = CMD_LED;
|
||||
report.sub_command_args[0] = switch_pro.player_id;
|
||||
|
||||
switch_pro.led_set = tuh_hid_send_report(dev_addr, instance, 0, &report, report_size);
|
||||
|
||||
return switch_pro.led_set;
|
||||
}
|
||||
else if (!switch_pro.led_home_set)
|
||||
{
|
||||
report_size = 14;
|
||||
|
||||
report.command = CMD_AND_RUMBLE;
|
||||
report.sub_command = CMD_LED_HOME;
|
||||
report.sub_command_args[0] = (0 /* Number of cycles */ << 4) | (true ? 0xF : 0);
|
||||
report.sub_command_args[1] = (0xF /* LED start intensity */ << 4) | 0x0 /* Number of full cycles */;
|
||||
report.sub_command_args[2] = (0xF /* Mini Cycle 1 LED intensity */ << 4) | 0x0 /* Mini Cycle 2 LED intensity */;
|
||||
|
||||
switch_pro.led_home_set = tuh_hid_send_report(dev_addr, instance, 0, &report, report_size);
|
||||
|
||||
return switch_pro.led_home_set;
|
||||
}
|
||||
else if (!switch_pro.full_report_enabled)
|
||||
{
|
||||
report_size = 12;
|
||||
|
||||
report.command = CMD_AND_RUMBLE;
|
||||
report.sub_command = CMD_MODE;
|
||||
report.sub_command_args[0] = SUBCMD_FULL_REPORT_MODE;
|
||||
|
||||
switch_pro.full_report_enabled = tuh_hid_send_report(dev_addr, instance, 0, &report, report_size);
|
||||
|
||||
return switch_pro.full_report_enabled;
|
||||
}
|
||||
else if (!switch_pro.imu_enabled)
|
||||
{
|
||||
report_size = 12;
|
||||
|
||||
report.command = CMD_AND_RUMBLE;
|
||||
report.sub_command = CMD_GYRO;
|
||||
report.sub_command_args[0] = 1 ? 1 : 0;
|
||||
|
||||
switch_pro.imu_enabled = tuh_hid_send_report(dev_addr, instance, 0, &report, report_size);
|
||||
|
||||
return switch_pro.imu_enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch_pro.commands_sent = true;
|
||||
}
|
||||
}
|
||||
|
||||
return tuh_hid_send_report(dev_addr, instance, 0, &report, report_size);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _SWITCHPRO_H_
|
||||
#define _SWITCHPRO_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usbd/descriptors/SwitchProDescriptors.h"
|
||||
#include "usbh/GPHostDriver.h"
|
||||
|
||||
const usb_vid_pid_t switch_pro_devices[] =
|
||||
{
|
||||
{0x057E, 0x2009} // Switch Pro
|
||||
};
|
||||
|
||||
struct SwitchProState
|
||||
{
|
||||
uint8_t player_id = {0};
|
||||
bool handshake_sent {false};
|
||||
bool timeout_disabled {false};
|
||||
bool full_report_enabled {false};
|
||||
bool led_set {false};
|
||||
bool led_home_set {false};
|
||||
bool imu_enabled {false};
|
||||
bool commands_sent {false};
|
||||
uint8_t output_sequence_counter {0};
|
||||
};
|
||||
|
||||
class SwitchPro : public GPHostDriver
|
||||
{
|
||||
public:
|
||||
virtual void init(uint8_t player_id, uint8_t dev_addr, uint8_t instance);
|
||||
virtual void process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
|
||||
virtual void process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len);
|
||||
virtual void hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
virtual bool send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance);
|
||||
private:
|
||||
SwitchProState switch_pro;
|
||||
void send_handshake(uint8_t dev_addr, uint8_t instance);
|
||||
void disable_timeout(uint8_t dev_addr, uint8_t instance);
|
||||
uint8_t get_output_sequence_counter();
|
||||
int16_t normalize_axes(uint16_t value);
|
||||
void update_gamepad(Gamepad& gp, const SwitchProReport* switch_pro_data);
|
||||
};
|
||||
|
||||
#endif // _SWITCHPRO_H_
|
||||
@@ -1,102 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/time.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#include "usbh/switch/SwitchWired.h"
|
||||
|
||||
#include "usbh/shared/scaling.h"
|
||||
#include "usbd/descriptors/SwitchDescriptors.h"
|
||||
|
||||
void SwitchWired::init(uint8_t player_id, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
switch_wired.player_id = player_id;
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void SwitchWired::process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
|
||||
{
|
||||
static SwitchWiredReport prev_report = {0};
|
||||
|
||||
SwitchWiredReport switch_report;
|
||||
memcpy(&switch_report, report, sizeof(switch_report));
|
||||
|
||||
if (memcmp(&switch_report, &prev_report, sizeof(switch_report)) != 0)
|
||||
{
|
||||
update_gamepad(gamepad, &switch_report);
|
||||
|
||||
prev_report = switch_report;
|
||||
}
|
||||
|
||||
tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void SwitchWired::update_gamepad(Gamepad& gamepad, const SwitchWiredReport* switch_report)
|
||||
{
|
||||
gamepad.reset_pad();
|
||||
|
||||
switch (switch_report->dpad)
|
||||
{
|
||||
case SWITCH_HAT_UP:
|
||||
gamepad.buttons.up = true;
|
||||
break;
|
||||
case SWITCH_HAT_UPRIGHT:
|
||||
gamepad.buttons.up = true;
|
||||
gamepad.buttons.right = true;
|
||||
break;
|
||||
case SWITCH_HAT_RIGHT:
|
||||
gamepad.buttons.right = true;
|
||||
break;
|
||||
case SWITCH_HAT_DOWNRIGHT:
|
||||
gamepad.buttons.right = true;
|
||||
gamepad.buttons.down = true;
|
||||
break;
|
||||
case SWITCH_HAT_DOWN:
|
||||
gamepad.buttons.down = true;
|
||||
break;
|
||||
case SWITCH_HAT_DOWNLEFT:
|
||||
gamepad.buttons.down = true;
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
case SWITCH_HAT_LEFT:
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
case SWITCH_HAT_UPLEFT:
|
||||
gamepad.buttons.up = true;
|
||||
gamepad.buttons.left = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (switch_report->b) gamepad.buttons.a = true;
|
||||
if (switch_report->a) gamepad.buttons.b = true;
|
||||
if (switch_report->y) gamepad.buttons.x = true;
|
||||
if (switch_report->x) gamepad.buttons.y = true;
|
||||
|
||||
if (switch_report->minus) gamepad.buttons.back = true;
|
||||
if (switch_report->plus) gamepad.buttons.start = true;
|
||||
if (switch_report->l3) gamepad.buttons.l3 = true;
|
||||
if (switch_report->r3) gamepad.buttons.r3 = true;
|
||||
|
||||
if(switch_report->home) gamepad.buttons.sys = true;
|
||||
if(switch_report->capture) gamepad.buttons.misc = true;
|
||||
|
||||
if(switch_report->l) gamepad.buttons.lb = true;
|
||||
if(switch_report->r) gamepad.buttons.rb = true;
|
||||
|
||||
if(switch_report->lz) gamepad.triggers.l = 0xFF;
|
||||
if(switch_report->rz) gamepad.triggers.r = 0xFF;
|
||||
|
||||
gamepad.joysticks.lx = scale_uint8_to_int16(switch_report->lx, false);
|
||||
gamepad.joysticks.ly = scale_uint8_to_int16(switch_report->ly, true);
|
||||
gamepad.joysticks.rx = scale_uint8_to_int16(switch_report->rx, false);
|
||||
gamepad.joysticks.ry = scale_uint8_to_int16(switch_report->ry, true);
|
||||
}
|
||||
|
||||
void SwitchWired::process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len) {}
|
||||
|
||||
void SwitchWired::hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {}
|
||||
|
||||
bool SwitchWired::send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
return true; // not aware of a wired switch gamepad with rumble
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _SWITCHWIRED_H_
|
||||
#define _SWITCHWIRED_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usbh/GPHostDriver.h"
|
||||
|
||||
const usb_vid_pid_t switch_wired_devices[] =
|
||||
{
|
||||
{0x20D6, 0xA719}, // PowerA wired
|
||||
{0x20D6, 0xA713}, // PowerA Enhanced wired
|
||||
{0x0F0D, 0x0092} // Hori Pokken wired, I don't have this one so not 100% on if it'll work
|
||||
};
|
||||
|
||||
struct SwitchWiredReport
|
||||
{
|
||||
uint8_t y : 1;
|
||||
uint8_t b : 1;
|
||||
uint8_t a : 1;
|
||||
uint8_t x : 1;
|
||||
|
||||
uint8_t l : 1;
|
||||
uint8_t r : 1;
|
||||
uint8_t lz : 1;
|
||||
uint8_t rz : 1;
|
||||
|
||||
uint8_t minus : 1;
|
||||
uint8_t plus : 1;
|
||||
uint8_t l3 : 1;
|
||||
uint8_t r3 : 1;
|
||||
|
||||
uint8_t home : 1;
|
||||
uint8_t capture : 1;
|
||||
|
||||
uint8_t : 2;
|
||||
|
||||
uint8_t dpad : 4;
|
||||
uint8_t : 4;
|
||||
|
||||
uint8_t lx;
|
||||
uint8_t ly;
|
||||
uint8_t rx;
|
||||
uint8_t ry;
|
||||
}
|
||||
__attribute__((packed));
|
||||
|
||||
struct SwitchWiredState
|
||||
{
|
||||
uint8_t player_id = {0};
|
||||
};
|
||||
|
||||
class SwitchWired : public GPHostDriver
|
||||
{
|
||||
public:
|
||||
virtual void init(uint8_t player_id, uint8_t dev_addr, uint8_t instance);
|
||||
virtual void process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
|
||||
virtual void process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len);
|
||||
virtual void hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
virtual bool send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance);
|
||||
private:
|
||||
SwitchWiredState switch_wired;
|
||||
void update_gamepad(Gamepad& gp, const SwitchWiredReport* switch_pro_data);
|
||||
};
|
||||
|
||||
#endif // _SWITCHWIRED_H_
|
||||
225
src/usbh/tusb_hid/hid_host_app.cpp
Normal file
225
src/usbh/tusb_hid/hid_host_app.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "tusb.h"
|
||||
#include "host/usbh.h"
|
||||
#include "host/usbh_pvt.h"
|
||||
#include "class/hid/hid_host.h"
|
||||
|
||||
#include "usbh/tusb_hid/shared.h"
|
||||
#include "usbh/tusb_hid/hid_host_app.h"
|
||||
|
||||
#include "usbh/tusb_host_manager.h" // global enum host_mode
|
||||
|
||||
// TODO: make a class for all this
|
||||
|
||||
Mouse* mouse = nullptr;
|
||||
N64USB* n64usb = nullptr;
|
||||
PSClassic* psclassic = nullptr;
|
||||
Dualshock3* dualshock3 = nullptr;
|
||||
Dualshock4* dualshock4 = nullptr;
|
||||
Dualsense* dualsense = nullptr;
|
||||
SwitchPro* switch_pro = nullptr;
|
||||
SwitchWired* switch_wired = nullptr;
|
||||
|
||||
static bool gamepad_mounted = false;
|
||||
static uint8_t gamepad_dev_addr = 0;
|
||||
static uint8_t gamepad_instance = 0;
|
||||
|
||||
usbh_class_driver_t const usbh_hid_driver =
|
||||
{
|
||||
.init = hidh_init,
|
||||
.open = hidh_open,
|
||||
.set_config = hidh_set_config,
|
||||
.xfer_cb = hidh_xfer_cb,
|
||||
.close = hidh_close
|
||||
};
|
||||
|
||||
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
|
||||
{
|
||||
(void)desc_report;
|
||||
(void)desc_len;
|
||||
|
||||
if (!gamepad_mounted)
|
||||
{
|
||||
gamepad_dev_addr = dev_addr;
|
||||
gamepad_instance = instance;
|
||||
gamepad_mounted = true;
|
||||
}
|
||||
|
||||
if (host_mode == HOST_MODE_HID_SWITCH_PRO && !switch_pro)
|
||||
{
|
||||
switch_pro = new SwitchPro();
|
||||
switch_pro->init(dev_addr, instance);
|
||||
}
|
||||
else if (host_mode == HOST_MODE_HID_SWITCH_WIRED && !switch_wired)
|
||||
{
|
||||
switch_wired = new SwitchWired();
|
||||
switch_wired->init(dev_addr, instance);
|
||||
}
|
||||
else if (host_mode == HOST_MODE_HID_PS3 && !dualshock3)
|
||||
{
|
||||
dualshock3 = new Dualshock3();
|
||||
dualshock3->init(dev_addr, instance);
|
||||
}
|
||||
else if (host_mode == HOST_MODE_HID_PS4 && !dualshock4)
|
||||
{
|
||||
dualshock4 = new Dualshock4();
|
||||
dualshock4->init(dev_addr, instance);
|
||||
}
|
||||
else if (host_mode == HOST_MODE_HID_PS5 && !dualsense)
|
||||
{
|
||||
dualsense = new Dualsense();
|
||||
dualsense->init(dev_addr, instance);
|
||||
}
|
||||
else if (host_mode == HOST_MODE_HID_PSCLASSIC && !psclassic)
|
||||
{
|
||||
psclassic = new PSClassic();
|
||||
psclassic->init(dev_addr, instance);
|
||||
}
|
||||
else if (host_mode == HOST_MODE_HID_N64USB && !n64usb)
|
||||
{
|
||||
n64usb = new N64USB();
|
||||
n64usb->init(dev_addr, instance);
|
||||
}
|
||||
else if (host_mode == HOST_MODE_HID_MOUSE && !mouse)
|
||||
{
|
||||
mouse = new Mouse();
|
||||
mouse->init(dev_addr, instance);
|
||||
}
|
||||
|
||||
if (!tuh_hid_receive_report(dev_addr, instance))
|
||||
{
|
||||
printf("Error: cannot request to receive report\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
if (gamepad_mounted && gamepad_dev_addr == dev_addr && gamepad_instance == instance)
|
||||
{
|
||||
gamepad_mounted = false;
|
||||
|
||||
if (switch_pro)
|
||||
{
|
||||
delete switch_pro;
|
||||
switch_pro = nullptr;
|
||||
}
|
||||
if (switch_wired)
|
||||
{
|
||||
delete switch_wired;
|
||||
switch_wired = nullptr;
|
||||
}
|
||||
if (n64usb)
|
||||
{
|
||||
delete n64usb;
|
||||
n64usb = nullptr;
|
||||
}
|
||||
if (psclassic)
|
||||
{
|
||||
delete psclassic;
|
||||
psclassic = nullptr;
|
||||
}
|
||||
if (dualshock3)
|
||||
{
|
||||
delete dualshock3;
|
||||
dualshock3 = nullptr;
|
||||
}
|
||||
if (dualshock4)
|
||||
{
|
||||
delete dualshock4;
|
||||
dualshock4 = nullptr;
|
||||
}
|
||||
if (dualsense)
|
||||
{
|
||||
delete dualsense;
|
||||
dualsense = nullptr;
|
||||
}
|
||||
if (mouse)
|
||||
{
|
||||
delete mouse;
|
||||
mouse = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
switch(host_mode)
|
||||
{
|
||||
case HOST_MODE_HID_PSCLASSIC:
|
||||
psclassic->process_report(report, len);
|
||||
break;
|
||||
case HOST_MODE_HID_PS3:
|
||||
dualshock3->process_report(report, len);
|
||||
break;
|
||||
case HOST_MODE_HID_PS4:
|
||||
dualshock4->process_report(report, len);
|
||||
break;
|
||||
case HOST_MODE_HID_PS5:
|
||||
dualsense->process_report(report, len);
|
||||
break;
|
||||
case HOST_MODE_HID_SWITCH_PRO:
|
||||
switch_pro->process_report(report, len);
|
||||
break;
|
||||
case HOST_MODE_HID_SWITCH_WIRED:
|
||||
switch_wired->process_report(report, len);
|
||||
break;
|
||||
case HOST_MODE_HID_N64USB:
|
||||
n64usb->process_report(report, len);
|
||||
break;
|
||||
case HOST_MODE_HID_MOUSE:
|
||||
mouse->process_report(report, len);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !tuh_hid_receive_report(dev_addr, instance) )
|
||||
{
|
||||
printf("Error: cannot request to receive report\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
// send rumble data
|
||||
bool send_fb_data_to_hid_gamepad()
|
||||
{
|
||||
if (!gamepad_mounted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rumble_sent = false;
|
||||
|
||||
if (tuh_hid_send_ready)
|
||||
{
|
||||
switch(host_mode)
|
||||
{
|
||||
case HOST_MODE_HID_PSCLASSIC:
|
||||
rumble_sent = psclassic->send_fb_data();
|
||||
break;
|
||||
case HOST_MODE_HID_PS3:
|
||||
rumble_sent = dualshock3->send_fb_data();
|
||||
break;
|
||||
case HOST_MODE_HID_PS4:
|
||||
rumble_sent = dualshock4->send_fb_data();
|
||||
break;
|
||||
case HOST_MODE_HID_PS5:
|
||||
rumble_sent = dualsense->send_fb_data();
|
||||
break;
|
||||
case HOST_MODE_HID_SWITCH_PRO:
|
||||
rumble_sent = switch_pro->send_fb_data();
|
||||
break;
|
||||
case HOST_MODE_HID_SWITCH_WIRED:
|
||||
rumble_sent = switch_wired->send_fb_data();
|
||||
break;
|
||||
case HOST_MODE_HID_N64USB:
|
||||
rumble_sent = n64usb->send_fb_data();
|
||||
break;
|
||||
case HOST_MODE_HID_MOUSE:
|
||||
rumble_sent = mouse->send_fb_data();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rumble_sent;
|
||||
}
|
||||
25
src/usbh/tusb_hid/hid_host_app.h
Normal file
25
src/usbh/tusb_hid/hid_host_app.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef _HID_HOST_APP_H_
|
||||
#define _HID_HOST_APP_H_
|
||||
|
||||
#include "usbh/tusb_hid/mouse.h"
|
||||
#include "usbh/tusb_hid/n64usb.h"
|
||||
#include "usbh/tusb_hid/psclassic.h"
|
||||
#include "usbh/tusb_hid/ps3.h"
|
||||
#include "usbh/tusb_hid/ps4.h"
|
||||
#include "usbh/tusb_hid/ps5.h"
|
||||
#include "usbh/tusb_hid/switch_pro.h"
|
||||
#include "usbh/tusb_hid/switch_wired.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern usbh_class_driver_t const usbh_hid_driver;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
bool send_fb_data_to_hid_gamepad();
|
||||
|
||||
#endif // _HID_HOST_APP_H_
|
||||
76
src/usbh/tusb_hid/mouse.cpp
Normal file
76
src/usbh/tusb_hid/mouse.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#include "usbh/tusb_hid/mouse.h"
|
||||
|
||||
#include "utilities/scaling.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
void Mouse::init(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
mouse.dev_addr = dev_addr;
|
||||
mouse.instance = instance;
|
||||
}
|
||||
|
||||
int16_t Mouse::scale_and_clamp_axes(int32_t value)
|
||||
{
|
||||
// minimum % of int16 +/- joystick value allowed
|
||||
float minimum_percentage = 0.10; // 5%
|
||||
int32_t scaled_value = 0;
|
||||
|
||||
if (value > 0)
|
||||
{
|
||||
scaled_value = (INT16_MAX * minimum_percentage) + ((1 - minimum_percentage) * value);
|
||||
}
|
||||
else if (value < 0)
|
||||
{
|
||||
scaled_value = (INT16_MIN * minimum_percentage) + ((1 - minimum_percentage) * value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (scaled_value >= INT16_MAX) return INT16_MAX;
|
||||
else if (scaled_value <= INT16_MIN) return INT16_MIN;
|
||||
|
||||
return (int16_t)scaled_value;
|
||||
}
|
||||
|
||||
void Mouse::update_gamepad(const hid_mouse_report_t* mouse_report)
|
||||
{
|
||||
// gamepad.reset_state();
|
||||
|
||||
gamepad.state.rt = (mouse_report->buttons & MOUSE_BUTTON_LEFT ) ? 0xFF : 0x00;
|
||||
gamepad.state.lt = (mouse_report->buttons & MOUSE_BUTTON_RIGHT) ? 0xFF : 0x00;
|
||||
|
||||
// for testing
|
||||
gamepad.state.x = (mouse_report->buttons & MOUSE_BUTTON_BACKWARD);
|
||||
gamepad.state.a = (mouse_report->buttons & MOUSE_BUTTON_FORWARD );
|
||||
|
||||
gamepad.state.y = (mouse_report->wheel != 0);
|
||||
|
||||
int32_t scaled_y = scale_int8_to_int16(mouse_report->y, true) * 2; // * 2 to make more responsive
|
||||
int32_t scaled_x = scale_int8_to_int16(mouse_report->x, false) * 2;
|
||||
|
||||
gamepad.state.ry = scale_and_clamp_axes(scaled_y);
|
||||
gamepad.state.rx = scale_and_clamp_axes(scaled_x);
|
||||
}
|
||||
|
||||
void Mouse::process_report(uint8_t const* report, uint16_t len)
|
||||
{
|
||||
static hid_mouse_report_t prev_report = { 0 };
|
||||
|
||||
hid_mouse_report_t mouse_report;
|
||||
memcpy(&mouse_report, report, sizeof(mouse_report));
|
||||
|
||||
if (memcmp(&mouse_report, &prev_report, sizeof(mouse_report)) != 0)
|
||||
update_gamepad(&mouse_report);
|
||||
}
|
||||
|
||||
bool Mouse::send_fb_data()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
31
src/usbh/tusb_hid/mouse.h
Normal file
31
src/usbh/tusb_hid/mouse.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _MOUSE_H_
|
||||
#define _MOUSE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "class/hid/hid.h"
|
||||
#include "usbh/tusb_hid/shared.h"
|
||||
|
||||
struct MouseState
|
||||
{
|
||||
uint8_t dev_addr = {0};
|
||||
uint8_t instance = {0};
|
||||
};
|
||||
|
||||
class Mouse
|
||||
{
|
||||
public:
|
||||
void init(uint8_t dev_addr, uint8_t instance);
|
||||
void process_report(uint8_t const* report, uint16_t len);
|
||||
bool send_fb_data();
|
||||
|
||||
private:
|
||||
MouseState mouse;
|
||||
|
||||
int16_t scale_and_clamp_axes(int32_t value);
|
||||
void update_gamepad(const hid_mouse_report_t* mouse_report);
|
||||
};
|
||||
|
||||
#endif // _MOUSE_H_
|
||||
90
src/usbh/tusb_hid/n64usb.cpp
Normal file
90
src/usbh/tusb_hid/n64usb.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#include "utilities/scaling.h"
|
||||
|
||||
#include "usbh/tusb_hid/n64usb.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
void N64USB::init(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
n64usb.dev_addr = dev_addr;
|
||||
n64usb.instance = instance;
|
||||
}
|
||||
|
||||
void N64USB::update_gamepad(const N64USBReport* n64_data)
|
||||
{
|
||||
gamepad.reset_state();
|
||||
|
||||
uint8_t n64_dpad = n64_data->buttons & N64_DPAD_MASK;
|
||||
|
||||
switch(n64_dpad)
|
||||
{
|
||||
case N64_DPAD_MASK_UP:
|
||||
gamepad.state.up = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_UP_RIGHT:
|
||||
gamepad.state.up = true;
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_RIGHT:
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_RIGHT_DOWN:
|
||||
gamepad.state.right = true;
|
||||
gamepad.state.down = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_DOWN:
|
||||
gamepad.state.down = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_DOWN_LEFT:
|
||||
gamepad.state.down = true;
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_LEFT:
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case N64_DPAD_MASK_LEFT_UP:
|
||||
gamepad.state.left = true;
|
||||
gamepad.state.up = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (n64_data->buttons & N64_C_UP_MASK) gamepad.state.ry = INT16_MAX;
|
||||
if (n64_data->buttons & N64_C_DOWN_MASK) gamepad.state.ry = INT16_MIN;
|
||||
if (n64_data->buttons & N64_C_LEFT_MASK) gamepad.state.rx = INT16_MIN;
|
||||
if (n64_data->buttons & N64_C_RIGHT_MASK) gamepad.state.rx = INT16_MAX;
|
||||
|
||||
if (n64_data->buttons & N64_A_MASK) gamepad.state.a = true;
|
||||
if (n64_data->buttons & N64_B_MASK) gamepad.state.b = true;
|
||||
if (n64_data->buttons & N64_START_MASK) gamepad.state.start = true;
|
||||
if (n64_data->buttons & N64_L_MASK) gamepad.state.lb = true;
|
||||
if (n64_data->buttons & N64_R_MASK) gamepad.state.rb = true;
|
||||
|
||||
if (n64_data->buttons & N64_Z_MASK) gamepad.state.rt = 0xFF;
|
||||
|
||||
gamepad.state.ly = scale_uint8_to_int16(n64_data->y, true);
|
||||
gamepad.state.lx = scale_uint8_to_int16(n64_data->x, false);
|
||||
}
|
||||
|
||||
void N64USB::process_report(uint8_t const* report, uint16_t len)
|
||||
{
|
||||
static N64USBReport prev_report = { 0 };
|
||||
|
||||
N64USBReport n64_report;
|
||||
memcpy(&n64_report, report, sizeof(n64_report));
|
||||
|
||||
if (memcmp(&n64_report, &prev_report, sizeof(n64_report)) != 0)
|
||||
{
|
||||
update_gamepad(&n64_report);
|
||||
|
||||
prev_report = n64_report;
|
||||
}
|
||||
}
|
||||
|
||||
bool N64USB::send_fb_data()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usbh/GPHostDriver.h"
|
||||
#include "usbh/tusb_hid/shared.h"
|
||||
|
||||
const usb_vid_pid_t n64_devices[] =
|
||||
{
|
||||
@@ -46,22 +46,21 @@ typedef struct __attribute__((packed))
|
||||
uint16_t buttons;
|
||||
} N64USBReport;
|
||||
|
||||
struct N64USBState
|
||||
struct N64USBState
|
||||
{
|
||||
uint8_t player_id = {0};
|
||||
uint8_t dev_addr = {0};
|
||||
uint8_t instance = {0};
|
||||
};
|
||||
|
||||
class N64USB: public GPHostDriver
|
||||
class N64USB
|
||||
{
|
||||
public:
|
||||
virtual void init(uint8_t player_id, uint8_t dev_addr, uint8_t instance);
|
||||
virtual void process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
|
||||
virtual void process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len);
|
||||
virtual void hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
virtual bool send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance);
|
||||
void init(uint8_t dev_addr, uint8_t instance);
|
||||
void process_report(uint8_t const* report, uint16_t len);
|
||||
bool send_fb_data();
|
||||
private:
|
||||
N64USBState n64usb;
|
||||
void update_gamepad(Gamepad& gp, const N64USBReport* n64_data);
|
||||
void update_gamepad(const N64USBReport* n64_data);
|
||||
};
|
||||
|
||||
#endif // _N64USB_H_
|
||||
232
src/usbh/tusb_hid/ps3.cpp
Normal file
232
src/usbh/tusb_hid/ps3.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "tusb.h"
|
||||
#include "class/hid/hid_host.h"
|
||||
|
||||
#include "utilities/scaling.h"
|
||||
#include "usbh/tusb_hid/ps3.h"
|
||||
#include "Gamepad.h"
|
||||
|
||||
#include "utilities/log.h"
|
||||
|
||||
/* ---------------------------- */
|
||||
/* this does not work currently */
|
||||
/* ---------------------------- */
|
||||
|
||||
void Dualshock3::init(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
dualshock3.dev_addr = dev_addr;
|
||||
dualshock3.instance = instance;
|
||||
|
||||
uint16_t vid, pid;
|
||||
tuh_vid_pid_get(dualshock3.dev_addr, &vid, &pid);
|
||||
if (vid == 0x054C && pid == 0x0268) dualshock3.sixaxis = true;
|
||||
|
||||
// tuh_hid_set_protocol(dualshock3.dev_addr, dualshock3.instance, HID_PROTOCOL_REPORT);
|
||||
// sleep_ms(200);
|
||||
if (enable_reports())
|
||||
{
|
||||
log("reports enabled, addr: %02X inst: %02X", dev_addr, instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
log("reports enable failed");
|
||||
}
|
||||
}
|
||||
|
||||
bool Dualshock3::enable_reports()
|
||||
{
|
||||
// uint8_t cmd_buf[4];
|
||||
// cmd_buf[0] = 0x42;
|
||||
// cmd_buf[1] = 0x0c;
|
||||
// cmd_buf[2] = 0x00;
|
||||
// cmd_buf[3] = 0x00;
|
||||
|
||||
// // if (tuh_hid_send_ready)
|
||||
// // {
|
||||
// dualshock3.report_enabled = tuh_hid_set_report(dualshock3.dev_addr, dualshock3.instance, 0xF4, HID_REPORT_TYPE_FEATURE, &cmd_buf, sizeof(cmd_buf));
|
||||
// // }
|
||||
|
||||
static uint8_t buffer[5] = {};
|
||||
buffer[0] = 0xF4;
|
||||
buffer[1] = 0x42;
|
||||
buffer[2] = 0x03;
|
||||
buffer[3] = 0x00;
|
||||
buffer[4] = 0x00;
|
||||
|
||||
dualshock3.report_enabled = tuh_hid_set_report(dualshock3.dev_addr, dualshock3.instance, 0, HID_REPORT_TYPE_FEATURE, buffer, sizeof(buffer));
|
||||
|
||||
tuh_hid_send_report(dualshock3.dev_addr, dualshock3.instance, 0, buffer, sizeof(buffer));
|
||||
|
||||
if (dualshock3.report_enabled)
|
||||
{
|
||||
tuh_hid_receive_report(dualshock3.dev_addr, dualshock3.instance);
|
||||
}
|
||||
|
||||
return dualshock3.report_enabled;
|
||||
}
|
||||
|
||||
// void Dualshock3::reset_state()
|
||||
// {
|
||||
// dualshock3.report_enabled = false;
|
||||
// dualshock3.dev_addr = 0;
|
||||
// dualshock3.instance = 0;
|
||||
// }
|
||||
|
||||
void Dualshock3::update_gamepad_dinput(const DInputReport* dinput_report)
|
||||
{
|
||||
gamepad.reset_state();
|
||||
|
||||
switch (dinput_report->direction)
|
||||
{
|
||||
case DINPUT_HAT_UP:
|
||||
gamepad.state.up = true;
|
||||
break;
|
||||
case DINPUT_HAT_UPRIGHT:
|
||||
gamepad.state.up = true;
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case DINPUT_HAT_RIGHT:
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case DINPUT_HAT_DOWNRIGHT:
|
||||
gamepad.state.right = true;
|
||||
gamepad.state.down = true;
|
||||
break;
|
||||
case DINPUT_HAT_DOWN:
|
||||
gamepad.state.down = true;
|
||||
break;
|
||||
case DINPUT_HAT_DOWNLEFT:
|
||||
gamepad.state.down = true;
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case DINPUT_HAT_LEFT:
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case DINPUT_HAT_UPLEFT:
|
||||
gamepad.state.up = true;
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dinput_report->square_btn) gamepad.state.x = true;
|
||||
if (dinput_report->triangle_btn) gamepad.state.y = true;
|
||||
if (dinput_report->cross_btn) gamepad.state.a = true;
|
||||
if (dinput_report->circle_btn) gamepad.state.b = true;
|
||||
|
||||
if (dinput_report->select_btn) gamepad.state.back = true;
|
||||
if (dinput_report->start_btn) gamepad.state.start = true;
|
||||
if (dinput_report->ps_btn) gamepad.state.sys = true;
|
||||
|
||||
if (dinput_report->l3_btn) gamepad.state.l3 = true;
|
||||
if (dinput_report->r3_btn) gamepad.state.r3 = true;
|
||||
|
||||
if (dinput_report->l1_btn) gamepad.state.lb = true;
|
||||
if (dinput_report->r1_btn) gamepad.state.rb = true;
|
||||
|
||||
if (dinput_report->l2_btn) gamepad.state.lt = 0xFF;
|
||||
if (dinput_report->r2_btn) gamepad.state.rt = 0xFF;
|
||||
|
||||
gamepad.state.lx = scale_uint8_to_int16(dinput_report->l_x_axis, false);
|
||||
gamepad.state.ly = scale_uint8_to_int16(dinput_report->l_y_axis, true);
|
||||
gamepad.state.rx = scale_uint8_to_int16(dinput_report->r_x_axis, false);
|
||||
gamepad.state.ry = scale_uint8_to_int16(dinput_report->r_y_axis, true);
|
||||
}
|
||||
|
||||
void Dualshock3::update_gamepad(const DualShock3Report* ds3_data)
|
||||
{
|
||||
gamepad.reset_state();
|
||||
|
||||
if (ds3_data->up) gamepad.state.up =true;
|
||||
if (ds3_data->down) gamepad.state.down =true;
|
||||
if (ds3_data->left) gamepad.state.left =true;
|
||||
if (ds3_data->right) gamepad.state.right =true;
|
||||
|
||||
if (ds3_data->square) gamepad.state.x = true;
|
||||
if (ds3_data->triangle) gamepad.state.y = true;
|
||||
if (ds3_data->cross) gamepad.state.a = true;
|
||||
if (ds3_data->circle) gamepad.state.b = true;
|
||||
|
||||
if (ds3_data->select) gamepad.state.back = true;
|
||||
if (ds3_data->start) gamepad.state.start = true;
|
||||
if (ds3_data->ps) gamepad.state.sys = true;
|
||||
|
||||
if (ds3_data->l3) gamepad.state.l3 = true;
|
||||
if (ds3_data->r3) gamepad.state.r3 = true;
|
||||
|
||||
if (ds3_data->l1) gamepad.state.lb = true;
|
||||
if (ds3_data->r1) gamepad.state.rb = true;
|
||||
|
||||
if (ds3_data->l2) gamepad.state.lt = 0xFF;
|
||||
if (ds3_data->r2) gamepad.state.rt = 0xFF;
|
||||
|
||||
gamepad.state.lx = scale_uint8_to_int16(ds3_data->left_x, false);
|
||||
gamepad.state.ly = scale_uint8_to_int16(ds3_data->left_y, true);
|
||||
gamepad.state.rx = scale_uint8_to_int16(ds3_data->right_x, false);
|
||||
gamepad.state.ry = scale_uint8_to_int16(ds3_data->right_y, true);
|
||||
}
|
||||
|
||||
void Dualshock3::process_report(uint8_t const* report, uint16_t len)
|
||||
{
|
||||
// if (report[0] != 0x01) return;
|
||||
|
||||
// print hex values
|
||||
char hex_buffer[len * 3];
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
sprintf(hex_buffer + (i * 3), "%02X ", report[i]); // Convert byte to hexadecimal string
|
||||
}
|
||||
log(hex_buffer);
|
||||
|
||||
if (!dualshock3.sixaxis)
|
||||
{
|
||||
static DInputReport prev_report = {0};
|
||||
DInputReport dinput_report;
|
||||
memcpy(&dinput_report, report, sizeof(dinput_report));
|
||||
update_gamepad_dinput(&dinput_report);
|
||||
prev_report = dinput_report;
|
||||
}
|
||||
else
|
||||
{
|
||||
static DualShock3Report prev_report = { 0 };
|
||||
DualShock3Report ds3_report;
|
||||
memcpy(&ds3_report, report, sizeof(ds3_report));
|
||||
update_gamepad(&ds3_report);
|
||||
prev_report = ds3_report;
|
||||
}
|
||||
}
|
||||
|
||||
bool Dualshock3::send_fb_data()
|
||||
{
|
||||
uint8_t default_report[] = {
|
||||
0x01, 0xff, 0x00, 0xff, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
struct sixaxis_output_report output_report;
|
||||
|
||||
memcpy(&output_report, default_report, sizeof(output_report));
|
||||
|
||||
// default_output_report.leds_bitmap |= 0x1 << (dualshock3.instance+1);
|
||||
output_report.leds_bitmap = 0x02;
|
||||
// output_report.led->time_enabled = 0xFF;
|
||||
// output_report.led->duty_on = 0xFF;
|
||||
|
||||
output_report.rumble.right_duration = UINT8_MAX / 2;
|
||||
if (gamepadOut.out_state.rrumble > 0) output_report.rumble.right_motor_on = 1;
|
||||
|
||||
output_report.rumble.left_duration = UINT8_MAX / 2;
|
||||
output_report.rumble.left_motor_force = gamepadOut.out_state.lrumble;
|
||||
|
||||
bool rumble_sent = tuh_hid_send_report(dualshock3.dev_addr, dualshock3.instance, 1, &output_report, sizeof(output_report));
|
||||
|
||||
return rumble_sent;
|
||||
// return true;
|
||||
}
|
||||
49
src/usbh/tusb_hid/ps3.h
Normal file
49
src/usbh/tusb_hid/ps3.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _PS3_H_
|
||||
#define _PS3_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usbh/tusb_hid/shared.h"
|
||||
#include "descriptors/DInputDescriptors.h"
|
||||
#include "descriptors/PS3Descriptors.h"
|
||||
|
||||
#define PS3_REPORT_BUFFER_SIZE 48
|
||||
|
||||
const usb_vid_pid_t ps3_devices[] =
|
||||
{
|
||||
{0x054C, 0x0268}, // Sony Batoh (Dualshock 3)
|
||||
{0x2563, 0x0575}, // Nuplay Armor 3
|
||||
// {0x045E, 0x028E}, // Voyee generic "P3", won't work, IDs are for a 360 controller (dumb)
|
||||
{0x044F, 0xB324}, // ThrustMaster Dual Trigger (PS3 mode)
|
||||
{0x0738, 0x8818}, // MadCatz Street Fighter IV Arcade FightStick
|
||||
{0x0810, 0x0003}, // Personal Communication Systems, Inc. Generic
|
||||
{0x146B, 0x0902} // BigBen Interactive Wired Mini PS3 Game Controller
|
||||
};
|
||||
|
||||
struct Dualshock3State
|
||||
{
|
||||
bool report_enabled {false};
|
||||
bool sixaxis {false};
|
||||
uint8_t dev_addr = {0};
|
||||
uint8_t instance = {0};
|
||||
};
|
||||
|
||||
class Dualshock3
|
||||
{
|
||||
public:
|
||||
void init(uint8_t dev_addr, uint8_t instance);
|
||||
void process_report(uint8_t const* report, uint16_t len);
|
||||
bool send_fb_data();
|
||||
|
||||
private:
|
||||
Dualshock3State dualshock3;
|
||||
|
||||
bool enable_reports();
|
||||
// void reset_state();
|
||||
void update_gamepad(const DualShock3Report* ds3_data);
|
||||
void update_gamepad_dinput(const DInputReport* dinput_report);
|
||||
};
|
||||
|
||||
#endif // _PS3_H_
|
||||
138
src/usbh/tusb_hid/ps4.cpp
Normal file
138
src/usbh/tusb_hid/ps4.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
#include "CRC32.h"
|
||||
|
||||
#include "usbh/tusb_hid/ps4.h"
|
||||
|
||||
#include "utilities/scaling.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
void Dualshock4::init(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
dualshock4.dev_addr = dev_addr;
|
||||
dualshock4.instance = instance;
|
||||
// set_leds();
|
||||
}
|
||||
|
||||
/* this DCs the controller, come back to it */
|
||||
bool Dualshock4::set_leds()
|
||||
{
|
||||
// see: https://github.com/Hydr8gon/VitaControl
|
||||
|
||||
static uint8_t buffer[79] = {};
|
||||
buffer[0] = 0xA2;
|
||||
buffer[1] = 0x11;
|
||||
buffer[2] = 0xC0;
|
||||
buffer[3] = 0x20;
|
||||
buffer[4] = 0xF3;
|
||||
buffer[5] = 0x04;
|
||||
buffer[9] = led_colors[dualshock4.instance][0];
|
||||
buffer[10] = led_colors[dualshock4.instance][1];
|
||||
buffer[11] = led_colors[dualshock4.instance][2];
|
||||
|
||||
// Calculate the CRC of the data (including the 0xA2 byte) and append it to the end
|
||||
uint32_t crc = CRC32::calculate(buffer, 75);
|
||||
buffer[75] = crc >> 0;
|
||||
buffer[76] = crc >> 8;
|
||||
buffer[77] = crc >> 16;
|
||||
buffer[78] = crc >> 24;
|
||||
|
||||
// Send the write request, omitting the 0xA2 byte
|
||||
dualshock4.leds_set = tuh_hid_send_report(dualshock4.dev_addr, dualshock4.instance, 0, &buffer + 1, sizeof(buffer) - 1);
|
||||
|
||||
return dualshock4.leds_set;
|
||||
}
|
||||
|
||||
void Dualshock4::update_gamepad(const Dualshock4Report* ds4_data)
|
||||
{
|
||||
gamepad.reset_state();
|
||||
|
||||
switch(ds4_data->dpad)
|
||||
{
|
||||
case PS4_DPAD_MASK_UP:
|
||||
gamepad.state.up = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_UP_RIGHT:
|
||||
gamepad.state.up = true;
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_RIGHT:
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_RIGHT_DOWN:
|
||||
gamepad.state.right = true;
|
||||
gamepad.state.down = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_DOWN:
|
||||
gamepad.state.down = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_DOWN_LEFT:
|
||||
gamepad.state.down = true;
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_LEFT:
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case PS4_DPAD_MASK_LEFT_UP:
|
||||
gamepad.state.left = true;
|
||||
gamepad.state.up = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ds4_data->square) gamepad.state.x = true;
|
||||
if (ds4_data->cross) gamepad.state.a = true;
|
||||
if (ds4_data->circle) gamepad.state.b = true;
|
||||
if (ds4_data->triangle) gamepad.state.y = true;
|
||||
|
||||
if (ds4_data->share) gamepad.state.back = true;
|
||||
if (ds4_data->option) gamepad.state.start = true;
|
||||
if (ds4_data->ps) gamepad.state.sys = true;
|
||||
if (ds4_data->tpad) gamepad.state.misc = true;
|
||||
|
||||
if (ds4_data->l1) gamepad.state.lb = true;
|
||||
if (ds4_data->r1) gamepad.state.rb = true;
|
||||
|
||||
if (ds4_data->l3) gamepad.state.l3 = true;
|
||||
if (ds4_data->r3) gamepad.state.r3 = true;
|
||||
|
||||
gamepad.state.lt = ds4_data->l2_trigger;
|
||||
gamepad.state.rt = ds4_data->r2_trigger;
|
||||
|
||||
gamepad.state.lx = scale_uint8_to_int16(ds4_data->lx, false);
|
||||
gamepad.state.ly = scale_uint8_to_int16(ds4_data->ly, true);
|
||||
gamepad.state.rx = scale_uint8_to_int16(ds4_data->rx, false);
|
||||
gamepad.state.ry = scale_uint8_to_int16(ds4_data->ry, true);
|
||||
}
|
||||
|
||||
void Dualshock4::process_report(uint8_t const* report, uint16_t len)
|
||||
{
|
||||
// if (report[0] != REPORT_ID_GAMEPAD_STATE)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
static Dualshock4Report prev_report = { 0 };
|
||||
|
||||
Dualshock4Report ds4_report;
|
||||
|
||||
memcpy(&ds4_report, report, sizeof(ds4_report));
|
||||
|
||||
if (memcmp(&ds4_report, &prev_report, sizeof(ds4_report)) != 0)
|
||||
{
|
||||
update_gamepad(&ds4_report);
|
||||
|
||||
prev_report = ds4_report;
|
||||
}
|
||||
}
|
||||
|
||||
bool Dualshock4::send_fb_data()
|
||||
{
|
||||
Dualshock4OutReport output_report = {0};
|
||||
output_report.set_rumble = 1;
|
||||
output_report.motor_left = gamepadOut.out_state.lrumble;
|
||||
output_report.motor_right = gamepadOut.out_state.rrumble;
|
||||
|
||||
return tuh_hid_send_report(dualshock4.dev_addr, dualshock4.instance, 5, &output_report, sizeof(output_report));
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _DUALSHOCK4_H_
|
||||
#define _DUALSHOCK4_H_
|
||||
#ifndef _PS4_H_
|
||||
#define _PS4_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usbh/GPHostDriver.h"
|
||||
#include "usbh/tusb_hid/shared.h"
|
||||
|
||||
#define REPORT_ID_GAMEPAD_STATE 0x11
|
||||
|
||||
const usb_vid_pid_t ps4_devices[] =
|
||||
{
|
||||
@@ -26,7 +28,7 @@ static const uint8_t led_colors[][3] =
|
||||
{ 0x20, 0x00, 0x20 }, // Pink
|
||||
};
|
||||
|
||||
enum Dualshock4DpadMask
|
||||
enum dualshock4_dpad_mask
|
||||
{
|
||||
PS4_DPAD_MASK_UP = 0x00,
|
||||
PS4_DPAD_MASK_UP_RIGHT = 0x01,
|
||||
@@ -118,22 +120,21 @@ typedef struct __attribute__((packed))
|
||||
|
||||
struct Dualshock4State
|
||||
{
|
||||
uint8_t player_id = {0};
|
||||
uint8_t dev_addr = {0};
|
||||
uint8_t instance = {0};
|
||||
bool leds_set = {false};
|
||||
};
|
||||
|
||||
class Dualshock4 : public GPHostDriver
|
||||
class Dualshock4
|
||||
{
|
||||
public:
|
||||
virtual void init(uint8_t player_id, uint8_t dev_addr, uint8_t instance);
|
||||
virtual void process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
|
||||
virtual void process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len);
|
||||
virtual void hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
virtual bool send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance);
|
||||
void init(uint8_t dev_addr, uint8_t instance);
|
||||
void process_report(uint8_t const* report, uint16_t len);
|
||||
bool send_fb_data();
|
||||
private:
|
||||
Dualshock4State dualshock4;
|
||||
void update_gamepad(Gamepad& gp, const Dualshock4Report* ds4_data);
|
||||
bool set_leds(uint8_t dev_addr, uint8_t instance);
|
||||
void update_gamepad(const Dualshock4Report* ds4_data);
|
||||
bool set_leds();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // _PS4_H_
|
||||
105
src/usbh/tusb_hid/ps5.cpp
Normal file
105
src/usbh/tusb_hid/ps5.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#include "usbh/tusb_hid/ps5.h"
|
||||
#include "utilities/scaling.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
void Dualsense::init(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
dualsense.dev_addr = dev_addr;
|
||||
dualsense.instance = instance;
|
||||
}
|
||||
|
||||
void Dualsense::update_gamepad(const DualsenseReport* ds_data)
|
||||
{
|
||||
gamepad.reset_state();
|
||||
|
||||
switch(ds_data->dpad)
|
||||
{
|
||||
case PS5_MASK_DPAD_UP:
|
||||
gamepad.state.up = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_UP_RIGHT:
|
||||
gamepad.state.up = true;
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_RIGHT:
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_RIGHT_DOWN:
|
||||
gamepad.state.right = true;
|
||||
gamepad.state.down = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_DOWN:
|
||||
gamepad.state.down = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_DOWN_LEFT:
|
||||
gamepad.state.down = true;
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_LEFT:
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case PS5_MASK_DPAD_LEFT_UP:
|
||||
gamepad.state.left = true;
|
||||
gamepad.state.up = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ds_data->square) gamepad.state.x = true;
|
||||
if (ds_data->cross) gamepad.state.a = true;
|
||||
if (ds_data->circle) gamepad.state.b = true;
|
||||
if (ds_data->triangle) gamepad.state.y = true;
|
||||
|
||||
if (ds_data->buttons[0] & PS5_MASK_L1) gamepad.state.lb = true;
|
||||
if (ds_data->buttons[0] & PS5_MASK_R1) gamepad.state.rb = true;
|
||||
|
||||
if (ds_data->buttons[0] & PS5_MASK_SHARE) gamepad.state.back = true;
|
||||
if (ds_data->buttons[0] & PS5_MASK_OPTIONS) gamepad.state.start = true;
|
||||
|
||||
if (ds_data->buttons[0] & PS5_MASK_L3) gamepad.state.l3 = true;
|
||||
if (ds_data->buttons[0] & PS5_MASK_R3) gamepad.state.r3 = true;
|
||||
|
||||
if (ds_data->buttons[1] & PS5_MASK_PS) gamepad.state.sys = true;
|
||||
if (ds_data->buttons[1] & PS5_MASK_MIC) gamepad.state.misc = true;
|
||||
|
||||
gamepad.state.lt = ds_data->lt;
|
||||
gamepad.state.rt = ds_data->rt;
|
||||
|
||||
gamepad.state.lx = scale_uint8_to_int16(ds_data->lx, false);
|
||||
gamepad.state.ly = scale_uint8_to_int16(ds_data->ly, true);
|
||||
gamepad.state.rx = scale_uint8_to_int16(ds_data->rx, false);
|
||||
gamepad.state.ry = scale_uint8_to_int16(ds_data->ry, true);
|
||||
}
|
||||
|
||||
/* not much point in comparing changes unless we only look at buttons */
|
||||
void Dualsense::process_report(uint8_t const* report, uint16_t len)
|
||||
{
|
||||
// static DualsenseReport prev_report = { 0 };
|
||||
|
||||
DualsenseReport ds_report;
|
||||
memcpy(&ds_report, report, sizeof(ds_report));
|
||||
|
||||
// if (memcmp(&ds_report, &prev_report, sizeof(ds_report)) != 0)
|
||||
// {
|
||||
update_gamepad(&ds_report);
|
||||
|
||||
// prev_report = ds_report;
|
||||
// }
|
||||
}
|
||||
|
||||
bool Dualsense::send_fb_data()
|
||||
{
|
||||
// need to figure out if the flags are necessary and how the LEDs work
|
||||
DualsenseOutReport output_report = {0};
|
||||
output_report.valid_flag0 = 0x02; // idk what this means
|
||||
output_report.valid_flag1 = 0x02; // this one either
|
||||
output_report.valid_flag2 = 0x04; // uhhhhh
|
||||
output_report.motor_left = gamepadOut.out_state.lrumble;
|
||||
output_report.motor_right = gamepadOut.out_state.rrumble;
|
||||
|
||||
return tuh_hid_send_report(dualsense.dev_addr, dualsense.instance, 5, &output_report, sizeof(output_report));
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
#ifndef _DUALSENSE_H_
|
||||
#define _DUALSENSE_H_
|
||||
#pragma once
|
||||
|
||||
#ifndef _PS5_H_
|
||||
#define _PS5_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usbh/GPHostDriver.h"
|
||||
#include "usbh/tusb_hid/shared.h"
|
||||
|
||||
const usb_vid_pid_t ps5_devices[] =
|
||||
{
|
||||
@@ -129,22 +131,21 @@ struct DualsenseOutReport {
|
||||
uint8_t lightbar_blue;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct DualsenseState
|
||||
struct DualsenseState
|
||||
{
|
||||
uint8_t player_id = {0};
|
||||
uint8_t dev_addr = {0};
|
||||
uint8_t instance = {0};
|
||||
};
|
||||
|
||||
class Dualsense : public GPHostDriver
|
||||
class Dualsense
|
||||
{
|
||||
public:
|
||||
virtual void init(uint8_t player_id, uint8_t dev_addr, uint8_t instance);
|
||||
virtual void process_hid_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
|
||||
virtual void process_xinput_report(Gamepad& gamepad, uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len);
|
||||
virtual void hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
virtual bool send_fb_data(const Gamepad& gamepad, uint8_t dev_addr, uint8_t instance);
|
||||
void init(uint8_t dev_addr, uint8_t instance);
|
||||
void process_report(uint8_t const* report, uint16_t len);
|
||||
bool send_fb_data();
|
||||
private:
|
||||
DualsenseState dualsense;
|
||||
void update_gamepad(Gamepad& gp, const DualsenseReport* ds_report);
|
||||
void update_gamepad(const DualsenseReport* ds_data);
|
||||
};
|
||||
|
||||
#endif // _DUALSENSE_H_
|
||||
#endif // _PS5_H_
|
||||
84
src/usbh/tusb_hid/psclassic.cpp
Normal file
84
src/usbh/tusb_hid/psclassic.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#include "usbh/tusb_hid/psclassic.h"
|
||||
// #include "usbh/tusb_hid/ps4.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
void PSClassic::init(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
psclassic.dev_addr = dev_addr;
|
||||
psclassic.instance = instance;
|
||||
}
|
||||
|
||||
void PSClassic::update_gamepad(const PSClassicReport* psc_data)
|
||||
{
|
||||
gamepad.reset_state();
|
||||
|
||||
switch (psc_data->buttons & 0x3C00) {
|
||||
case PSCLASSIC_MASK_UP_LEFT:
|
||||
gamepad.state.up = true;
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_UP:
|
||||
gamepad.state.up = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_UP_RIGHT:
|
||||
gamepad.state.up = true;
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_LEFT:
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_RIGHT:
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_DOWN_LEFT:
|
||||
gamepad.state.down = true;
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_DOWN:
|
||||
gamepad.state.down = true;
|
||||
break;
|
||||
case PSCLASSIC_MASK_DOWN_RIGHT:
|
||||
gamepad.state.down = true;
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_TRIANGLE) gamepad.state.y = true;
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_CIRCLE) gamepad.state.b = true;
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_CROSS) gamepad.state.a = true;
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_SQUARE) gamepad.state.x = true;
|
||||
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_L2) gamepad.state.lt = 0xFF;
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_R2) gamepad.state.rt = 0xFF;
|
||||
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_L1) gamepad.state.lb = true;
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_R1) gamepad.state.rb = true;
|
||||
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_SELECT) gamepad.state.back = true;
|
||||
if (psc_data->buttons & PSCLASSIC_MASK_START) gamepad.state.start = true;
|
||||
}
|
||||
|
||||
void PSClassic::process_report(uint8_t const* report, uint16_t len)
|
||||
{
|
||||
static PSClassicReport prev_report = { 0 };
|
||||
|
||||
PSClassicReport psc_report;
|
||||
memcpy(&psc_report, report, sizeof(psc_report));
|
||||
|
||||
if (memcmp(&psc_report, &prev_report, sizeof(psc_report)) != 0)
|
||||
{
|
||||
update_gamepad(&psc_report);
|
||||
|
||||
prev_report = psc_report;
|
||||
}
|
||||
}
|
||||
|
||||
bool PSClassic::send_fb_data()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
34
src/usbh/tusb_hid/psclassic.h
Normal file
34
src/usbh/tusb_hid/psclassic.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _PSCLASSIC_H_
|
||||
#define _PSCLASSIC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usbh/tusb_hid/shared.h"
|
||||
|
||||
#include "descriptors/PSClassicDescriptors.h"
|
||||
|
||||
const usb_vid_pid_t psc_devices[] =
|
||||
{
|
||||
{0x054C, 0x0CDA} // psclassic
|
||||
};
|
||||
|
||||
struct PSClassicState
|
||||
{
|
||||
uint8_t dev_addr = {0};
|
||||
uint8_t instance = {0};
|
||||
};
|
||||
|
||||
class PSClassic
|
||||
{
|
||||
public:
|
||||
void init(uint8_t dev_addr, uint8_t instance);
|
||||
void process_report(uint8_t const* report, uint16_t len);
|
||||
bool send_fb_data();
|
||||
private:
|
||||
PSClassicState psclassic;
|
||||
void update_gamepad(const PSClassicReport* psc_data);
|
||||
};
|
||||
|
||||
#endif // _PSCLASSIC_H_
|
||||
266
src/usbh/tusb_hid/switch_pro.cpp
Normal file
266
src/usbh/tusb_hid/switch_pro.cpp
Normal file
@@ -0,0 +1,266 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include <cmath>
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#include "usbh/tusb_hid/switch_pro.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
// commands
|
||||
#define CMD_HID 0x80
|
||||
#define SUBCMD_HANDSHAKE 0x02
|
||||
#define SUBCMD_DISABLE_TIMEOUT 0x04
|
||||
|
||||
// out report commands
|
||||
#define CMD_RUMBLE_ONLY 0x10
|
||||
#define CMD_AND_RUMBLE 0x01
|
||||
|
||||
// out report subcommands
|
||||
#define CMD_LED 0x30
|
||||
#define CMD_LED_HOME 0x38
|
||||
#define CMD_GYRO 0x40
|
||||
#define CMD_MODE 0x03
|
||||
#define SUBCMD_FULL_REPORT_MODE 0x30
|
||||
|
||||
void SwitchPro::init(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
reset_state();
|
||||
|
||||
switch_pro.dev_addr = dev_addr;
|
||||
switch_pro.instance = instance;
|
||||
|
||||
if (tuh_hid_send_ready)
|
||||
{
|
||||
uint8_t handshake_command[2] = {CMD_HID, SUBCMD_HANDSHAKE};
|
||||
switch_pro.handshake_sent = tuh_hid_send_report(switch_pro.dev_addr, switch_pro.instance, 0, handshake_command, sizeof(handshake_command));
|
||||
}
|
||||
}
|
||||
|
||||
void SwitchPro::reset_state()
|
||||
{
|
||||
switch_pro.handshake_sent = false;
|
||||
switch_pro.timeout_disabled = false;
|
||||
switch_pro.full_report_enabled = false;
|
||||
switch_pro.led_set = false;
|
||||
switch_pro.led_home_set = false;
|
||||
switch_pro.imu_enabled = false;
|
||||
switch_pro.dev_addr = 0;
|
||||
switch_pro.instance = 0;
|
||||
switch_pro.output_sequence_counter = 0;
|
||||
}
|
||||
|
||||
uint8_t SwitchPro::get_output_sequence_counter()
|
||||
{
|
||||
// increments each report, resets to 0 after 15
|
||||
uint8_t counter = switch_pro.output_sequence_counter;
|
||||
switch_pro.output_sequence_counter = (switch_pro.output_sequence_counter + 1) & 0x0F;
|
||||
return counter;
|
||||
}
|
||||
|
||||
bool SwitchPro::disable_timeout()
|
||||
{
|
||||
uint8_t disable_timeout_command[2] = {CMD_HID, SUBCMD_DISABLE_TIMEOUT};
|
||||
|
||||
if (tuh_hid_send_ready)
|
||||
{
|
||||
switch_pro.timeout_disabled = tuh_hid_send_report(switch_pro.dev_addr, switch_pro.instance, 0, disable_timeout_command, sizeof(disable_timeout_command));
|
||||
}
|
||||
|
||||
return switch_pro.timeout_disabled;
|
||||
}
|
||||
|
||||
void SwitchPro::process_report(uint8_t const* report, uint16_t len)
|
||||
{
|
||||
if (switch_pro.handshake_sent)
|
||||
{
|
||||
// this is here to the respond to handshake, doesn't seem to work otherwise
|
||||
if (!switch_pro.timeout_disabled)
|
||||
{
|
||||
disable_timeout();
|
||||
return;
|
||||
}
|
||||
|
||||
static SwitchProReport prev_report = {0};
|
||||
|
||||
SwitchProReport switch_report;
|
||||
memcpy(&switch_report, report, sizeof(switch_report));
|
||||
|
||||
if (memcmp(&switch_report, &prev_report, sizeof(switch_report)) != 0)
|
||||
{
|
||||
update_gamepad(&switch_report);
|
||||
|
||||
prev_report = switch_report;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16_t SwitchPro::normalize_axes(uint16_t value)
|
||||
{
|
||||
/* 12bit value from the controller doesnt cover the full 12bit range seemingly
|
||||
doesn't seem completely centered at 2047 so I may be missing something here
|
||||
tried to get as close as possible with the multiplier */
|
||||
|
||||
int32_t normalized_value = (value - 2047) * 22;
|
||||
|
||||
if (normalized_value < -32768)
|
||||
{
|
||||
normalized_value = -32768;
|
||||
}
|
||||
else if (normalized_value > 32767)
|
||||
{
|
||||
normalized_value = 32767;
|
||||
}
|
||||
|
||||
return (int16_t)normalized_value;
|
||||
}
|
||||
|
||||
void SwitchPro::update_gamepad(const SwitchProReport* switch_report)
|
||||
{
|
||||
gamepad.reset_state();
|
||||
|
||||
if (switch_report->up) gamepad.state.up =true;
|
||||
if (switch_report->down) gamepad.state.down =true;
|
||||
if (switch_report->left) gamepad.state.left =true;
|
||||
if (switch_report->right) gamepad.state.right =true;
|
||||
|
||||
if (switch_report->y) gamepad.state.x = true;
|
||||
if (switch_report->x) gamepad.state.y = true;
|
||||
if (switch_report->b) gamepad.state.a = true;
|
||||
if (switch_report->a) gamepad.state.b = true;
|
||||
|
||||
if (switch_report->minus) gamepad.state.back = true;
|
||||
if (switch_report->plus) gamepad.state.start = true;
|
||||
if (switch_report->home) gamepad.state.sys = true;
|
||||
if (switch_report->capture) gamepad.state.misc = true;
|
||||
|
||||
if (switch_report->stickL) gamepad.state.l3 = true;
|
||||
if (switch_report->stickR) gamepad.state.r3 = true;
|
||||
|
||||
if (switch_report->l) gamepad.state.lb = true;
|
||||
if (switch_report->r) gamepad.state.rb = true;
|
||||
|
||||
if (switch_report->zl) gamepad.state.lt = 0xFF;
|
||||
if (switch_report->zr) gamepad.state.rt = 0xFF;
|
||||
|
||||
gamepad.state.lx = normalize_axes(switch_report->leftX );
|
||||
gamepad.state.ly = normalize_axes(switch_report->leftY );
|
||||
gamepad.state.rx = normalize_axes(switch_report->rightX);
|
||||
gamepad.state.ry = normalize_axes(switch_report->rightY);
|
||||
}
|
||||
|
||||
bool SwitchPro::send_fb_data()
|
||||
{
|
||||
if (!switch_pro.handshake_sent || !switch_pro.timeout_disabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// See: https://github.com/Dan611/hid-procon
|
||||
// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
|
||||
// https://github.com/HisashiKato/USB_Host_Shield_Library_2.0
|
||||
|
||||
uint8_t buffer[14] = { 0 };
|
||||
uint8_t report_size = 10;
|
||||
|
||||
buffer[1] = get_output_sequence_counter();
|
||||
|
||||
// whoever came up with hd rumble is some kind of sick freak, I'm just guessing here
|
||||
|
||||
if (gamepadOut.out_state.lrumble > 0)
|
||||
{
|
||||
// full on
|
||||
// buffer[2] = 0x28;
|
||||
// buffer[3] = 0x88;
|
||||
// buffer[4] = 0x60;
|
||||
// buffer[5] = 0x61;
|
||||
|
||||
// uint8_t amplitude_l = static_cast<uint8_t>((gamepadOut.out_state.lrumble / 255.0) * (0xC0 - 0x40) + 0x40);
|
||||
uint8_t amplitude_l = static_cast<uint8_t>(((gamepadOut.out_state.lrumble / 255.0f) * 0.8f + 0.5f) * (0xC0 - 0x40) + 0x40);
|
||||
|
||||
buffer[2] = amplitude_l;
|
||||
buffer[3] = 0x88;
|
||||
buffer[4] = amplitude_l / 2;
|
||||
buffer[5] = 0x61;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[2] = 0x00;
|
||||
buffer[3] = 0x01;
|
||||
buffer[4] = 0x40;
|
||||
buffer[5] = 0x40;
|
||||
}
|
||||
|
||||
if (gamepadOut.out_state.rrumble > 0)
|
||||
{
|
||||
// full on
|
||||
// buffer[6] = 0x28;
|
||||
// buffer[7] = 0x88;
|
||||
// buffer[8] = 0x60;
|
||||
// buffer[9] = 0x61;
|
||||
|
||||
// uint8_t amplitude_r = static_cast<uint8_t>((gamepadOut.out_state.rrumble / 255.0) * (0xC0 - 0x40) + 0x40);
|
||||
uint8_t amplitude_r = static_cast<uint8_t>(((gamepadOut.out_state.rrumble / 255.0f) * 0.8f + 0.5f) * (0xC0 - 0x40) + 0x40);
|
||||
|
||||
buffer[6] = amplitude_r;
|
||||
buffer[7] = 0x88;
|
||||
buffer[8] = amplitude_r / 2;
|
||||
buffer[9] = 0x61;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[6] = 0x00;
|
||||
buffer[7] = 0x01;
|
||||
buffer[8] = 0x40;
|
||||
buffer[9] = 0x40;
|
||||
}
|
||||
|
||||
if (!switch_pro.commands_sent)
|
||||
{
|
||||
buffer[0] = CMD_AND_RUMBLE;
|
||||
|
||||
if (!switch_pro.led_set)
|
||||
{
|
||||
report_size = 12;
|
||||
buffer[10] = CMD_LED;
|
||||
buffer[11] = 0x01;
|
||||
switch_pro.led_set = tuh_hid_send_report(switch_pro.dev_addr, switch_pro.instance, 0, &buffer, report_size);
|
||||
return switch_pro.led_set;
|
||||
}
|
||||
else if (!switch_pro.led_home_set)
|
||||
{
|
||||
report_size = 14;
|
||||
buffer[10] = CMD_LED_HOME;
|
||||
buffer[11] = (0 /* Number of cycles */ << 4) | (true ? 0xF : 0);
|
||||
buffer[12] = (0xF /* LED start intensity */ << 4) | 0x0 /* Number of full cycles */;
|
||||
buffer[13] = (0xF /* Mini Cycle 1 LED intensity */ << 4) | 0x0 /* Mini Cycle 2 LED intensity */;
|
||||
switch_pro.led_home_set = tuh_hid_send_report(switch_pro.dev_addr, switch_pro.instance, 0, &buffer, report_size);
|
||||
return switch_pro.led_home_set;
|
||||
}
|
||||
else if (!switch_pro.full_report_enabled)
|
||||
{
|
||||
report_size = 12;
|
||||
buffer[10] = CMD_MODE;
|
||||
buffer[11] = SUBCMD_FULL_REPORT_MODE;
|
||||
switch_pro.full_report_enabled = tuh_hid_send_report(switch_pro.dev_addr, switch_pro.instance, 0, &buffer, report_size);
|
||||
return switch_pro.full_report_enabled;
|
||||
}
|
||||
else if (!switch_pro.imu_enabled)
|
||||
{
|
||||
report_size = 12;
|
||||
buffer[10] = CMD_GYRO;
|
||||
buffer[11] = 1 ? 1 : 0;
|
||||
switch_pro.imu_enabled = tuh_hid_send_report(switch_pro.dev_addr, switch_pro.instance, 0, &buffer, report_size);
|
||||
return switch_pro.imu_enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch_pro.commands_sent = true;
|
||||
}
|
||||
}
|
||||
|
||||
buffer[0] = CMD_RUMBLE_ONLY;
|
||||
|
||||
return tuh_hid_send_report(switch_pro.dev_addr, switch_pro.instance, 0, &buffer, report_size);
|
||||
}
|
||||
95
src/usbh/tusb_hid/switch_pro.h
Normal file
95
src/usbh/tusb_hid/switch_pro.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _SWITCH_PRO_H_
|
||||
#define _SWITCH_PRO_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usbh/tusb_hid/shared.h"
|
||||
|
||||
const usb_vid_pid_t switch_pro_devices[] =
|
||||
{
|
||||
{0x057E, 0x2009} // Switch Pro
|
||||
};
|
||||
|
||||
struct SwitchProReport
|
||||
{
|
||||
uint8_t reportId;
|
||||
uint8_t timer;
|
||||
|
||||
uint8_t connInfo : 4;
|
||||
uint8_t battery : 4;
|
||||
|
||||
uint8_t y : 1;
|
||||
uint8_t x : 1;
|
||||
uint8_t b : 1;
|
||||
uint8_t a : 1;
|
||||
uint8_t : 2;
|
||||
uint8_t r : 1;
|
||||
uint8_t zr : 1;
|
||||
|
||||
uint8_t minus : 1;
|
||||
uint8_t plus : 1;
|
||||
uint8_t stickR : 1;
|
||||
uint8_t stickL : 1;
|
||||
uint8_t home : 1;
|
||||
uint8_t capture : 1;
|
||||
uint8_t : 0;
|
||||
|
||||
uint8_t down : 1;
|
||||
uint8_t up : 1;
|
||||
uint8_t right : 1;
|
||||
uint8_t left : 1;
|
||||
uint8_t : 2;
|
||||
uint8_t l : 1;
|
||||
uint8_t zl : 1;
|
||||
|
||||
uint16_t leftX : 12;
|
||||
uint16_t leftY : 12;
|
||||
uint16_t rightX : 12;
|
||||
uint16_t rightY : 12;
|
||||
|
||||
uint8_t vibrator;
|
||||
|
||||
uint16_t accelerX;
|
||||
uint16_t accelerY;
|
||||
uint16_t accelerZ;
|
||||
|
||||
uint16_t velocityX;
|
||||
uint16_t velocityY;
|
||||
uint16_t velocityZ;
|
||||
}
|
||||
__attribute__((packed));
|
||||
|
||||
struct SwitchProState
|
||||
{
|
||||
bool handshake_sent {false};
|
||||
bool timeout_disabled {false};
|
||||
bool full_report_enabled {false};
|
||||
bool led_set {false};
|
||||
bool led_home_set {false};
|
||||
bool imu_enabled {false};
|
||||
bool commands_sent {false};
|
||||
uint8_t output_sequence_counter {0};
|
||||
uint8_t dev_addr = {0};
|
||||
uint8_t instance = {0};
|
||||
};
|
||||
|
||||
class SwitchPro
|
||||
{
|
||||
public:
|
||||
void init(uint8_t dev_addr, uint8_t instance);
|
||||
void process_report(uint8_t const* report, uint16_t len);
|
||||
bool send_fb_data();
|
||||
|
||||
private:
|
||||
SwitchProState switch_pro;
|
||||
|
||||
bool disable_timeout();
|
||||
void reset_state();
|
||||
uint8_t get_output_sequence_counter();
|
||||
int16_t normalize_axes(uint16_t value);
|
||||
void update_gamepad(const SwitchProReport* switch_pro_data);
|
||||
};
|
||||
|
||||
#endif // _SWITCH_PRO_H_
|
||||
106
src/usbh/tusb_hid/switch_wired.cpp
Normal file
106
src/usbh/tusb_hid/switch_wired.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/time.h"
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#include "utilities/scaling.h"
|
||||
#include "descriptors/SwitchDescriptors.h"
|
||||
#include "usbh/tusb_hid/switch_wired.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
void SwitchWired::init(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
reset_state();
|
||||
switch_wired.dev_addr = dev_addr;
|
||||
switch_wired.instance = instance;
|
||||
}
|
||||
|
||||
void SwitchWired::reset_state()
|
||||
{
|
||||
switch_wired.dev_addr = 0;
|
||||
switch_wired.instance = 0;
|
||||
}
|
||||
|
||||
void SwitchWired::process_report(uint8_t const* report, uint16_t len)
|
||||
{
|
||||
static SwitchWiredReport prev_report = {0};
|
||||
|
||||
SwitchWiredReport switch_report;
|
||||
memcpy(&switch_report, report, sizeof(switch_report));
|
||||
|
||||
if (memcmp(&switch_report, &prev_report, sizeof(switch_report)) != 0)
|
||||
{
|
||||
update_gamepad(&switch_report);
|
||||
|
||||
prev_report = switch_report;
|
||||
}
|
||||
}
|
||||
|
||||
void SwitchWired::update_gamepad(const SwitchWiredReport* switch_report)
|
||||
{
|
||||
gamepad.reset_state();
|
||||
|
||||
switch (switch_report->dpad)
|
||||
{
|
||||
case SWITCH_HAT_UP:
|
||||
gamepad.state.up = true;
|
||||
break;
|
||||
case SWITCH_HAT_UPRIGHT:
|
||||
gamepad.state.up = true;
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case SWITCH_HAT_RIGHT:
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case SWITCH_HAT_DOWNRIGHT:
|
||||
gamepad.state.right = true;
|
||||
gamepad.state.down = true;
|
||||
break;
|
||||
case SWITCH_HAT_DOWN:
|
||||
gamepad.state.down = true;
|
||||
break;
|
||||
case SWITCH_HAT_DOWNLEFT:
|
||||
gamepad.state.down = true;
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case SWITCH_HAT_LEFT:
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case SWITCH_HAT_UPLEFT:
|
||||
gamepad.state.up = true;
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (switch_report->b) gamepad.state.a = true;
|
||||
if (switch_report->a) gamepad.state.b = true;
|
||||
if (switch_report->y) gamepad.state.x = true;
|
||||
if (switch_report->x) gamepad.state.y = true;
|
||||
|
||||
if (switch_report->minus) gamepad.state.back = true;
|
||||
if (switch_report->plus) gamepad.state.start = true;
|
||||
if (switch_report->l3) gamepad.state.l3 = true;
|
||||
if (switch_report->r3) gamepad.state.r3 = true;
|
||||
|
||||
if(switch_report->home) gamepad.state.sys = true;
|
||||
if(switch_report->capture) gamepad.state.misc = true;
|
||||
|
||||
if(switch_report->l) gamepad.state.lb = true;
|
||||
if(switch_report->r) gamepad.state.rb = true;
|
||||
|
||||
if(switch_report->lz) gamepad.state.lt = 0xFF;
|
||||
if(switch_report->rz) gamepad.state.rt = 0xFF;
|
||||
|
||||
gamepad.state.lx = scale_uint8_to_int16(switch_report->lx, false);
|
||||
gamepad.state.ly = scale_uint8_to_int16(switch_report->ly, true);
|
||||
gamepad.state.rx = scale_uint8_to_int16(switch_report->rx, false);
|
||||
gamepad.state.ry = scale_uint8_to_int16(switch_report->ry, true);
|
||||
}
|
||||
|
||||
bool SwitchWired::send_fb_data()
|
||||
{
|
||||
// mine doesn't have rumble motors, not sure what goes here
|
||||
return true;
|
||||
}
|
||||
68
src/usbh/tusb_hid/switch_wired.h
Normal file
68
src/usbh/tusb_hid/switch_wired.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _SWITCH_WIRED_H_
|
||||
#define _SWITCH_WIRED_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usbh/tusb_hid/shared.h"
|
||||
|
||||
const usb_vid_pid_t switch_wired_devices[] =
|
||||
{
|
||||
{0x20D6, 0xA719}, // PowerA wired
|
||||
{0x0F0D, 0x0092} // Hori Pokken wired, I don't have this one so not 100% on if it'll work
|
||||
};
|
||||
|
||||
struct SwitchWiredReport
|
||||
{
|
||||
uint8_t y : 1;
|
||||
uint8_t b : 1;
|
||||
uint8_t a : 1;
|
||||
uint8_t x : 1;
|
||||
|
||||
uint8_t l : 1;
|
||||
uint8_t r : 1;
|
||||
uint8_t lz : 1;
|
||||
uint8_t rz : 1;
|
||||
|
||||
uint8_t minus : 1;
|
||||
uint8_t plus : 1;
|
||||
uint8_t l3 : 1;
|
||||
uint8_t r3 : 1;
|
||||
|
||||
uint8_t home : 1;
|
||||
uint8_t capture : 1;
|
||||
|
||||
uint8_t : 2;
|
||||
|
||||
uint8_t dpad : 4;
|
||||
uint8_t : 4;
|
||||
|
||||
uint8_t lx;
|
||||
uint8_t ly;
|
||||
uint8_t rx;
|
||||
uint8_t ry;
|
||||
}
|
||||
__attribute__((packed));
|
||||
|
||||
struct SwitchWiredState
|
||||
{
|
||||
uint8_t dev_addr = {0};
|
||||
uint8_t instance = {0};
|
||||
};
|
||||
|
||||
class SwitchWired
|
||||
{
|
||||
public:
|
||||
void init(uint8_t dev_addr, uint8_t instance);
|
||||
void process_report(uint8_t const* report, uint16_t len);
|
||||
bool send_fb_data();
|
||||
|
||||
private:
|
||||
SwitchWiredState switch_wired;
|
||||
|
||||
void reset_state();
|
||||
void update_gamepad(const SwitchWiredReport* switch_pro_data);
|
||||
};
|
||||
|
||||
#endif // _SWITCH_WIRED_H_
|
||||
@@ -1,29 +1,47 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
#include "pio_usb.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#include "usbh/tusb_host.h"
|
||||
#include "usbh/tusb_host_manager.h"
|
||||
#include "usbd/board_config.h"
|
||||
#include "tusb_host.h"
|
||||
#include "tusb_host_manager.h"
|
||||
|
||||
// TODO figure out something to do with the leds
|
||||
// tuh_mounted is always true after unplugging a gamepad, idk what's up with that
|
||||
|
||||
#ifdef FEATHER_RP2040
|
||||
#define PIO_USB_DP_PIN 16 // DM = 17
|
||||
#define PIN_5V_EN 18
|
||||
// #define RED_LED_PIN 13
|
||||
// #define NEOPIXEL_PWR_PIN 20
|
||||
// #define NEOPIXEL_CTRL_PIN 21
|
||||
|
||||
void board_setup()
|
||||
{
|
||||
gpio_init(PIN_5V_EN);
|
||||
gpio_set_dir(PIN_5V_EN, GPIO_OUT);
|
||||
gpio_put(PIN_5V_EN, 1);
|
||||
}
|
||||
|
||||
#else
|
||||
#define PIO_USB_DP_PIN 0 // DM = 1
|
||||
// #define PICO_LED_PIN 25
|
||||
|
||||
void board_setup()
|
||||
{
|
||||
// gpio_init(PICO_LED_PIN);
|
||||
// gpio_set_dir(PICO_LED_PIN, GPIO_OUT);
|
||||
// gpio_put(PICO_LED_PIN, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define PIO_USB_CONFIG { PIO_USB_DP_PIN, PIO_USB_TX_DEFAULT, PIO_SM_USB_TX_DEFAULT, PIO_USB_DMA_TX_DEFAULT, PIO_USB_RX_DEFAULT, PIO_SM_USB_RX_DEFAULT, PIO_SM_USB_EOP_DEFAULT, NULL, PIO_USB_DEBUG_PIN_NONE, PIO_USB_DEBUG_PIN_NONE, false, PIO_USB_PINOUT_DPDM }
|
||||
|
||||
void board_setup()
|
||||
{
|
||||
#if USBD_BOARD == OGXM_ADA_FEATHER_USBH
|
||||
gpio_init(VCC_EN_PIN);
|
||||
gpio_set_dir(VCC_EN_PIN, GPIO_OUT);
|
||||
gpio_put(VCC_EN_PIN, 1);
|
||||
#endif
|
||||
|
||||
gpio_init(LED_INDICATOR_PIN);
|
||||
gpio_set_dir(LED_INDICATOR_PIN, GPIO_OUT);
|
||||
gpio_put(LED_INDICATOR_PIN, 0);
|
||||
}
|
||||
|
||||
void usbh_main()
|
||||
{
|
||||
pio_usb_configuration_t pio_cfg = PIO_USB_CONFIG;
|
||||
@@ -33,9 +51,19 @@ void usbh_main()
|
||||
|
||||
tuh_init(1);
|
||||
|
||||
uint32_t fb_sent_time = to_ms_since_boot(get_absolute_time());
|
||||
const uint32_t fb_interval = 100;
|
||||
|
||||
while (true)
|
||||
{
|
||||
tuh_task();
|
||||
send_fb_data_to_gamepad();
|
||||
|
||||
uint32_t current_time = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
if (current_time - fb_sent_time >= fb_interval)
|
||||
{
|
||||
send_fb_data_to_gamepad();
|
||||
fb_sent_time = current_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +1,39 @@
|
||||
#include "hardware/gpio.h"
|
||||
#include "tusb.h"
|
||||
#include "host/usbh.h"
|
||||
#include "class/hid/hid_host.h"
|
||||
|
||||
#include "usbh/xinput/XInput.h"
|
||||
#include "usbh/n64usb/N64USB.h"
|
||||
#include "usbh/psclassic/PSClassic.h"
|
||||
#include "usbh/ps3/DInput.h"
|
||||
#include "usbh/ps3/Dualshock3.h"
|
||||
#include "usbh/ps4/Dualshock4.h"
|
||||
#include "usbh/ps5/Dualsense.h"
|
||||
#include "usbh/switch/SwitchPro.h"
|
||||
#include "usbh/switch/SwitchWired.h"
|
||||
#include "tusb_xinput/xinput_host_app.h"
|
||||
#include "tusb_xinput/xinput_host.h"
|
||||
|
||||
#include "usbh/shared/hid_class_driver.h"
|
||||
#include "usbh/tusb_host_manager.h"
|
||||
#include "tusb_hid/hid_host_app.h"
|
||||
#include "tusb_hid/shared.h"
|
||||
|
||||
#include "tusb_host_manager.h"
|
||||
|
||||
#include "usbd/board_config.h"
|
||||
#include "Gamepad.h"
|
||||
|
||||
struct HostedDevice
|
||||
{
|
||||
bool hid_class = {false};
|
||||
bool class_mounted = {false};
|
||||
uint8_t class_address;
|
||||
uint8_t class_instance;
|
||||
const usbh_class_driver_t* class_driver = {&usbh_xinput_driver};
|
||||
GPHostDriver* gamepad_driver = {nullptr};
|
||||
};
|
||||
HostMode host_mode;
|
||||
|
||||
HostedDevice hosted_device[MAX_GAMEPADS] = {};
|
||||
bool device_mounted = false;
|
||||
uint8_t device_daddr = 0;
|
||||
tusb_desc_interface_t config_descriptor;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const usb_vid_pid_t* devices;
|
||||
size_t num_devices;
|
||||
HostMode check_mode;
|
||||
} DeviceType;
|
||||
} DeviceTypeInfo;
|
||||
|
||||
const DeviceType device_types[] =
|
||||
const DeviceTypeInfo device_types[] =
|
||||
{
|
||||
{ n64_devices, sizeof(n64_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_N64USB },
|
||||
{ psc_devices, sizeof(psc_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_PSCLASSIC },
|
||||
{ dinput_devices, sizeof(dinput_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_DINPUT},
|
||||
{ ps3_devices, sizeof(ps3_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_PS3 },
|
||||
// { ps3_devices, sizeof(ps3_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_PS3 },
|
||||
{ ps4_devices, sizeof(ps4_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_PS4 },
|
||||
{ ps5_devices, sizeof(ps5_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_PS5 },
|
||||
{ switch_wired_devices, sizeof(switch_wired_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_SWITCH_WIRED },
|
||||
{ switch_pro_devices, sizeof(switch_pro_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_SWITCH_PRO }
|
||||
};
|
||||
|
||||
void led_mounted_indicator(bool mounted)
|
||||
{
|
||||
gpio_put(LED_INDICATOR_PIN, mounted ? 1 : 0);
|
||||
}
|
||||
|
||||
bool check_vid_pid(const usb_vid_pid_t* devices, size_t num_devices, HostMode check_mode, uint16_t vid, uint16_t pid)
|
||||
{
|
||||
for (size_t i = 0; i < num_devices; i++)
|
||||
@@ -68,244 +47,82 @@ bool check_vid_pid(const usb_vid_pid_t* devices, size_t num_devices, HostMode ch
|
||||
return false;
|
||||
}
|
||||
|
||||
void create_gamepad_driver(HostMode host_mode, int idx)
|
||||
void config_descriptor_complete_cb(tuh_xfer_t* xfer)
|
||||
{
|
||||
switch(host_mode)
|
||||
if (xfer->result == XFER_RESULT_SUCCESS)
|
||||
{
|
||||
case HOST_MODE_XINPUT:
|
||||
hosted_device[idx].gamepad_driver = new XInputHost();
|
||||
break;
|
||||
case HOST_MODE_HID_PSCLASSIC:
|
||||
hosted_device[idx].gamepad_driver = new PSClassic();
|
||||
break;
|
||||
case HOST_MODE_HID_DINPUT:
|
||||
hosted_device[idx].gamepad_driver = new DInput();
|
||||
break;
|
||||
case HOST_MODE_HID_PS3:
|
||||
hosted_device[idx].gamepad_driver = new Dualshock3();
|
||||
break;
|
||||
case HOST_MODE_HID_PS4:
|
||||
hosted_device[idx].gamepad_driver = new Dualshock4();
|
||||
break;
|
||||
case HOST_MODE_HID_PS5:
|
||||
hosted_device[idx].gamepad_driver = new Dualsense();
|
||||
break;
|
||||
case HOST_MODE_HID_SWITCH_PRO:
|
||||
hosted_device[idx].gamepad_driver = new SwitchPro();
|
||||
break;
|
||||
case HOST_MODE_HID_SWITCH_WIRED:
|
||||
hosted_device[idx].gamepad_driver = new SwitchWired();
|
||||
break;
|
||||
case HOST_MODE_HID_N64USB:
|
||||
hosted_device[idx].gamepad_driver = new N64USB();
|
||||
break;
|
||||
}
|
||||
|
||||
if (hosted_device[idx].gamepad_driver)
|
||||
{
|
||||
hosted_device[idx].gamepad_driver->init((uint8_t)idx + 1, hosted_device[idx].class_address, hosted_device[idx].class_instance);
|
||||
}
|
||||
}
|
||||
|
||||
int find_free_slot()
|
||||
{
|
||||
for (int i = 0; i < MAX_GAMEPADS; i++)
|
||||
{
|
||||
if (!hosted_device[i].class_mounted)
|
||||
if ((config_descriptor.bInterfaceSubClass == 0x5D && config_descriptor.bInterfaceProtocol == 0x81) ||
|
||||
(config_descriptor.bInterfaceSubClass == 0x5D && config_descriptor.bInterfaceProtocol == 0x01) ||
|
||||
(config_descriptor.bInterfaceSubClass == 0x47 && config_descriptor.bInterfaceProtocol == 0xD0) ||
|
||||
(config_descriptor.bInterfaceSubClass == 0x42 && config_descriptor.bInterfaceClass == 0x58))
|
||||
{
|
||||
hosted_device[i].class_mounted = true;
|
||||
return i;
|
||||
host_mode = HOST_MODE_XINPUT;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void unmount_gamepad(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
for (int i = 0; i < MAX_GAMEPADS; i++)
|
||||
{
|
||||
if (hosted_device[i].class_mounted && hosted_device[i].class_address == dev_addr && hosted_device[i].class_instance == instance)
|
||||
{
|
||||
hosted_device[i].class_mounted = false;
|
||||
gamepad(i).reset_pad();
|
||||
|
||||
if (hosted_device[i].gamepad_driver)
|
||||
{
|
||||
delete hosted_device[i].gamepad_driver;
|
||||
hosted_device[i].gamepad_driver = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool all_free = true;
|
||||
|
||||
for (int i = 0; i < MAX_GAMEPADS; i++)
|
||||
{
|
||||
if (hosted_device[i].gamepad_driver != nullptr)
|
||||
{
|
||||
all_free = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_free)
|
||||
{
|
||||
led_mounted_indicator(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------- TUSB ----------- */
|
||||
|
||||
void tuh_mount_cb(uint8_t daddr)
|
||||
{
|
||||
led_mounted_indicator(true);
|
||||
if (!device_mounted)
|
||||
{
|
||||
device_mounted = true;
|
||||
device_daddr = daddr;
|
||||
}
|
||||
|
||||
uint16_t vid, pid;
|
||||
tuh_vid_pid_get(daddr, &vid, &pid);
|
||||
|
||||
// set host mode depending on VID/PID match
|
||||
const size_t num_device_types = sizeof(device_types) / sizeof(DeviceTypeInfo);
|
||||
|
||||
for (size_t i = 0; i < num_device_types; i++)
|
||||
{
|
||||
if (check_vid_pid(device_types[i].devices, device_types[i].num_devices, device_types[i].check_mode, vid, pid))
|
||||
{
|
||||
host_mode = device_types[i].check_mode;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if (!tuh_descriptor_get_configuration(daddr, 0, &config_descriptor, sizeof(config_descriptor), config_descriptor_complete_cb, 0))
|
||||
// {
|
||||
host_mode = HOST_MODE_HID_MOUSE; // idk
|
||||
// }
|
||||
}
|
||||
|
||||
void tuh_umount_cb(uint8_t daddr)
|
||||
void tuh_umount_cb(uint8_t daddr)
|
||||
{
|
||||
|
||||
if (device_mounted && device_daddr == daddr)
|
||||
{
|
||||
device_mounted = false;
|
||||
}
|
||||
}
|
||||
|
||||
usbh_class_driver_t const* usbh_app_driver_get_cb(uint8_t* driver_count)
|
||||
{
|
||||
*driver_count = 1;
|
||||
|
||||
return hosted_device[0].class_driver;
|
||||
}
|
||||
|
||||
/* ----------- HID ----------- */
|
||||
|
||||
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
|
||||
{
|
||||
(void)desc_report;
|
||||
(void)desc_len;
|
||||
|
||||
int slot = find_free_slot();
|
||||
|
||||
if (slot < 0) return; // no available slots
|
||||
|
||||
hosted_device[slot].class_address = dev_addr;
|
||||
hosted_device[slot].class_instance = instance;
|
||||
|
||||
HostMode host_mode;
|
||||
uint16_t vid, pid;
|
||||
|
||||
tuh_vid_pid_get(dev_addr, &vid, &pid);
|
||||
|
||||
const size_t num_device_types = sizeof(device_types) / sizeof(DeviceType); // set host mode depending on VID/PID match
|
||||
|
||||
for (size_t i = 0; i < num_device_types; i++)
|
||||
switch (host_mode)
|
||||
{
|
||||
if (check_vid_pid(device_types[i].devices, device_types[i].num_devices, device_types[i].check_mode, vid, pid))
|
||||
{
|
||||
hosted_device[slot].hid_class = true;
|
||||
host_mode = device_types[i].check_mode;
|
||||
create_gamepad_driver(host_mode, slot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// tuh_hid_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
unmount_gamepad(dev_addr, instance);
|
||||
}
|
||||
|
||||
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
|
||||
{
|
||||
for (int i = 0; i < MAX_GAMEPADS; i++)
|
||||
{
|
||||
if (hosted_device[i].class_mounted &&
|
||||
hosted_device[i].class_address == dev_addr &&
|
||||
hosted_device[i].class_instance == instance)
|
||||
{
|
||||
if (hosted_device[i].gamepad_driver)
|
||||
{
|
||||
hosted_device[i].gamepad_driver->process_hid_report(gamepad(i), dev_addr, instance, report, len);
|
||||
}
|
||||
}
|
||||
case HOST_MODE_XINPUT:
|
||||
return &usbh_xinput_driver;
|
||||
default:
|
||||
return &usbh_hid_driver;
|
||||
}
|
||||
}
|
||||
|
||||
// Current version of TinyUSB may be bugged so omitting this for now
|
||||
|
||||
// void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len)
|
||||
// {
|
||||
// for (int i = 0; i < MAX_GAMEPADS; i++)
|
||||
// {
|
||||
// if (hosted_device[i].class_mounted &&
|
||||
// hosted_device[i].class_address == dev_addr &&
|
||||
// hosted_device[i].class_instance == instance)
|
||||
// {
|
||||
// hosted_device[i].gamepad_driver->hid_get_report_complete_cb(dev_addr, instance, report_id, report_type, len);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/* ----------- XINPUT ----------- */
|
||||
|
||||
void tuh_xinput_mount_cb(uint8_t dev_addr, uint8_t instance, const xinputh_interface_t *xinput_itf)
|
||||
{
|
||||
int slot = find_free_slot();
|
||||
|
||||
if (slot < 0) return; // no available slots
|
||||
|
||||
hosted_device[slot].class_address = dev_addr;
|
||||
hosted_device[slot].class_instance = instance;
|
||||
|
||||
create_gamepad_driver(HOST_MODE_XINPUT, slot);
|
||||
|
||||
tuh_xinput_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
void tuh_xinput_umount_cb(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
unmount_gamepad(dev_addr, instance);
|
||||
}
|
||||
|
||||
void tuh_xinput_report_received_cb(uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* report, uint16_t len)
|
||||
{
|
||||
for (int i = 0; i < MAX_GAMEPADS; i++)
|
||||
{
|
||||
if (hosted_device[i].class_mounted && hosted_device[i].class_address == dev_addr && hosted_device[i].class_instance == instance)
|
||||
{
|
||||
if (hosted_device[i].gamepad_driver)
|
||||
{
|
||||
hosted_device[i].gamepad_driver->process_xinput_report(gamepad(i), dev_addr, instance, report, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tuh_xinput_receive_report(dev_addr, instance);
|
||||
}
|
||||
|
||||
/* ----------- SEND FEEDBACK ----------- */
|
||||
|
||||
void send_fb_data_to_gamepad()
|
||||
{
|
||||
static const uint8_t fb_interval_ms = 100;
|
||||
static unsigned long fb_sent_time[MAX_GAMEPADS] = {to_ms_since_boot(get_absolute_time())};
|
||||
|
||||
for (int i = 0; i < MAX_GAMEPADS; i++)
|
||||
if (device_mounted)
|
||||
{
|
||||
if (!hosted_device[i].class_mounted || !hosted_device[i].gamepad_driver) break;
|
||||
|
||||
unsigned long current_time = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
if (current_time - fb_sent_time[i] >= fb_interval_ms)
|
||||
if (host_mode == HOST_MODE_XINPUT)
|
||||
{
|
||||
if (hosted_device[i].gamepad_driver->send_fb_data(gamepad(i), hosted_device[i].class_address, hosted_device[i].class_instance))
|
||||
{
|
||||
fb_sent_time[i] = current_time;
|
||||
|
||||
if (hosted_device[i].hid_class)
|
||||
{
|
||||
gamepad(i).reset_hid_rumble(); // reset rumble so it doesn't get stuck on
|
||||
}
|
||||
}
|
||||
send_fb_data_to_xinput_gamepad();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (send_fb_data_to_hid_gamepad())
|
||||
gamepadOut.rumble_hid_reset(); // needed so rumble doesn't get stuck on
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user