25 Commits

Author SHA1 Message Date
wiredopposite
b9038483ce update to tusb_gamepad lib 2024-05-01 22:13:43 -06:00
wiredopposite
e57de52cc4 new libs 2024-04-24 17:01:51 -06:00
wiredopposite
ee05f9516a use tusb_xinput fork 2024-04-24 16:58:05 -06:00
wiredopposite
a87ed08a63 Update issue templates 2024-04-19 22:19:40 -06:00
wiredopposite
e3acca6d5c add PowerA Enhanced controller 2024-04-19 17:07:27 -06:00
wiredopposite
2c601723f1 update readme 2024-04-17 21:54:00 -06:00
wiredopposite
d6dcbc52a7 update readme 2024-04-17 21:42:59 -06:00
wiredopposite
e210b50c1a update readme 2024-04-17 21:42:05 -06:00
wiredopposite
ae59b5e0c9 update readme 2024-04-17 21:38:43 -06:00
wiredopposite
9ebbbc3735 update readme 2024-04-17 21:27:06 -06:00
wiredopposite
f3ddd49252 xinput host 2024-04-17 20:12:05 -06:00
wiredopposite
f7da9a07d0 xinput 2024-04-17 20:08:04 -06:00
wiredopposite
e2ececd2e0 restrict input mode for 2+ players 2024-04-17 20:01:14 -06:00
wiredopposite
3604c8e112 fixed wireless adapter rumble 2024-04-17 19:40:56 -06:00
wiredopposite
86c60d76a4 switch to tusb_xinput fork 2024-04-17 19:16:17 -06:00
wiredopposite
2613221d44 new boards 2024-04-17 18:26:02 -06:00
wiredopposite
1f27c3d4b5 resolve dinput bug 2024-04-16 18:28:16 -06:00
wiredopposite
da72c91c04 resolve dinput bug 2024-04-16 18:27:10 -06:00
wiredopposite
933206398a resolve dinput bug 2024-04-16 18:25:24 -06:00
wiredopposite
0a053f8f3d modularizing device drivers 2024-04-16 18:13:13 -06:00
wiredopposite
b1535c9c75 refactoring for different hardware 2024-04-16 15:19:57 -06:00
wiredopposite
383274c83b fixed dinput bug 2024-04-16 15:09:23 -06:00
wiredopposite
54bbdf0a41 4 player support 2024-03-24 20:53:41 -06:00
wiredopposite
b0fd734194 add config header file 2024-03-24 10:10:26 -06:00
wiredopposite
49fff842f2 host side refactor 2024-03-24 10:06:05 -06:00
112 changed files with 4299 additions and 6809 deletions

28
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,28 @@
---
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...

View File

@@ -0,0 +1,17 @@
---
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.

3
.gitignore vendored
View File

@@ -3,5 +3,4 @@ build
release
generated
tools
.ignore
src/usbh/tusb_hid/experiment
.ignore

6
.gitmodules vendored
View File

@@ -4,3 +4,9 @@
[submodule "lib/tinyusb"]
path = lib/tinyusb
url = https://github.com/hathach/tinyusb.git
[submodule "lib/tusb_gamepad"]
path = lib/tusb_gamepad
url = https://github.com/wiredopposite/tusb_gamepad.git
[submodule "lib/tusb_xinput"]
path = lib/tusb_xinput
url = https://github.com/wiredopposite/tusb_xinput.git

View File

@@ -1,17 +1,18 @@
cmake_minimum_required(VERSION 3.12)
set(CMAKE_BUILD_TYPE Release)
message("Build type: \"${CMAKE_BUILD_TYPE}\"")
# Project name
set(NAME OGX-Mini)
# Board type
set(PICO_BOARD none)
# Fixes that allow some MCH2022 badges with a slowly starting oscillator to boot properly
add_compile_definitions(PICO_BOOT_STAGE2_CHOOSE_GENERIC_03H=1 PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64)
# add_compile_definitions(PICO_BOOT_STAGE2_CHOOSE_GENERIC_03H=1 PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64)
# add_compile_options(-Wno-unused-parameter -Wno-unused-variable -Wno-missing-field-initializers -Wno-unused-function)
add_compile_options(-Wno-missing-field-initializers)
# SDK
include($ENV{PICO_SDK_PATH}/pico_sdk_init.cmake)
project(${NAME} C CXX ASM)
@@ -22,45 +23,48 @@ if (PICO_SDK_VERSION_STRING VERSION_LESS "1.3.0")
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.3.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
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(ROOT ${CMAKE_CURRENT_LIST_DIR})
set(PICO_PIO_USB_PATH ${ROOT}/lib/Pico-PIO-USB)
set(PICO_TINYUSB_PATH ${ROOT}/lib/tinyusb)
set(TUSB_GAMEPAD_PATH ${ROOT}/lib/tusb_gamepad)
set(XINPUT_HOST_PATH ${ROOT}/lib/tusb_xinput)
pico_sdk_init()
add_subdirectory(lib/Pico-PIO-USB)
add_subdirectory(lib)
# add_subdirectory(${ROOT}/lib)
add_subdirectory(${ROOT}/lib/CRC32 CRC32)
add_subdirectory(${XINPUT_HOST_PATH} xinput_host)
add_subdirectory(${PICO_PIO_USB_PATH})
add_subdirectory(${TUSB_GAMEPAD_PATH})
target_include_directories(tusb_gamepad PRIVATE ${ROOT}/src) # so tusb_gamepad can see tusb_config.h
set(SRC_DIR ${ROOT}/src)
file(GLOB_RECURSE SOURCES
"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/xinput/*")
${SRC_DIR}/main.cpp
${SRC_DIR}/input_mode.cpp
${SRC_DIR}/usbh/tusb_host_manager.cpp
${SRC_DIR}/usbh/tusb_host.cpp
${SRC_DIR}/usbh/n64usb/N64USB.cpp
${SRC_DIR}/usbh/ps3/Dualshock3.cpp
${SRC_DIR}/usbh/ps3/DInput.cpp
${SRC_DIR}/usbh/ps4/Dualshock4.cpp
${SRC_DIR}/usbh/ps5/Dualsense.cpp
${SRC_DIR}/usbh/psclassic/PSClassic.cpp
${SRC_DIR}/usbh/switch/SwitchPro.cpp
${SRC_DIR}/usbh/switch/SwitchWired.cpp
${SRC_DIR}/usbh/xinput/XInput.cpp
${SRC_DIR}/usbh/shared/hid_class_driver.c
${SRC_DIR}/usbh/shared/scaling.cpp
)
# Firmware
add_executable(${NAME}
${SOURCES}
)
add_executable(${NAME} ${SOURCES})
target_include_directories(${NAME} PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src ${CMAKE_CURRENT_LIST_DIR}/lib)
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 ---------------------------------------#
# add_compile_definitions(HOST_DEBUG) # CDC device, include utilities/log.h and use log() as you would printf()
${ROOT}/src
${ROOT}/lib)
target_link_libraries(${NAME}
pico_stdlib
@@ -80,6 +84,8 @@ target_link_libraries(${NAME}
tinyusb_pico_pio_usb
CRC32
cmsis_core
xinput_host
tusb_gamepad
)
pico_add_extra_outputs(${NAME})
pico_add_extra_outputs(${NAME})

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024 wiredopposite
Copyright (c) 2024 wiredOpposite (wiredopposite.com)
Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
Copyright (c) 2020 Ryan Wendland

View File

@@ -1,40 +0,0 @@
# Copyright (c) 2022 Nicolai Electronics
# SPDX-License-Identifier: MIT
INSTALL_PREFIX := $PWD
BUILD_DIR := build
GENERATED_DIR := generated
.PHONY: all firmware flash clean install_rules $(BUILD_DIR) format
all: build flash
@echo "All tasks completed"
build:
mkdir -p $(BUILD_DIR)
mkdir -p $(GENERATED_DIR)
cd $(BUILD_DIR); cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX -DCMAKE_BUILD_TYPE=Release ..
$(MAKE) -C $(BUILD_DIR) --no-print-directory all
debug:
mkdir -p $(BUILD_DIR)
mkdir -p $(GENERATED_DIR)
cd $(BUILD_DIR); cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX -DCMAKE_BUILD_TYPE=Debug ..
$(MAKE) -C $(BUILD_DIR) --no-print-directory all
flash:
picotool load $(BUILD_DIR)/i2c_adapter.bin
picotool reboot
clean:
rm -rf $(BUILD_DIR)
rm -rf $(GENERATED_DIR)
install_rules:
cp tools/99-pico.rules /etc/udev/rules.d/
@echo "reload rules with:"
@echo "\tudevadm control --reload-rules"
@echo "\tudevadm trigger"
format:
find . -iname '*.h' -o -iname '*.c' -o -iname '*.cpp' | grep -v '$(BUILD_DIR)' | xargs clang-format -i

View File

@@ -1,7 +1,9 @@
# OGX-Mini
![Adafruit Feather RP2040 USB Host](images/ada_feather_rp2040_usb.jpg "Adafruit Feather RP2040 USB Host")
![OGX-Mini Boards](images/OGX-Mini-github.jpg "OGX-Mini Boards")
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
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
- Original Xbox
- Playstation 3
- Nintendo Switch (docked)
@@ -12,12 +14,14 @@ Firmware for the RP2040, setup for the [Adafruit Feather USB Host board](https:/
### 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)
@@ -44,16 +48,28 @@ 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.
![OGX-Mini Boards](images/OGX-Mini-rpzero-int.jpg "OGX-Mini Boards")
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:
![Pi Pico Wiring Diagram](images/pi_pico_diagram.png "Pi Pico Wiring Diagram]")
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 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.
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).
Here's a diagram of how you'd use the Pico:
![Pi Pico Wiring Diagram](images/pi_pico_diagram.png "Pi Pico Wiring Diagram]")
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).
## Special thanks
Thank you to Ryzee119 and the OpenStickCommunity, without their work this project would not exist.

View File

@@ -0,0 +1,6 @@
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,,,,,,,
1 References Qty Description Manufacturer MPN Digikey Mouser RS Newark Farnell LCSC JLC Assembly
2 USB1 1 USB TYPE-A SINGLE PORT RIGHT ANG CU01SAH0S00 2987-CU01SAH0S00-ND
3 R1,R2 2 RES 27 OHM 5% 1/8W 0805 RC0805JR-0727RL 311-27ARCT-ND
4 R3 1 RES 470 OHM 1% 1/8W 0805 (Optional) RC0805FR-07470RL 311-470CRCT-ND
5 LED1 1 LED GREEN CLEAR 0805 SMD (Optional) 150080GS75000 732-4983-1-ND
6 U1 1 Waveshare RP2040-Zero Waveshare RP2040-Zero

Binary file not shown.

5
hardware/README.md Normal file
View File

@@ -0,0 +1,5 @@
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.
![OGX-Mini Boards](../images/OGX-Mini-rpzero-int.jpg "OGX-Mini Boards")

File diff suppressed because it is too large Load Diff

BIN
images/OGX-Mini-github.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

1
lib/tusb_gamepad Submodule

Submodule lib/tusb_gamepad added at 5e5ee00ee8

1
lib/tusb_xinput Submodule

Submodule lib/tusb_xinput added at b34398847a

View File

@@ -1,62 +0,0 @@
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
# This can be dropped into an external project to help locate this SDK
# It should be include()ed prior to project()
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
)
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
FetchContent_Populate(pico_sdk)
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
else ()
message(FATAL_ERROR
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
)
endif ()
endif ()
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PICO_SDK_PATH})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
endif ()
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
endif ()
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
include(${PICO_SDK_INIT_CMAKE_FILE})

View File

@@ -1,33 +0,0 @@
#include <cstdint>
#include "Gamepad.h"
#include "usbh/tusb_hid/shared.h"
void Gamepad::reset_state()
{
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 GamepadOut::update_gamepad_rumble(uint8_t left_rumble, uint8_t right_rumble)
{
out_state.lrumble = left_rumble;
out_state.rrumble = right_rumble;
}
void GamepadOut::rumble_hid_reset()
{
if (out_state.lrumble != UINT8_MAX)
{
out_state.lrumble = 0;
}
if (out_state.rrumble != UINT8_MAX)
{
gamepadOut.out_state.rrumble = 0;
}
}

View File

@@ -1,70 +0,0 @@
#pragma once
#ifndef _GAMEPAD_H_
#define _GAMEPAD_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <cstdint>
#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};
bool right {false};
bool a {false};
bool b {false};
bool x {false};
bool y {false};
bool l3 {false};
bool r3 {false};
bool back {false};
bool start {false};
bool rb {false};
bool lb {false};
bool sys {false};
bool misc {false};
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 GamepadOutState{
uint8_t lrumble {0};
uint8_t rrumble {0};
};
class Gamepad {
public:
GamepadState state;
void update_gamepad_state_from_xinput(const xinput_gamepad_t* xinput_data);
void reset_state();
};
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_

View File

@@ -1,276 +0,0 @@
#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 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_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)
};

View File

@@ -1,264 +0,0 @@
/*
* 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)
};

View File

@@ -1,298 +0,0 @@
#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)
};

View File

@@ -1,147 +0,0 @@
/*
* 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
};

View File

@@ -1,244 +0,0 @@
/*
* 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
};

View File

@@ -1,85 +0,0 @@
#pragma once
#include <stdint.h>
#include "tusb.h"
#include "bsp/board_api.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#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,
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.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,
};
// array of pointer to string descriptors
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];

View File

@@ -1,140 +0,0 @@
/*
* 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)
};

View File

@@ -1,23 +0,0 @@
#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;

View File

@@ -1,6 +1,5 @@
#include <stdio.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "pico/multicore.h"
#include "hardware/flash.h"
@@ -16,7 +15,10 @@
#define FLASH_TARGET_OFFSET (256 * 1024)
#define FLASH_SIZE_BYTES (2 * 1024 * 1024)
void system_reset() {
InputMode current_input_mode;
void system_reset()
{
AIRCR_REG = AIRCR_VECTKEY | AIRCR_SYSRESETREQ;
while(1);
}
@@ -36,44 +38,42 @@ bool store_input_mode(enum InputMode new_mode)
restore_interrupts(saved_interrupts);
return true;
const uint8_t *flash_target_contents = (const uint8_t *)(XIP_BASE + FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE);
// 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)
// {
// return false;
// }
// return true;
}
bool change_input_mode(Gamepad previous_gamepad)
{
if (!previous_gamepad.state.start)
if ((uint8_t)saved_mode != *flash_target_contents)
{
return false;
}
InputMode new_mode;
return true;
}
if (previous_gamepad.state.up)
bool change_input_mode(GamepadButtons buttons)
{
if (!buttons.start)
{
return false;
}
InputMode new_mode = current_input_mode;
if (buttons.up)
{
new_mode = INPUT_MODE_XINPUT;
}
else if (previous_gamepad.state.left)
else if (buttons.left)
{
new_mode = INPUT_MODE_HID;
new_mode = INPUT_MODE_DINPUT;
}
else if (previous_gamepad.state.right)
else if (buttons.right)
{
new_mode = INPUT_MODE_XBOXORIGINAL;
}
else if (previous_gamepad.state.down)
else if (buttons.down)
{
new_mode = INPUT_MODE_SWITCH;
}
else if (previous_gamepad.state.a)
else if (buttons.a)
{
new_mode = INPUT_MODE_PSCLASSIC;
}
@@ -82,9 +82,6 @@ bool change_input_mode(Gamepad previous_gamepad)
if (new_mode)
{
// tinyusb needs to be kaput in order to write to the flash
// just hangs otherwise
tud_disconnect();
sleep_ms(300);
multicore_reset_core1(); // stop tusb host
@@ -101,18 +98,31 @@ bool change_input_mode(Gamepad previous_gamepad)
enum InputMode get_input_mode()
{
#ifdef HOST_DEBUG
#if (CDC_DEBUG > 0)
return INPUT_MODE_USBSERIAL;
#endif
const uint8_t *stored_value = (const uint8_t *)(XIP_BASE + FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE);
#if (MAX_GAMEPADS < 1)
if ((*stored_value == INPUT_MODE_DINPUT) || (*stored_value == INPUT_MODE_SWITCH))
{
current_input_mode = (enum InputMode)*stored_value;
}
else
{
current_input_mode = INPUT_MODE_DINPUT;
}
#else
if (*stored_value >= INPUT_MODE_XINPUT && *stored_value <= INPUT_MODE_XBOXORIGINAL)
{
return(enum InputMode)*stored_value;
current_input_mode = (enum InputMode)*stored_value;
}
else
{
return INPUT_MODE_XBOXORIGINAL;
current_input_mode = INPUT_MODE_XBOXORIGINAL;
}
#endif
return current_input_mode;
}

View File

@@ -5,21 +5,10 @@
extern "C" {
#endif
#include "Gamepad.h"
enum InputMode
{
INPUT_MODE_XINPUT = 0x01,
INPUT_MODE_SWITCH = 0x02,
INPUT_MODE_HID = 0x03,
INPUT_MODE_PSCLASSIC = 0x04,
INPUT_MODE_XBOXORIGINAL = 0x05,
INPUT_MODE_USBSERIAL = 0x06,
// INPUT_MODE_CONFIG,
};
#include "tusb_gamepad.h"
enum InputMode get_input_mode();
bool change_input_mode(Gamepad previous_gamepad_state);
bool change_input_mode(GamepadButtons buttons);
#ifdef __cplusplus
}

View File

@@ -1,24 +1,18 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pico/stdlib.h"
#include "pico/multicore.h"
#include "tusb.h"
#include "bsp/board_api.h"
#include "tusb_gamepad.h"
#include "drivermanager.h"
#include "drivers/gpdriver.h"
#include "usbh/tusb_host.h"
#include "usbd/drivermanager.h"
#include "usbd/gpdriver.h"
#include "Gamepad.h"
#include "input_mode.h"
Gamepad gamepad;
GamepadOut gamepadOut;
int main(void)
{
set_sys_clock_khz(120000, true);
@@ -34,28 +28,32 @@ int main(void)
multicore_reset_core1();
multicore_launch_core1(usbh_main);
Gamepad previous_gamepad = gamepad;
GamepadButtons prev_gamepad_buttons = gamepad(0)->buttons;
absolute_time_t last_time_gamepad_changed = get_absolute_time();
absolute_time_t last_time_gamepad_checked = get_absolute_time();
while (1)
{
uint8_t outBuffer[64];
GPDriver* driver = driverManager.getDriver();
driver->process(&gamepad, outBuffer);
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));
}
if (absolute_time_diff_us(last_time_gamepad_checked, get_absolute_time()) >= 500000)
{
// check if digital buttons have changed (first 16 bytes of gamepad.state)
if (memcmp(&gamepad.state, &previous_gamepad.state, 16) != 0)
// check if digital buttons have changed
if (memcmp(&gamepad(0)->buttons, &prev_gamepad_buttons, sizeof(GamepadButtons)) != 0)
{
memcpy(&previous_gamepad.state, &gamepad.state, sizeof(gamepad.state));
memcpy(&prev_gamepad_buttons, &gamepad(0)->buttons, sizeof(GamepadButtons));
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(previous_gamepad))
if (!change_input_mode(prev_gamepad_buttons))
{
last_time_gamepad_changed = get_absolute_time();
}

39
src/ogxm_config.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef _OGXM_CONFIG_H_
#define _OGXM_CONFIG_H_
#define ADAFRUIT_FEATHER_USBH 1
#define PI_PICO 2
#define RP2040_ZERO_INTERPOSER 3
// Options //
#define OGXM_BOARD ADAFRUIT_FEATHER_USBH
#define CDC_DEBUG 0
// ------- //
#if OGXM_BOARD == ADAFRUIT_FEATHER_USBH
#define PIO_USB_DP_PIN 16 // DM = 17
#define LED_INDICATOR_PIN 13
#define VCC_EN_PIN 18
// #define NEOPIXEL_PWR_PIN 20
// #define NEOPIXEL_CTRL_PIN 21
#elif OGXM_BOARD == PI_PICO
#define PIO_USB_DP_PIN 0 // DM = 1
#define LED_INDICATOR_PIN 25
#elif OGXM_BOARD == RP2040_ZERO_INTERPOSER
#define PIO_USB_DP_PIN 10 // DM = 11
#define LED_INDICATOR_PIN 13
#endif
#ifndef OGXM_BOARD
#error OGXM_BOARD must be defined in ogxm_config.h
#endif
#ifndef CDC_DEBUG
#define CDC_DEBUG 0
#endif
#endif // _OGXM_CONFIG_H_

View File

@@ -26,6 +26,10 @@
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
#include "board_config.h"
#define MAX_GAMEPADS 1
#ifdef __cplusplus
extern "C" {
#endif
@@ -87,7 +91,7 @@
#define CFG_TUD_ENABLED 1
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
// #define CFG_TUSB_DEBUG 1
#define CFG_TUSB_DEBUG 0
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
@@ -126,7 +130,7 @@
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
#define CFG_TUD_CDC_RX_BUFSIZE 256
@@ -135,7 +139,7 @@
//------------- CLASS -------------//
#define CFG_TUD_CDC 1
#define CFG_TUD_ECM_RNDIS 0
#define CFG_TUD_HID 2
#define CFG_TUD_HID (MAX_GAMEPADS + 1)
//--------------------------------------------------------------------
// HOST CONFIGURATION
@@ -150,12 +154,12 @@
// Size of buffer to hold descriptors and other data used for enumeration
#define CFG_TUH_ENUMERATION_BUFSIZE 512
#define CFG_TUH_HUB 1
#define CFG_TUH_HUB MAX_GAMEPADS
#define CFG_TUH_CDC 0
#define CFG_TUH_HID 1 // typical keyboard + mouse device can have 3-4 HID interfaces
#define CFG_TUH_HID MAX_GAMEPADS // typical keyboard + mouse device can have 3-4 HID interfaces
#define CFG_TUH_MSC 0
#define CFG_TUH_VENDOR 0
#define CFG_TUH_XINPUT 1
#define CFG_TUH_XINPUT MAX_GAMEPADS
// max device support (excluding hub device)
#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports

View File

@@ -1,197 +0,0 @@
/*
* 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;
}

View File

@@ -1,32 +0,0 @@
/*
* 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_

View File

@@ -1,50 +0,0 @@
#include "usbd/drivermanager.h"
// #include "net/NetDriver.h"
// #include "keyboard/KeyboardDriver.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_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);
}

View File

@@ -1,24 +0,0 @@
#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

View File

@@ -1,40 +0,0 @@
/*
* 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

View File

@@ -1,171 +0,0 @@
/*
* 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 "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;
}

View File

@@ -1,30 +0,0 @@
/*
* 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_

View File

@@ -1,108 +0,0 @@
#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;
}

View File

@@ -1,30 +0,0 @@
/*
* 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_

View File

@@ -1,26 +0,0 @@
#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_

View File

@@ -1,151 +0,0 @@
#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;
}

View File

@@ -1,30 +0,0 @@
/*
* 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_

View File

@@ -1,114 +0,0 @@
/*
* 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

View File

@@ -1,7 +0,0 @@
#ifndef _USB_DRIVER_H_
#define _USB_DRIVER_H_
bool get_usb_mounted(void);
bool get_usb_suspended(void);
#endif // #ifndef _USB_DRIVER_H_

View File

@@ -1,120 +0,0 @@
#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;
}

View File

@@ -1,22 +0,0 @@
#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

View File

@@ -1,120 +0,0 @@
#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;
}

View File

@@ -1,30 +0,0 @@
/*
* 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_

View File

@@ -1,21 +0,0 @@
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.

View File

@@ -1,257 +0,0 @@
#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;
}

View File

@@ -1,54 +0,0 @@
#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_

View File

@@ -1,6 +0,0 @@
#include "usbd/xboxog/xid/xid.h"
uint8_t *xremote_get_rom()
{
return NULL;
}

View File

@@ -1,87 +0,0 @@
#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_

View File

@@ -1,33 +0,0 @@
#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;
}

View File

@@ -1,94 +0,0 @@
#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

View File

@@ -1,47 +0,0 @@
#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;
}

View File

@@ -1,77 +0,0 @@
#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

View File

@@ -1,33 +0,0 @@
#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;
}

View File

@@ -1,155 +0,0 @@
#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

View File

@@ -1,217 +0,0 @@
/*
* 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;
}

View File

@@ -1,30 +0,0 @@
/*
* 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

23
src/usbh/GPHostDriver.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef _GPHOSTDRIVER_H_
#define _GPHOSTDRIVER_H_
#include <stdint.h>
#include "host/usbh.h" // needed so xinput_host will build
#include "xinput_host.h"
#include "tusb_gamepad.h"
#include "usbh/shared/shared.h"
class GPHostDriver
{
public:
virtual ~GPHostDriver() = default;
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_

113
src/usbh/n64usb/N64USB.cpp Normal file
View File

@@ -0,0 +1,113 @@
#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(gamepad);
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)
{
(void)len;
static N64USBReport prev_report = {};
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)gamepad;
(void)dev_addr;
(void)instance;
(void)report;
(void)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)
{
(void)dev_addr;
(void)instance;
(void)report_id;
(void)report_type;
(void)len;
}
bool N64USB::send_fb_data(const Gamepad* gamepad, uint8_t dev_addr, uint8_t instance)
{
(void)gamepad;
(void)dev_addr;
(void)instance;
return true;
}

View File

@@ -1,11 +1,9 @@
#pragma once
#ifndef _N64USB_H_
#define _N64USB_H_
#include <stdint.h>
#include "usbh/tusb_hid/shared.h"
#include "usbh/GPHostDriver.h"
const usb_vid_pid_t n64_devices[] =
{
@@ -46,21 +44,24 @@ typedef struct __attribute__((packed))
uint16_t buttons;
} N64USBReport;
struct N64USBState
struct N64USBState
{
uint8_t dev_addr = {0};
uint8_t instance = {0};
uint8_t player_id = {0};
};
class N64USB
class N64USB: public GPHostDriver
{
public:
void init(uint8_t dev_addr, uint8_t instance);
void process_report(uint8_t const* report, uint16_t len);
bool send_fb_data();
~N64USB() override {}
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:
N64USBState n64usb;
void update_gamepad(const N64USBReport* n64_data);
void update_gamepad(Gamepad* gp, const N64USBReport* n64_data);
};
#endif // _N64USB_H_

146
src/usbh/ps3/DInput.cpp Normal file
View File

@@ -0,0 +1,146 @@
#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(gamepad);
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->analog_buttons.up = dinput_report->up_axis;
gamepad->analog_buttons.down = dinput_report->down_axis;
gamepad->analog_buttons.left = dinput_report->left_axis;
gamepad->analog_buttons.right = dinput_report->right_axis;
gamepad->analog_buttons.x = dinput_report->square_axis;
gamepad->analog_buttons.y = dinput_report->triangle_axis;
gamepad->analog_buttons.a = dinput_report->cross_axis;
gamepad->analog_buttons.b = dinput_report->circle_axis;
gamepad->analog_buttons.lb = dinput_report->l1_axis;
gamepad->analog_buttons.rb = dinput_report->r1_axis;
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)
{
(void)len;
static DInputReport prev_report = {};
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)gamepad;
(void)dev_addr;
(void)instance;
(void)report;
(void)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)
{
(void)dev_addr;
(void)instance;
(void)report_id;
(void)report_type;
(void)len;
}
bool DInput::send_fb_data(const Gamepad* gamepad, uint8_t dev_addr, uint8_t instance)
{
(void)gamepad;
(void)dev_addr;
(void)instance;
return true;
}

38
src/usbh/ps3/DInput.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef _DINPUT_H_
#define _DINPUT_H_
#include <stdint.h>
#include "descriptors/DInputDescriptors.h"
#include "usbh/GPHostDriver.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:
~DInput() override {}
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_

340
src/usbh/ps3/Dualshock3.cpp Normal file
View File

@@ -0,0 +1,340 @@
#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)
{
(void)dev_addr;
(void)instance;
(void)report_id;
(void)report_type;
(void)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(gamepad);
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->analog_buttons.up = ds3_data->up_axis;
gamepad->analog_buttons.down = ds3_data->down_axis;
gamepad->analog_buttons.left = ds3_data->left_axis;
gamepad->analog_buttons.right = ds3_data->right_axis;
gamepad->analog_buttons.x = ds3_data->square_axis;
gamepad->analog_buttons.y = ds3_data->triangle_axis;
gamepad->analog_buttons.a = ds3_data->cross_axis;
gamepad->analog_buttons.b = ds3_data->circle_axis;
gamepad->analog_buttons.lb = ds3_data->l1_axis;
gamepad->analog_buttons.rb = ds3_data->r1_axis;
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)
{
(void)len;
static Dualshock3Report prev_report = {};
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)
{
(void)gamepad;
(void)dev_addr;
(void)instance;
(void)report;
(void)len;
}
bool Dualshock3::send_fb_data(const Gamepad* gamepad, uint8_t dev_addr, uint8_t instance)
{
(void)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;
}

39
src/usbh/ps3/Dualshock3.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef _DUALSHOCK3_H_
#define _DUALSHOCK3_H_
#include <stdint.h>
#include "descriptors/PS3Descriptors.h"
#include "usbh/GPHostDriver.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:
~Dualshock3() override {}
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

152
src/usbh/ps4/Dualshock4.cpp Normal file
View File

@@ -0,0 +1,152 @@
#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(gamepad);
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)
{
(void)len;
static Dualshock4Report prev_report = {};
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)gamepad;
(void)dev_addr;
(void)instance;
(void)report;
(void)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)
{
(void)dev_addr;
(void)instance;
(void)report_id;
(void)report_type;
(void)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));
}

View File

@@ -1,13 +1,9 @@
#pragma once
#ifndef _PS4_H_
#define _PS4_H_
#ifndef _DUALSHOCK4_H_
#define _DUALSHOCK4_H_
#include <stdint.h>
#include "usbh/tusb_hid/shared.h"
#define REPORT_ID_GAMEPAD_STATE 0x11
#include "usbh/GPHostDriver.h"
const usb_vid_pid_t ps4_devices[] =
{
@@ -28,7 +24,7 @@ static const uint8_t led_colors[][3] =
{ 0x20, 0x00, 0x20 }, // Pink
};
enum dualshock4_dpad_mask
enum Dualshock4DpadMask
{
PS4_DPAD_MASK_UP = 0x00,
PS4_DPAD_MASK_UP_RIGHT = 0x01,
@@ -120,21 +116,24 @@ typedef struct __attribute__((packed))
struct Dualshock4State
{
uint8_t dev_addr = {0};
uint8_t instance = {0};
uint8_t player_id = {0};
bool leds_set = {false};
};
class Dualshock4
class Dualshock4 : public GPHostDriver
{
public:
void init(uint8_t dev_addr, uint8_t instance);
void process_report(uint8_t const* report, uint16_t len);
bool send_fb_data();
~Dualshock4() override {}
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:
Dualshock4State dualshock4;
void update_gamepad(const Dualshock4Report* ds4_data);
bool set_leds();
void update_gamepad(Gamepad* gp, const Dualshock4Report* ds4_data);
bool set_leds(uint8_t dev_addr, uint8_t instance);
};
#endif // _PS4_H_
#endif

115
src/usbh/ps5/Dualsense.cpp Normal file
View File

@@ -0,0 +1,115 @@
#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(gamepad);
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)
{
(void)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)gp;
(void)dev_addr;
(void)instance;
(void)report;
(void)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)
{
(void)dev_addr;
(void)instance;
(void)report_id;
(void)report_type;
(void)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));
}

View File

@@ -1,11 +1,9 @@
#pragma once
#ifndef _PS5_H_
#define _PS5_H_
#ifndef _DUALSENSE_H_
#define _DUALSENSE_H_
#include <stdint.h>
#include "usbh/tusb_hid/shared.h"
#include "usbh/GPHostDriver.h"
const usb_vid_pid_t ps5_devices[] =
{
@@ -131,21 +129,22 @@ struct DualsenseOutReport {
uint8_t lightbar_blue;
} __attribute__((packed));
struct DualsenseState
struct DualsenseState
{
uint8_t dev_addr = {0};
uint8_t instance = {0};
uint8_t player_id = {0};
};
class Dualsense
class Dualsense : public GPHostDriver
{
public:
void init(uint8_t dev_addr, uint8_t instance);
void process_report(uint8_t const* report, uint16_t len);
bool send_fb_data();
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:
DualsenseState dualsense;
void update_gamepad(const DualsenseReport* ds_data);
void update_gamepad(Gamepad* gp, const DualsenseReport* ds_report);
};
#endif // _PS5_H_
#endif // _DUALSENSE_H_

View File

@@ -0,0 +1,107 @@
#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(gamepad);
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)
{
(void)len;
static PSClassicReport prev_report = {};
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)gamepad;
(void)dev_addr;
(void)instance;
(void)report;
(void)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)
{
(void)dev_addr;
(void)instance;
(void)report_id;
(void)report_type;
(void)len;
}
bool PSClassic::send_fb_data(const Gamepad* gamepad, uint8_t dev_addr, uint8_t instance)
{
(void)gamepad;
(void)dev_addr;
(void)instance;
return true;
}

View File

@@ -0,0 +1,34 @@
#ifndef _PSCLASSIC_H_
#define _PSCLASSIC_H_
#include <stdint.h>
#include "descriptors/PSClassicDescriptors.h"
#include "usbh/GPHostDriver.h"
const usb_vid_pid_t psc_devices[] =
{
{0x054C, 0x0CDA} // psclassic
};
struct PSClassicState
{
uint8_t player_id = {0};
};
class PSClassic : public GPHostDriver
{
public:
~PSClassic() override {}
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_

View File

@@ -0,0 +1,13 @@
#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
};

View File

@@ -0,0 +1,8 @@
#pragma once
#ifndef _HID_CLASS_DRIVER_H_
#define _HID_CLASS_DRIVER_H_
extern usbh_class_driver_t const usbh_hid_driver;
#endif

View File

@@ -1,26 +1,4 @@
#include "utilities/scaling.h"
uint8_t scale_int16_to_uint8(int16_t value, bool invert)
{
if (value == INT16_MIN && invert)
{
return UINT8_MAX;
}
if (invert)
{
value = -value;
}
uint32_t scaled_value = static_cast<uint32_t>(value - INT16_MIN) * UINT8_MAX / (INT16_MAX - INT16_MIN);
if (scaled_value > UINT8_MAX)
{
scaled_value = UINT8_MAX;
}
return static_cast<uint8_t>(scaled_value);
}
#include "usbh/shared/scaling.h"
int16_t scale_uint8_to_int16(uint8_t value, bool invert)
{

View File

@@ -1,11 +1,8 @@
#pragma once
#ifndef _SCALING_H_
#define _SCALING_H_
#include <stdint.h>
uint8_t scale_int16_to_uint8(int16_t value, bool invert);
int16_t scale_uint8_to_int16(uint8_t value, bool invert);
#endif // _SCALING_H_

View File

@@ -0,0 +1,260 @@
#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)
{
(void)dev_addr;
(void)instance;
switch_pro.player_id = player_id;
}
void SwitchPro::send_handshake(uint8_t dev_addr, uint8_t instance)
{
if (tuh_hid_send_ready(dev_addr, instance))
{
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(dev_addr, instance))
{
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)
{
(void)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 = {};
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(gamepad);
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)gamepad;
(void)dev_addr;
(void)instance;
(void)report;
(void)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)
{
(void)dev_addr;
(void)instance;
(void)report_id;
(void)report_type;
(void)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);
}

View File

@@ -0,0 +1,46 @@
#ifndef _SWITCHPRO_H_
#define _SWITCHPRO_H_
#include <stdint.h>
#include "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:
~SwitchPro() override {}
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_

View File

@@ -0,0 +1,121 @@
#include <stdint.h>
#include "pico/stdlib.h"
#include "pico/time.h"
#include "tusb.h"
#include "descriptors/SwitchDescriptors.h"
#include "usbh/switch/SwitchWired.h"
#include "usbh/shared/scaling.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)
{
(void)len;
static SwitchWiredReport prev_report = {};
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(gamepad);
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)gamepad;
(void)dev_addr;
(void)instance;
(void)report;
(void)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)
{
(void)dev_addr;
(void)instance;
(void)report_id;
(void)report_type;
(void)len;
}
bool SwitchWired::send_fb_data(const Gamepad* gamepad, uint8_t dev_addr, uint8_t instance)
{
(void)gamepad;
(void)dev_addr;
(void)instance;
return true; // not aware of a wired switch gamepad with rumble
}

View File

@@ -0,0 +1,67 @@
#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:
~SwitchWired() override {}
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_

View File

@@ -1,207 +0,0 @@
#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 host driver class for all this
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;
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);
}
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;
}
}
}
// 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;
}
if ( !tuh_hid_receive_report(dev_addr, instance) )
{
printf("Error: cannot request to receive report\r\n");
}
}
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;
}
}
return rumble_sent;
}

View File

@@ -1,24 +0,0 @@
#ifndef _HID_HOST_APP_H_
#define _HID_HOST_APP_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_

View File

@@ -1,90 +0,0 @@
#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;
}

View File

@@ -1,221 +0,0 @@
#include <stdint.h>
#include "pico/stdlib.h"
#include "tusb.h"
#include "class/hid/hid_host.h"
#include "utilities/scaling.h"
#include "Gamepad.h"
#include "usbh/tusb_hid/ps3.h"
#include "utilities/log.h"
/* -------------------------------------------- */
/* this only works for DInput currently, no DS3 */
/* -------------------------------------------- */
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;
if (enable_reports())
{
// log("reports enabled, addr: %02X inst: %02X", dev_addr, instance);
}
else
{
// log("report 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::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;
return tuh_hid_send_report(dualshock3.dev_addr, dualshock3.instance, 0x1, &output_report, sizeof(output_report));
}

View File

@@ -1,48 +0,0 @@
#pragma once
#ifndef _PS3_H_
#define _PS3_H_
#include <stdint.h>
#include "usbh/tusb_hid/shared.h"
#include "descriptors/PS3Descriptors.h"
#include "descriptors/DInputDescriptors.h"
#define PS3_REPORT_BUFFER_SIZE 48
const usb_vid_pid_t ps3_devices[] =
{
{0x054C, 0x0268}, // Sony Batoh (Dualshock 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_

View File

@@ -1,138 +0,0 @@
#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));
}

View File

@@ -1,105 +0,0 @@
#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));
}

View File

@@ -1,84 +0,0 @@
#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;
}

View File

@@ -1,34 +0,0 @@
#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_

View File

@@ -1,266 +0,0 @@
#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);
}

View File

@@ -1,95 +0,0 @@
#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_

View File

@@ -1,106 +0,0 @@
#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;
}

View File

@@ -1,68 +0,0 @@
#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_

Some files were not shown because too many files have changed in this diff Show More