Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9038483ce | ||
|
|
e57de52cc4 | ||
|
|
ee05f9516a | ||
|
|
a87ed08a63 | ||
|
|
e3acca6d5c | ||
|
|
2c601723f1 | ||
|
|
d6dcbc52a7 | ||
|
|
e210b50c1a | ||
|
|
ae59b5e0c9 | ||
|
|
9ebbbc3735 | ||
|
|
f3ddd49252 | ||
|
|
f7da9a07d0 | ||
|
|
e2ececd2e0 | ||
|
|
3604c8e112 | ||
|
|
86c60d76a4 | ||
|
|
2613221d44 | ||
|
|
1f27c3d4b5 | ||
|
|
da72c91c04 | ||
|
|
933206398a | ||
|
|
0a053f8f3d | ||
|
|
b1535c9c75 | ||
|
|
383274c83b | ||
|
|
54bbdf0a41 | ||
|
|
b0fd734194 | ||
|
|
49fff842f2 | ||
|
|
3995fd538c | ||
|
|
2643628dac | ||
|
|
8fb77437cf | ||
|
|
559633010e |
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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...
|
||||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal 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
3
.gitignore
vendored
@@ -3,5 +3,4 @@ build
|
|||||||
release
|
release
|
||||||
generated
|
generated
|
||||||
tools
|
tools
|
||||||
.ignore
|
.ignore
|
||||||
src/usbh/tusb_hid/experiment
|
|
||||||
12
.gitmodules
vendored
Normal file
12
.gitmodules
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[submodule "lib/Pico-PIO-USB"]
|
||||||
|
path = lib/Pico-PIO-USB
|
||||||
|
url = https://github.com/sekigon-gonnoc/Pico-PIO-USB.git
|
||||||
|
[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
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
cmake_minimum_required(VERSION 3.12)
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
|
||||||
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
message("Build type: \"${CMAKE_BUILD_TYPE}\"")
|
message("Build type: \"${CMAKE_BUILD_TYPE}\"")
|
||||||
|
|
||||||
# Project name
|
|
||||||
set(NAME OGX-Mini)
|
set(NAME OGX-Mini)
|
||||||
|
|
||||||
# Board type
|
|
||||||
set(PICO_BOARD none)
|
set(PICO_BOARD none)
|
||||||
|
|
||||||
# Fixes that allow some MCH2022 badges with a slowly starting oscillator to boot properly
|
# 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)
|
include($ENV{PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||||
|
|
||||||
project(${NAME} C CXX ASM)
|
project(${NAME} C CXX ASM)
|
||||||
@@ -22,56 +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}")
|
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.3.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(PICO_PIO_USB_PATH "${CMAKE_CURRENT_LIST_DIR}/lib/Pico-PIO-USB")
|
set(ROOT ${CMAKE_CURRENT_LIST_DIR})
|
||||||
set(PICO_TINYUSB_PATH "${CMAKE_CURRENT_LIST_DIR}/lib/tinyusb")
|
|
||||||
|
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()
|
pico_sdk_init()
|
||||||
|
|
||||||
add_subdirectory(lib/Pico-PIO-USB)
|
# add_subdirectory(${ROOT}/lib)
|
||||||
add_subdirectory(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
|
file(GLOB_RECURSE SOURCES
|
||||||
"src/usbh/*"
|
${SRC_DIR}/main.cpp
|
||||||
"src/usbh/tusb_xinput/*"
|
${SRC_DIR}/input_mode.cpp
|
||||||
"src/usbh/tusb_hid/*"
|
|
||||||
"src/*"
|
${SRC_DIR}/usbh/tusb_host_manager.cpp
|
||||||
"src/utilities/*"
|
${SRC_DIR}/usbh/tusb_host.cpp
|
||||||
"src/usbd/*"
|
${SRC_DIR}/usbh/n64usb/N64USB.cpp
|
||||||
"src/usbd/shared/*"
|
${SRC_DIR}/usbh/ps3/Dualshock3.cpp
|
||||||
"src/usbd/switch/*"
|
${SRC_DIR}/usbh/ps3/DInput.cpp
|
||||||
"src/usbd/xboxog/*"
|
${SRC_DIR}/usbh/ps4/Dualshock4.cpp
|
||||||
"src/usbd/xinput/*")
|
${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
|
# Firmware
|
||||||
add_executable(${NAME}
|
add_executable(${NAME} ${SOURCES})
|
||||||
${SOURCES}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(${NAME} PUBLIC
|
target_include_directories(${NAME} PUBLIC
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src ${CMAKE_CURRENT_LIST_DIR}/lib)
|
${ROOT}/src
|
||||||
|
${ROOT}/lib)
|
||||||
include_directories(
|
|
||||||
lib/)
|
|
||||||
# src/usbh
|
|
||||||
# src/usbd)
|
|
||||||
|
|
||||||
#------- 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 ---------------------------------------#
|
|
||||||
|
|
||||||
|
|
||||||
#------ These are your options for platforms -----#
|
|
||||||
#------ Uncomment only one at a time -------------#
|
|
||||||
|
|
||||||
add_compile_definitions(HOST_ORIGINAL_XBOX)
|
|
||||||
# add_compile_definitions(HOST_XINPUT)
|
|
||||||
# add_compile_definitions(HOST_NINTENDO_SWITCH)
|
|
||||||
# add_compile_definitions(HOST_PLAYSTATION_3)
|
|
||||||
# add_compile_definitions(HOST_PLAYSTATION_CLASSIC)
|
|
||||||
# add_compile_definitions(HOST_DEBUG) # CDC device, include log.h and use log() as you would printf()
|
|
||||||
|
|
||||||
|
|
||||||
target_link_libraries(${NAME}
|
target_link_libraries(${NAME}
|
||||||
pico_stdlib
|
pico_stdlib
|
||||||
@@ -91,6 +84,8 @@ target_link_libraries(${NAME}
|
|||||||
tinyusb_pico_pio_usb
|
tinyusb_pico_pio_usb
|
||||||
CRC32
|
CRC32
|
||||||
cmsis_core
|
cmsis_core
|
||||||
|
xinput_host
|
||||||
|
tusb_gamepad
|
||||||
)
|
)
|
||||||
|
|
||||||
pico_add_extra_outputs(${NAME})
|
pico_add_extra_outputs(${NAME})
|
||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2024 wiredopposite
|
Copyright (c) 2024 wiredOpposite (wiredopposite.com)
|
||||||
Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||||
Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
|
Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
|
||||||
Copyright (c) 2020 Ryan Wendland
|
Copyright (c) 2020 Ryan Wendland
|
||||||
|
|||||||
40
Makefile
40
Makefile
@@ -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
|
|
||||||
76
README.md
76
README.md
@@ -1,43 +1,75 @@
|
|||||||
# OGX-Mini
|
# OGX-Mini
|
||||||

|

|
||||||
|
|
||||||
Firmware for the RP2040, setup for the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723) (an be used with the Pi Pico), 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
|
- Original Xbox
|
||||||
- XInput (not Xbox 360)
|
|
||||||
- Nintendo Switch (docked)
|
|
||||||
- Playstation Classic
|
|
||||||
- Playstation 3
|
- Playstation 3
|
||||||
|
- Nintendo Switch (docked)
|
||||||
|
- XInput (not Xbox 360)
|
||||||
|
- Playstation Classic
|
||||||
|
|
||||||
Currently there's no way to switch what device is being emulated on the fly, so the firmware must be compiled specifically for whichever platform you'd like to play on. As long as that's the case, I'll provide compiled .uf2 files for each platform in [Releases](https://github.com/wiredopposite/OGX-Mini/releases).
|
## Supported devices
|
||||||
|
### Wired controllers
|
||||||
# Supported devices
|
|
||||||
## Wired controllers
|
|
||||||
- Original Xbox Duke and S
|
- Original Xbox Duke and S
|
||||||
- Xbox 360, One, Series, and Elite
|
- Xbox 360, One, Series, and Elite
|
||||||
|
- Dualshock 3 (PS3)
|
||||||
- Dualshock 4 (PS4)
|
- Dualshock 4 (PS4)
|
||||||
- Dualsense (PS5, Dualsense Edge should work but it's untested)
|
- Dualsense (PS5, Dualsense Edge should work but it's untested)
|
||||||
- Nintendo Switch Pro
|
- Nintendo Switch Pro
|
||||||
- Nintnedo Switch wired (tested with PowerA brand, no rumble)
|
- Nintendo Switch wired (tested with PowerA brand)
|
||||||
- Nintendo 64 USB (experimental, tested with RetroLink brand)
|
- Nintendo 64 USB (experimental, tested with RetroLink brand)
|
||||||
- Playstation Classic
|
- Playstation Classic
|
||||||
|
- Generic DInput
|
||||||
|
|
||||||
## Wireless adapters
|
### Wireless adapters
|
||||||
- Xbox 360 PC adapter (Microsoft or clones, syncs 1 controller)
|
- Xbox 360 PC adapter (Microsoft or clones, syncs 1 controller)
|
||||||
- 8Bitdo v1 and v2 Bluetooth adapters
|
- 8Bitdo v1 and v2 Bluetooth adapters (set to XInput mode)
|
||||||
- Most wireless adapters that present themselves as Switch/XInput/Playstation controllers should work
|
- Most wireless adapters that present themselves as Switch/XInput/PlayStation controllers should work
|
||||||
|
|
||||||
Note: There are some third party controllers that use incorrect VID and PIDs, these will not work correctly.
|
Note: There are some third party controllers that can change their VID/PID, these might not work correctly.
|
||||||
|
|
||||||
# Adding supported controllers
|
## Changing input mode
|
||||||
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.
|
By default the input mode is set to OG Xbox, you must hold a button combo for 3 seconds to change which platform you want to play on. Your chosen input mode will persist after powering off the device.
|
||||||
|
|
||||||
# Compiling
|
Start = Plus (Switch) = Options (Dualsense/DS4)
|
||||||
You can compile this for the Pi Pico by commenting out this line in CMakeLists.txt
|
|
||||||
`add_compile_definitions(FEATHER_RP2040)`
|
### XInput
|
||||||
That will set the D+ and D- host pins to GPIO 0 and 1. Below that you can uncomment whichever platform (OG Xbox, Xinput, etc.) you'd like to use.
|
Start + Dpad Up
|
||||||
|
### Original Xbox
|
||||||
|
Start + Dpad Right
|
||||||
|
### Switch
|
||||||
|
Start + Dpad Down
|
||||||
|
### PlayStation 3
|
||||||
|
Start + Dpad Left
|
||||||
|
### PlayStation Classic
|
||||||
|
Start + A (Cross for PlayStation and B for Switch gamepads)
|
||||||
|
|
||||||
|
After a new mode is stored, the RP2040 will reset itself so you don't need to unplug it.
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
I've designed a PCB for the RP2040-Zero so you can make a small form-factor adapter yourself. The gerber files, schematic, and BOM are in Hardware folder.
|
||||||
|
|
||||||
|

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

|

|
||||||
|
|
||||||
# Special thanks
|
For the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723), no extra work is needed.
|
||||||
|
|
||||||
|
## Adding supported controllers
|
||||||
|
If your third party controller isn't working, but the original version is listed above, send me the device's VID and PID and I'll add it so it's recognized properly.
|
||||||
|
|
||||||
|
## Compiling
|
||||||
|
You can compile this for different boards by changing USBD_BOARD in the usbd_config.h file, you can also adjust USBD_MAX_GAMEPADS to enable more controllers on PlayStation 3 (this is experimental).
|
||||||
|
|
||||||
|
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.
|
Thank you to Ryzee119 and the OpenStickCommunity, without their work this project would not exist.
|
||||||
6
hardware/BOM_OGX-Mini_RP2040-Zero_Interposer.csv
Normal file
6
hardware/BOM_OGX-Mini_RP2040-Zero_Interposer.csv
Normal 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,,,,,,,
|
||||||
|
BIN
hardware/Gerber_OGX-Mini_RP2040-Zero_Interposer.zip
Normal file
BIN
hardware/Gerber_OGX-Mini_RP2040-Zero_Interposer.zip
Normal file
Binary file not shown.
5
hardware/README.md
Normal file
5
hardware/README.md
Normal 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.
|
||||||
|
|
||||||
|

|
||||||
1951
hardware/Schematic_OGX-Mini_RP2040-Zero_Interposer.pdf
Normal file
1951
hardware/Schematic_OGX-Mini_RP2040-Zero_Interposer.pdf
Normal file
File diff suppressed because it is too large
Load Diff
BIN
images/OGX-Mini-github.jpg
Normal file
BIN
images/OGX-Mini-github.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 507 KiB |
BIN
images/OGX-Mini-rpzero-int.jpg
Normal file
BIN
images/OGX-Mini-rpzero-int.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 594 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 110 KiB |
@@ -1 +1,2 @@
|
|||||||
add_subdirectory(CRC32)
|
add_subdirectory(CRC32)
|
||||||
|
add_subdirectory(hid_parser)
|
||||||
8
lib/hid_parser/CMakeLists.txt
Normal file
8
lib/hid_parser/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
add_library(hid_parser
|
||||||
|
src/hid_parser.c
|
||||||
|
src/hid_parser.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(hid_parser INTERFACE
|
||||||
|
src
|
||||||
|
)
|
||||||
444
lib/hid_parser/src/hid_parser.c
Normal file
444
lib/hid_parser/src/hid_parser.c
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Adapted from the LUFA Library:
|
||||||
|
*
|
||||||
|
* Copyright 2011 Dean Camera (dean [at] fourwalledcubicle [dot] com)
|
||||||
|
* dean [at] fourwalledcubicle [dot] com, www.lufa-lib.org
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, distribute, and sell this
|
||||||
|
* software and its documentation for any purpose is hereby granted
|
||||||
|
* without fee, provided that the above copyright notice appear in
|
||||||
|
* all copies and that both that the copyright notice and this
|
||||||
|
* permission notice and warranty disclaimer appear in supporting
|
||||||
|
* documentation, and that the name of the author not be used in
|
||||||
|
* advertising or publicity pertaining to distribution of the
|
||||||
|
* software without specific, written prior permission.
|
||||||
|
*
|
||||||
|
* The author disclaim all warranties with regard to this
|
||||||
|
* software, including all implied warranties of merchantability
|
||||||
|
* and fitness. In no event shall the author be liable for any
|
||||||
|
* special, indirect or consequential damages or any damages
|
||||||
|
* whatsoever resulting from loss of use, data or profits, whether
|
||||||
|
* in an action of contract, negligence or other tortious action,
|
||||||
|
* arising out of or in connection with the use or performance of
|
||||||
|
* this software.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "hid_parser.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#ifdef DYNAMIC
|
||||||
|
#include <malloc.h>
|
||||||
|
#define ACQUIRE_AND_RELEASE(structure, size) \
|
||||||
|
__attribute__ ((always_inline)) static inline structure##_t* acquire_##structure() { return malloc(sizeof(structure##_t)); } \
|
||||||
|
__attribute__ ((always_inline)) static inline void release_##structure(structure##_t* pointer) { free(pointer); }
|
||||||
|
#else
|
||||||
|
#define ACQUIRE_AND_RELEASE(structure, size) \
|
||||||
|
enum { MAX_##structure = size }; \
|
||||||
|
static structure##_t structure##s[MAX_##structure]; \
|
||||||
|
static bool structure##sAcquired[MAX_##structure]; \
|
||||||
|
static structure##_t* acquire_##structure() \
|
||||||
|
{\
|
||||||
|
for(uint8_t i=0; i < MAX_##structure; i++) \
|
||||||
|
{\
|
||||||
|
if(!structure##sAcquired[i])\
|
||||||
|
{\
|
||||||
|
structure##sAcquired[i] = true;\
|
||||||
|
return &structure##s[i];\
|
||||||
|
}\
|
||||||
|
}\
|
||||||
|
assert(false);\
|
||||||
|
return NULL;\
|
||||||
|
}\
|
||||||
|
static void release_##structure(structure##_t* pointer)\
|
||||||
|
{\
|
||||||
|
structure##sAcquired[pointer - structure##s] = false;\
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ACQUIRE_AND_RELEASE(HID_ReportSizeInfo, 100);
|
||||||
|
ACQUIRE_AND_RELEASE(HID_CollectionPath, 25);
|
||||||
|
ACQUIRE_AND_RELEASE(HID_ReportInfo, 1);
|
||||||
|
ACQUIRE_AND_RELEASE(HID_ReportItem, 50);
|
||||||
|
|
||||||
|
void USB_FreeReportInfo(HID_ReportInfo_t *ReportInfo)
|
||||||
|
{
|
||||||
|
if (ReportInfo)
|
||||||
|
{
|
||||||
|
HID_ReportItem_t *item = ReportInfo->FirstReportItem;
|
||||||
|
while (item)
|
||||||
|
{
|
||||||
|
HID_ReportItem_t *current = item;
|
||||||
|
item = item->Next;
|
||||||
|
release_HID_ReportItem(current);
|
||||||
|
}
|
||||||
|
release_HID_ReportInfo(ReportInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t USB_ProcessHIDReport(const uint8_t *ReportData,
|
||||||
|
uint16_t ReportSize,
|
||||||
|
HID_ReportInfo_t **ParserDataOut)
|
||||||
|
{
|
||||||
|
HID_ReportSizeInfo_t *FirstReportIDSize = acquire_HID_ReportSizeInfo();
|
||||||
|
HID_CollectionPath_t *FirstCollectionPath = acquire_HID_CollectionPath();
|
||||||
|
memset(FirstCollectionPath, 0, sizeof(HID_CollectionPath_t));
|
||||||
|
HID_ReportInfo_t *ParserData = acquire_HID_ReportInfo();
|
||||||
|
HID_StateTable_t StateTable[HID_STATETABLE_STACK_DEPTH];
|
||||||
|
HID_StateTable_t *CurrStateTable = &StateTable[0];
|
||||||
|
HID_CollectionPath_t *CurrCollectionPath = NULL;
|
||||||
|
HID_ReportSizeInfo_t *CurrReportIDInfo = FirstReportIDSize;
|
||||||
|
uint16_t UsageList[HID_USAGE_STACK_DEPTH];
|
||||||
|
uint8_t UsageListSize = 0;
|
||||||
|
HID_MinMax_t UsageMinMax = {0, 0};
|
||||||
|
|
||||||
|
memset(ParserData, 0x00, sizeof(HID_ReportInfo_t));
|
||||||
|
memset(CurrStateTable, 0x00, sizeof(HID_StateTable_t));
|
||||||
|
memset(CurrReportIDInfo, 0x00, sizeof(HID_ReportSizeInfo_t));
|
||||||
|
|
||||||
|
ParserData->TotalDeviceReports = 1;
|
||||||
|
uint8_t Result = HID_PARSE_Successful;
|
||||||
|
|
||||||
|
while (ReportSize)
|
||||||
|
{
|
||||||
|
uint8_t HIDReportItem = *ReportData;
|
||||||
|
uint32_t ReportItemData;
|
||||||
|
|
||||||
|
ReportData++;
|
||||||
|
ReportSize--;
|
||||||
|
|
||||||
|
switch (HIDReportItem & HID_RI_DATA_SIZE_MASK)
|
||||||
|
{
|
||||||
|
case HID_RI_DATA_BITS_32:
|
||||||
|
ReportItemData = (((uint32_t)ReportData[3] << 24) | ((uint32_t)ReportData[2] << 16) |
|
||||||
|
((uint16_t)ReportData[1] << 8) | ReportData[0]);
|
||||||
|
ReportSize -= 4;
|
||||||
|
ReportData += 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_DATA_BITS_16:
|
||||||
|
ReportItemData = (((uint16_t)ReportData[1] << 8) | (ReportData[0]));
|
||||||
|
ReportSize -= 2;
|
||||||
|
ReportData += 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_DATA_BITS_8:
|
||||||
|
ReportItemData = ReportData[0];
|
||||||
|
ReportSize -= 1;
|
||||||
|
ReportData += 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ReportItemData = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (HIDReportItem & (HID_RI_TYPE_MASK | HID_RI_TAG_MASK))
|
||||||
|
{
|
||||||
|
case HID_RI_PUSH(0):
|
||||||
|
if (CurrStateTable == &StateTable[HID_STATETABLE_STACK_DEPTH - 1])
|
||||||
|
{
|
||||||
|
Result = HID_PARSE_HIDStackOverflow;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy((CurrStateTable + 1),
|
||||||
|
CurrStateTable,
|
||||||
|
sizeof(HID_StateTable_t));
|
||||||
|
|
||||||
|
CurrStateTable++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_POP(0):
|
||||||
|
if (CurrStateTable == &StateTable[0])
|
||||||
|
{
|
||||||
|
Result = HID_PARSE_HIDStackUnderflow;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrStateTable--;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_USAGE_PAGE(0):
|
||||||
|
CurrStateTable->Attributes.Usage.Page = ReportItemData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_LOGICAL_MINIMUM(0):
|
||||||
|
CurrStateTable->Attributes.Logical.Minimum = ReportItemData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_LOGICAL_MAXIMUM(0):
|
||||||
|
CurrStateTable->Attributes.Logical.Maximum = ReportItemData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_PHYSICAL_MINIMUM(0):
|
||||||
|
CurrStateTable->Attributes.Physical.Minimum = ReportItemData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_PHYSICAL_MAXIMUM(0):
|
||||||
|
CurrStateTable->Attributes.Physical.Maximum = ReportItemData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_UNIT_EXPONENT(0):
|
||||||
|
CurrStateTable->Attributes.Unit.Exponent = ReportItemData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_UNIT(0):
|
||||||
|
CurrStateTable->Attributes.Unit.Type = ReportItemData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_REPORT_SIZE(0):
|
||||||
|
CurrStateTable->Attributes.BitSize = ReportItemData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_REPORT_COUNT(0):
|
||||||
|
CurrStateTable->ReportCount = ReportItemData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_REPORT_ID(0):
|
||||||
|
CurrStateTable->ReportID = ReportItemData;
|
||||||
|
|
||||||
|
if (ParserData->UsingReportIDs)
|
||||||
|
{
|
||||||
|
CurrReportIDInfo = NULL;
|
||||||
|
HID_ReportSizeInfo_t *iterator = FirstReportIDSize;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (iterator->ReportID == CurrStateTable->ReportID)
|
||||||
|
{
|
||||||
|
CurrReportIDInfo = iterator;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!iterator->Next)
|
||||||
|
break;
|
||||||
|
iterator = iterator->Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CurrReportIDInfo == NULL)
|
||||||
|
{
|
||||||
|
ParserData->TotalDeviceReports++;
|
||||||
|
iterator->Next = CurrReportIDInfo = acquire_HID_ReportSizeInfo();
|
||||||
|
memset(CurrReportIDInfo, 0x00, sizeof(HID_ReportSizeInfo_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParserData->UsingReportIDs = true;
|
||||||
|
|
||||||
|
CurrReportIDInfo->ReportID = CurrStateTable->ReportID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_USAGE(0):
|
||||||
|
if (UsageListSize == HID_USAGE_STACK_DEPTH)
|
||||||
|
{
|
||||||
|
Result = HID_PARSE_UsageListOverflow;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((HIDReportItem & HID_RI_DATA_SIZE_MASK) == HID_RI_DATA_BITS_32)
|
||||||
|
CurrStateTable->Attributes.Usage.Page = (ReportItemData >> 16);
|
||||||
|
|
||||||
|
UsageList[UsageListSize++] = ReportItemData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_USAGE_MINIMUM(0):
|
||||||
|
UsageMinMax.Minimum = ReportItemData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_USAGE_MAXIMUM(0):
|
||||||
|
UsageMinMax.Maximum = ReportItemData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_COLLECTION(0):
|
||||||
|
if (CurrCollectionPath == NULL)
|
||||||
|
{
|
||||||
|
CurrCollectionPath = FirstCollectionPath;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HID_CollectionPath_t *ParentCollectionPath = CurrCollectionPath;
|
||||||
|
|
||||||
|
CurrCollectionPath = FirstCollectionPath;
|
||||||
|
while (CurrCollectionPath->Next)
|
||||||
|
{
|
||||||
|
CurrCollectionPath = CurrCollectionPath->Next;
|
||||||
|
}
|
||||||
|
HID_CollectionPath_t *NewCollectionPath = acquire_HID_CollectionPath();
|
||||||
|
CurrCollectionPath->Next = NewCollectionPath;
|
||||||
|
CurrCollectionPath = NewCollectionPath;
|
||||||
|
memset(CurrCollectionPath, 0, sizeof(HID_CollectionPath_t));
|
||||||
|
CurrCollectionPath->Parent = ParentCollectionPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrCollectionPath->Type = ReportItemData;
|
||||||
|
CurrCollectionPath->Usage.Page = CurrStateTable->Attributes.Usage.Page;
|
||||||
|
|
||||||
|
if (UsageListSize)
|
||||||
|
{
|
||||||
|
CurrCollectionPath->Usage.Usage = UsageList[0];
|
||||||
|
|
||||||
|
for (uint8_t i = 1; i < UsageListSize; i++)
|
||||||
|
UsageList[i - 1] = UsageList[i];
|
||||||
|
|
||||||
|
UsageListSize--;
|
||||||
|
}
|
||||||
|
else if (UsageMinMax.Minimum <= UsageMinMax.Maximum)
|
||||||
|
{
|
||||||
|
CurrCollectionPath->Usage.Usage = UsageMinMax.Minimum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_END_COLLECTION(0):
|
||||||
|
if (CurrCollectionPath == NULL)
|
||||||
|
{
|
||||||
|
Result = HID_PARSE_UnexpectedEndCollection;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CurrCollectionPath = CurrCollectionPath->Parent;
|
||||||
|
if (CurrCollectionPath)
|
||||||
|
{
|
||||||
|
release_HID_CollectionPath(CurrCollectionPath->Next);
|
||||||
|
CurrCollectionPath->Next = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_RI_INPUT(0):
|
||||||
|
case HID_RI_OUTPUT(0):
|
||||||
|
case HID_RI_FEATURE(0):
|
||||||
|
for (uint8_t ReportItemNum = 0; ReportItemNum < CurrStateTable->ReportCount; ReportItemNum++)
|
||||||
|
{
|
||||||
|
HID_ReportItem_t NewReportItem;
|
||||||
|
|
||||||
|
memcpy(&NewReportItem.Attributes,
|
||||||
|
&CurrStateTable->Attributes,
|
||||||
|
sizeof(HID_ReportItem_Attributes_t));
|
||||||
|
|
||||||
|
NewReportItem.ItemFlags = ReportItemData;
|
||||||
|
NewReportItem.ReportID = CurrStateTable->ReportID;
|
||||||
|
if (UsageListSize)
|
||||||
|
{
|
||||||
|
NewReportItem.Attributes.Usage.Usage = UsageList[0];
|
||||||
|
|
||||||
|
for (uint8_t i = 1; i < UsageListSize; i++)
|
||||||
|
UsageList[i - 1] = UsageList[i];
|
||||||
|
|
||||||
|
UsageListSize--;
|
||||||
|
}
|
||||||
|
else if (UsageMinMax.Minimum <= UsageMinMax.Maximum)
|
||||||
|
{
|
||||||
|
NewReportItem.Attributes.Usage.Usage = UsageMinMax.Minimum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ItemTypeTag = (HIDReportItem & (HID_RI_TYPE_MASK | HID_RI_TAG_MASK));
|
||||||
|
|
||||||
|
if (ItemTypeTag == HID_RI_INPUT(0))
|
||||||
|
NewReportItem.ItemType = HID_REPORT_ITEM_In;
|
||||||
|
else if (ItemTypeTag == HID_RI_OUTPUT(0))
|
||||||
|
NewReportItem.ItemType = HID_REPORT_ITEM_Out;
|
||||||
|
else
|
||||||
|
NewReportItem.ItemType = HID_REPORT_ITEM_Feature;
|
||||||
|
|
||||||
|
NewReportItem.BitOffset = CurrReportIDInfo->ReportSizeBits[NewReportItem.ItemType];
|
||||||
|
|
||||||
|
CurrReportIDInfo->ReportSizeBits[NewReportItem.ItemType] += CurrStateTable->Attributes.BitSize;
|
||||||
|
|
||||||
|
ParserData->LargestReportSizeBits = MAX(ParserData->LargestReportSizeBits, CurrReportIDInfo->ReportSizeBits[NewReportItem.ItemType]);
|
||||||
|
|
||||||
|
if (!(ReportItemData & HID_IOF_CONSTANT) && CALLBACK_HIDParser_FilterHIDReportItem(&NewReportItem))
|
||||||
|
{
|
||||||
|
if (!ParserData->FirstReportItem)
|
||||||
|
{
|
||||||
|
ParserData->FirstReportItem = acquire_HID_ReportItem();
|
||||||
|
ParserData->LastReportItem = ParserData->FirstReportItem;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ParserData->LastReportItem->Next = acquire_HID_ReportItem();
|
||||||
|
ParserData->LastReportItem = ParserData->LastReportItem->Next;
|
||||||
|
}
|
||||||
|
memcpy(ParserData->LastReportItem, &NewReportItem, sizeof(HID_ReportItem_t));
|
||||||
|
ParserData->LastReportItem->Next = NULL;
|
||||||
|
ParserData->TotalReportItems++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (Result != HID_PARSE_Successful)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((HIDReportItem & HID_RI_TYPE_MASK) == HID_RI_TYPE_MAIN)
|
||||||
|
{
|
||||||
|
UsageMinMax.Minimum = 0;
|
||||||
|
UsageMinMax.Maximum = 0;
|
||||||
|
UsageListSize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(ParserData->TotalReportItems))
|
||||||
|
Result = HID_PARSE_NoUnfilteredReportItems;
|
||||||
|
if (Result != HID_PARSE_Successful)
|
||||||
|
{
|
||||||
|
USB_FreeReportInfo(ParserData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*ParserDataOut = ParserData;
|
||||||
|
}
|
||||||
|
|
||||||
|
HID_ReportSizeInfo_t *iteratorReportIDSizes = FirstReportIDSize;
|
||||||
|
while (iteratorReportIDSizes)
|
||||||
|
{
|
||||||
|
HID_ReportSizeInfo_t *temp = iteratorReportIDSizes->Next;
|
||||||
|
release_HID_ReportSizeInfo(iteratorReportIDSizes);
|
||||||
|
iteratorReportIDSizes = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
HID_CollectionPath_t *iteratorCollectionPaths = FirstCollectionPath;
|
||||||
|
while (iteratorCollectionPaths)
|
||||||
|
{
|
||||||
|
HID_CollectionPath_t *temp = iteratorCollectionPaths->Next;
|
||||||
|
release_HID_CollectionPath(iteratorCollectionPaths);
|
||||||
|
iteratorCollectionPaths = temp;
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool USB_GetHIDReportItemInfo(uint16_t report_id, const uint8_t *ReportData,
|
||||||
|
HID_ReportItem_t *const ReportItem)
|
||||||
|
{
|
||||||
|
if (ReportItem == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReportItem->ReportID != report_id)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t DataBitsRem = ReportItem->Attributes.BitSize;
|
||||||
|
uint16_t CurrentBit = ReportItem->BitOffset;
|
||||||
|
uint32_t BitMask = (1 << 0);
|
||||||
|
|
||||||
|
ReportItem->PreviousValue = ReportItem->Value;
|
||||||
|
ReportItem->Value = 0;
|
||||||
|
|
||||||
|
while (DataBitsRem--)
|
||||||
|
{
|
||||||
|
if (ReportData[CurrentBit / 8] & (1 << (CurrentBit % 8)))
|
||||||
|
ReportItem->Value |= BitMask;
|
||||||
|
|
||||||
|
CurrentBit++;
|
||||||
|
BitMask <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
424
lib/hid_parser/src/hid_parser.h
Normal file
424
lib/hid_parser/src/hid_parser.h
Normal file
@@ -0,0 +1,424 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* Adapted from the LUFA Library:
|
||||||
|
*
|
||||||
|
* Copyright 2011 Dean Camera (dean [at] fourwalledcubicle [dot] com)
|
||||||
|
* dean [at] fourwalledcubicle [dot] com, www.lufa-lib.org
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, distribute, and sell this
|
||||||
|
* software and its documentation for any purpose is hereby granted
|
||||||
|
* without fee, provided that the above copyright notice appear in
|
||||||
|
* all copies and that both that the copyright notice and this
|
||||||
|
* permission notice and warranty disclaimer appear in supporting
|
||||||
|
* documentation, and that the name of the author not be used in
|
||||||
|
* advertising or publicity pertaining to distribution of the
|
||||||
|
* software without specific, written prior permission.
|
||||||
|
*
|
||||||
|
* The author disclaim all warranties with regard to this
|
||||||
|
* software, including all implied warranties of merchantability
|
||||||
|
* and fitness. In no event shall the author be liable for any
|
||||||
|
* special, indirect or consequential damages or any damages
|
||||||
|
* whatsoever resulting from loss of use, data or profits, whether
|
||||||
|
* in an action of contract, negligence or other tortious action,
|
||||||
|
* arising out of or in connection with the use or performance of
|
||||||
|
* this software.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \brief USB Human Interface Device (HID) Class report descriptor parser.
|
||||||
|
*
|
||||||
|
* This file allows for the easy parsing of complex HID report descriptors, which describes the data that
|
||||||
|
* a HID device transmits to the host. It also provides an easy API for extracting and processing the data
|
||||||
|
* elements inside a HID report sent from an attached HID device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** \ingroup Group_USB
|
||||||
|
* \defgroup Group_HIDParser HID Report Parser
|
||||||
|
* \brief USB Human Interface Device (HID) Class report descriptor parser.
|
||||||
|
*
|
||||||
|
* \section Sec_HIDParser_Dependencies Module Source Dependencies
|
||||||
|
* The following files must be built with any user project that uses this module:
|
||||||
|
* - LUFA/Drivers/USB/Class/Host/HIDParser.c <i>(Makefile source module name: LUFA_SRC_USB)</i>
|
||||||
|
*
|
||||||
|
* \section Sec_HIDParser_ModDescription Module Description
|
||||||
|
* Human Interface Device (HID) class report descriptor parser. This module implements a parser than is
|
||||||
|
* capable of processing a complete HID report descriptor, and outputting a flat structure containing the
|
||||||
|
* contents of the report in an a more friendly format. The parsed data may then be further processed and used
|
||||||
|
* within an application to process sent and received HID reports to and from an attached HID device.
|
||||||
|
*
|
||||||
|
* A HID report descriptor consists of a set of HID report items, which describe the function and layout
|
||||||
|
* of data exchanged between a HID device and a host, including both the physical encoding of each item
|
||||||
|
* (such as a button, key press or joystick axis) in the sent and received data packets - known as "reports" -
|
||||||
|
* as well as other information about each item such as the usages, data range, physical location and other
|
||||||
|
* characteristics. In this way a HID device can retain a high degree of flexibility in its capabilities, as it
|
||||||
|
* is not forced to comply with a given report layout or feature-set.
|
||||||
|
*
|
||||||
|
* This module also contains routines for the processing of data in an actual HID report, using the parsed report
|
||||||
|
* descriptor data as a guide for the encoding.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* Includes: */
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/* Enable C linkage for C++ Compilers: */
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
/* Macros: */
|
||||||
|
#if !defined(HID_STATETABLE_STACK_DEPTH) || defined(__DOXYGEN__)
|
||||||
|
/** Constant indicating the maximum stack depth of the state table. A larger state table
|
||||||
|
* allows for more PUSH/POP report items to be nested, but consumes more memory. By default
|
||||||
|
* this is set to 2 levels (allowing non-nested PUSH items) but this can be overridden by
|
||||||
|
* defining \c HID_STATETABLE_STACK_DEPTH to another value in the user project makefile, passing the
|
||||||
|
* define to the compiler using the -D compiler switch.
|
||||||
|
*/
|
||||||
|
#define HID_STATETABLE_STACK_DEPTH 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(HID_USAGE_STACK_DEPTH) || defined(__DOXYGEN__)
|
||||||
|
/** Constant indicating the maximum stack depth of the usage table. A larger usage table
|
||||||
|
* allows for more USAGE items to be indicated sequentially for REPORT COUNT entries of more than
|
||||||
|
* one, but requires more stack space. By default this is set to 8 levels (allowing for a report
|
||||||
|
* item with a count of 8) but this can be overridden by defining \c HID_USAGE_STACK_DEPTH to another
|
||||||
|
* value in the user project makefile, passing the define to the compiler using the -D compiler
|
||||||
|
* switch.
|
||||||
|
*/
|
||||||
|
#define HID_USAGE_STACK_DEPTH 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Returns the value a given HID report item (once its value has been fetched via \ref USB_GetHIDReportItemInfo())
|
||||||
|
* left-aligned to the given data type. This allows for signed data to be interpreted correctly, by shifting the data
|
||||||
|
* leftwards until the data's sign bit is in the correct position.
|
||||||
|
*
|
||||||
|
* \param[in] ReportItem HID Report Item whose retrieved value is to be aligned.
|
||||||
|
* \param[in] Type Data type to align the HID report item's value to.
|
||||||
|
*
|
||||||
|
* \return Left-aligned data of the given report item's pre-retrieved value for the given datatype.
|
||||||
|
*/
|
||||||
|
#define HID_ALIGN_DATA(ReportItem, Type) ((Type)(ReportItem->Value << ((8 * sizeof(Type)) - ReportItem->Attributes.BitSize)))
|
||||||
|
|
||||||
|
/** Convenience macro to determine the larger of two values.
|
||||||
|
*
|
||||||
|
* \attention This macro should only be used with operands that do not have side effects from being evaluated
|
||||||
|
* multiple times.
|
||||||
|
*
|
||||||
|
* \param[in] x First value to compare
|
||||||
|
* \param[in] y First value to compare
|
||||||
|
*
|
||||||
|
* \return The larger of the two input parameters
|
||||||
|
*/
|
||||||
|
#if !defined(MAX) || defined(__DOXYGEN__)
|
||||||
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Convenience macro to determine the smaller of two values.
|
||||||
|
*
|
||||||
|
* \attention This macro should only be used with operands that do not have side effects from being evaluated
|
||||||
|
* multiple times.
|
||||||
|
*
|
||||||
|
* \param[in] x First value to compare.
|
||||||
|
* \param[in] y First value to compare.
|
||||||
|
*
|
||||||
|
* \return The smaller of the two input parameters
|
||||||
|
*/
|
||||||
|
#if !defined(MIN) || defined(__DOXYGEN__)
|
||||||
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(CONCAT) || defined(__DOXYGEN__)
|
||||||
|
/** Concatenates the given input into a single token, via the C Preprocessor.
|
||||||
|
*
|
||||||
|
* \param[in] x First item to concatenate.
|
||||||
|
* \param[in] y Second item to concatenate.
|
||||||
|
*
|
||||||
|
* \return Concatenated version of the input.
|
||||||
|
*/
|
||||||
|
#define CONCAT(x, y) x##y
|
||||||
|
|
||||||
|
/** CConcatenates the given input into a single token after macro expansion, via the C Preprocessor.
|
||||||
|
*
|
||||||
|
* \param[in] x First item to concatenate.
|
||||||
|
* \param[in] y Second item to concatenate.
|
||||||
|
*
|
||||||
|
* \return Concatenated version of the expanded input.
|
||||||
|
*/
|
||||||
|
#define CONCAT_EXPANDED(x, y) CONCAT(x, y)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Public Interface - May be used in end-application: */
|
||||||
|
/* Enums: */
|
||||||
|
/** Enum for the possible error codes in the return value of the \ref USB_ProcessHIDReport() function. */
|
||||||
|
enum HID_Parse_ErrorCodes_t
|
||||||
|
{
|
||||||
|
HID_PARSE_Successful = 0, /**< Successful parse of the HID report descriptor, no error. */
|
||||||
|
HID_PARSE_HIDStackOverflow = 1, /**< More than \ref HID_STATETABLE_STACK_DEPTH nested PUSHes in the report. */
|
||||||
|
HID_PARSE_HIDStackUnderflow = 2, /**< A POP was found when the state table stack was empty. */
|
||||||
|
HID_PARSE_UnexpectedEndCollection = 3, /**< An END COLLECTION item found without matching COLLECTION item. */
|
||||||
|
HID_PARSE_UsageListOverflow = 4, /**< More than \ref HID_USAGE_STACK_DEPTH usages listed in a row. */
|
||||||
|
HID_PARSE_NoUnfilteredReportItems = 5, /**< All report items from the device were filtered by the filtering callback routine. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Private Interface - For use in library only: */
|
||||||
|
#if !defined(__DOXYGEN__)
|
||||||
|
/* Macros: */
|
||||||
|
#define HID_RI_DATA_SIZE_MASK 0x03
|
||||||
|
#define HID_RI_TYPE_MASK 0x0C
|
||||||
|
#define HID_RI_TAG_MASK 0xF0
|
||||||
|
|
||||||
|
#define HID_RI_TYPE_MAIN 0x00
|
||||||
|
#define HID_RI_TYPE_GLOBAL 0x04
|
||||||
|
#define HID_RI_TYPE_LOCAL 0x08
|
||||||
|
|
||||||
|
#define HID_RI_DATA_BITS_0 0x00
|
||||||
|
#define HID_RI_DATA_BITS_8 0x01
|
||||||
|
#define HID_RI_DATA_BITS_16 0x02
|
||||||
|
#define HID_RI_DATA_BITS_32 0x03
|
||||||
|
#define HID_RI_DATA_BITS(DataBits) CONCAT_EXPANDED(HID_RI_DATA_BITS_, DataBits)
|
||||||
|
|
||||||
|
#define _HID_RI_ENCODE_0(Data)
|
||||||
|
#define _HID_RI_ENCODE_8(Data) , (Data & 0xFF)
|
||||||
|
#define _HID_RI_ENCODE_16(Data) \
|
||||||
|
_HID_RI_ENCODE_8(Data) \
|
||||||
|
_HID_RI_ENCODE_8(Data >> 8)
|
||||||
|
#define _HID_RI_ENCODE_32(Data) \
|
||||||
|
_HID_RI_ENCODE_16(Data) \
|
||||||
|
_HID_RI_ENCODE_16(Data >> 16)
|
||||||
|
#define _HID_RI_ENCODE(DataBits, ...) CONCAT_EXPANDED(_HID_RI_ENCODE_, DataBits(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define _HID_RI_ENTRY(Type, Tag, DataBits, ...) (Type | Tag | HID_RI_DATA_BITS(DataBits)) _HID_RI_ENCODE(DataBits, (__VA_ARGS__))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Public Interface - May be used in end-application: */
|
||||||
|
/* Macros: */
|
||||||
|
/** \name HID Input, Output and Feature Report Descriptor Item Flags */
|
||||||
|
/**@{*/
|
||||||
|
#define HID_IOF_CONSTANT (1 << 0)
|
||||||
|
#define HID_IOF_DATA (0 << 0)
|
||||||
|
#define HID_IOF_VARIABLE (1 << 1)
|
||||||
|
#define HID_IOF_ARRAY (0 << 1)
|
||||||
|
#define HID_IOF_RELATIVE (1 << 2)
|
||||||
|
#define HID_IOF_ABSOLUTE (0 << 2)
|
||||||
|
#define HID_IOF_WRAP (1 << 3)
|
||||||
|
#define HID_IOF_NO_WRAP (0 << 3)
|
||||||
|
#define HID_IOF_NON_LINEAR (1 << 4)
|
||||||
|
#define HID_IOF_LINEAR (0 << 4)
|
||||||
|
#define HID_IOF_NO_PREFERRED_STATE (1 << 5)
|
||||||
|
#define HID_IOF_PREFERRED_STATE (0 << 5)
|
||||||
|
#define HID_IOF_NULLSTATE (1 << 6)
|
||||||
|
#define HID_IOF_NO_NULL_POSITION (0 << 6)
|
||||||
|
#define HID_IOF_VOLATILE (1 << 7)
|
||||||
|
#define HID_IOF_NON_VOLATILE (0 << 7)
|
||||||
|
#define HID_IOF_BUFFERED_BYTES (1 << 8)
|
||||||
|
#define HID_IOF_BITFIELD (0 << 8)
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** \name HID Report Descriptor Item Macros */
|
||||||
|
/**@{*/
|
||||||
|
#define HID_RI_INPUT(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_MAIN, 0x80, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_OUTPUT(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_MAIN, 0x90, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_COLLECTION(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_MAIN, 0xA0, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_FEATURE(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_MAIN, 0xB0, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_END_COLLECTION(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_MAIN, 0xC0, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_USAGE_PAGE(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x00, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_LOGICAL_MINIMUM(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x10, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_LOGICAL_MAXIMUM(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x20, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_PHYSICAL_MINIMUM(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x30, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_PHYSICAL_MAXIMUM(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x40, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_UNIT_EXPONENT(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x50, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_UNIT(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x60, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_REPORT_SIZE(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x70, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_REPORT_ID(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x80, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_REPORT_COUNT(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x90, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_PUSH(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0xA0, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_POP(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0xB0, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_USAGE(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_LOCAL, 0x00, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_USAGE_MINIMUM(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_LOCAL, 0x10, DataBits, __VA_ARGS__)
|
||||||
|
#define HID_RI_USAGE_MAXIMUM(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_LOCAL, 0x20, DataBits, __VA_ARGS__)
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/* Type Defines: */
|
||||||
|
/** \brief HID Parser Report Item Min/Max Structure.
|
||||||
|
*
|
||||||
|
* Type define for an attribute with both minimum and maximum values (e.g. Logical Min/Max).
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t Minimum; /**< Minimum value for the attribute. */
|
||||||
|
uint32_t Maximum; /**< Maximum value for the attribute. */
|
||||||
|
} HID_MinMax_t;
|
||||||
|
|
||||||
|
/** \brief HID Parser Report Item Unit Structure.
|
||||||
|
*
|
||||||
|
* Type define for the Unit attributes of a report item.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t Type; /**< Unit type (refer to HID specifications for details). */
|
||||||
|
uint8_t Exponent; /**< Unit exponent (refer to HID specifications for details). */
|
||||||
|
} HID_Unit_t;
|
||||||
|
|
||||||
|
/** \brief HID Parser Report Item Usage Structure.
|
||||||
|
*
|
||||||
|
* Type define for the Usage attributes of a report item.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t Page; /**< Usage page of the report item. */
|
||||||
|
uint16_t Usage; /**< Usage of the report item. */
|
||||||
|
} HID_Usage_t;
|
||||||
|
|
||||||
|
/** \brief HID Parser Report Item Collection Path Structure.
|
||||||
|
*
|
||||||
|
* Type define for a COLLECTION object. Contains the collection attributes and a reference to the
|
||||||
|
* parent collection if any.
|
||||||
|
*/
|
||||||
|
typedef struct HID_CollectionPath
|
||||||
|
{
|
||||||
|
uint8_t Type; /**< Collection type (e.g. "Generic Desktop"). */
|
||||||
|
HID_Usage_t Usage; /**< Collection usage. */
|
||||||
|
struct HID_CollectionPath *Parent; /**< Reference to parent collection, or \c NULL if root collection. */
|
||||||
|
struct HID_CollectionPath *Next; /**< Reference to parent collection, or \c NULL if root collection. */
|
||||||
|
} HID_CollectionPath_t;
|
||||||
|
|
||||||
|
/** \brief HID Parser Report Item Attributes Structure.
|
||||||
|
*
|
||||||
|
* Type define for all the data attributes of a report item, except flags.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t BitSize; /**< Size in bits of the report item's data. */
|
||||||
|
HID_Usage_t Usage; /**< Usage of the report item. */
|
||||||
|
HID_Unit_t Unit; /**< Unit type and exponent of the report item. */
|
||||||
|
HID_MinMax_t Logical; /**< Logical minimum and maximum of the report item. */
|
||||||
|
HID_MinMax_t Physical; /**< Physical minimum and maximum of the report item. */
|
||||||
|
} HID_ReportItem_Attributes_t;
|
||||||
|
|
||||||
|
/** \brief HID Parser Report Item Details Structure.
|
||||||
|
*
|
||||||
|
* Type define for a report item (IN, OUT or FEATURE) layout attributes and other details.
|
||||||
|
*/
|
||||||
|
typedef struct HID_ReportItem_s
|
||||||
|
{
|
||||||
|
uint16_t BitOffset; /**< Bit offset in the IN, OUT or FEATURE report of the item. */
|
||||||
|
uint8_t ItemType; /**< Report item type, a value in \ref HID_ReportItemTypes_t. */
|
||||||
|
uint16_t ItemFlags; /**< Item data flags, a mask of \c HID_IOF_* constants. */
|
||||||
|
uint8_t ReportID; /**< Report ID this item belongs to, or 0x00 if device has only one report */
|
||||||
|
HID_ReportItem_Attributes_t Attributes; /**< Report item attributes. */
|
||||||
|
uint32_t Value; /**< Current value of the report item - use \ref HID_ALIGN_DATA() when processing
|
||||||
|
* a retrieved value so that it is aligned to a specific type.
|
||||||
|
*/
|
||||||
|
uint32_t PreviousValue; /**< Previous value of the report item. */
|
||||||
|
struct HID_ReportItem_s* Next;
|
||||||
|
} HID_ReportItem_t;
|
||||||
|
|
||||||
|
/** \brief HID Parser Report Size Structure.
|
||||||
|
*
|
||||||
|
* Type define for a report item size information structure, to retain the size of a device's reports by ID.
|
||||||
|
*/
|
||||||
|
typedef struct HID_ReportSizeInfo_s
|
||||||
|
{
|
||||||
|
uint8_t ReportID; /**< Report ID of the report within the HID interface. */
|
||||||
|
uint16_t ReportSizeBits[3]; /**< Total number of bits in each report type for the given Report ID,
|
||||||
|
* indexed by the \ref HID_ReportItemTypes_t enum.
|
||||||
|
*/
|
||||||
|
struct HID_ReportSizeInfo_s* Next;
|
||||||
|
} HID_ReportSizeInfo_t;
|
||||||
|
|
||||||
|
/** \brief HID Parser State Structure.
|
||||||
|
*
|
||||||
|
* Type define for a complete processed HID report, including all report item data and collections.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t TotalReportItems; /**< Total number of report items stored in the \c ReportItems array. */
|
||||||
|
HID_ReportItem_t* FirstReportItem; /**< Report items array, including all IN, OUT
|
||||||
|
* and FEATURE items.
|
||||||
|
*/
|
||||||
|
uint8_t TotalDeviceReports; /**< Number of reports within the HID interface */
|
||||||
|
uint16_t LargestReportSizeBits; /**< Largest report that the attached device will generate, in bits */
|
||||||
|
bool UsingReportIDs; /**< Indicates if the device has at least one REPORT ID
|
||||||
|
* element in its HID report descriptor.
|
||||||
|
*/
|
||||||
|
HID_ReportItem_t* LastReportItem;
|
||||||
|
} HID_ReportInfo_t;
|
||||||
|
|
||||||
|
/* Function Prototypes: */
|
||||||
|
/** Function to process a given HID report returned from an attached device, and store it into a given
|
||||||
|
* \ref HID_ReportInfo_t structure.
|
||||||
|
*
|
||||||
|
* \param[in] ReportData Buffer containing the device's HID report table.
|
||||||
|
* \param[in] ReportSize Size in bytes of the HID report table.
|
||||||
|
* \param[out] ParserData Pointer to a \ref HID_ReportInfo_t instance for the parser output.
|
||||||
|
*
|
||||||
|
* \return A value in the \ref HID_Parse_ErrorCodes_t enum.
|
||||||
|
*/
|
||||||
|
uint8_t USB_ProcessHIDReport(const uint8_t *ReportData,
|
||||||
|
uint16_t ReportSize,
|
||||||
|
HID_ReportInfo_t **ParserData);
|
||||||
|
|
||||||
|
void USB_FreeReportInfo(HID_ReportInfo_t *ReportInfo);
|
||||||
|
|
||||||
|
/** Extracts the given report item's value out of the given HID report and places it into the Value
|
||||||
|
* member of the report item's \ref HID_ReportItem_t structure.
|
||||||
|
*
|
||||||
|
* When called on a report with an item that exists in that report, this copies the report item's \c Value
|
||||||
|
* to its \c PreviousValue element for easy checking to see if an item's value has changed before processing
|
||||||
|
* a report. If the given item does not exist in the report, the function does not modify the report item's
|
||||||
|
* data.
|
||||||
|
*
|
||||||
|
* \param[in] ReportData Buffer containing an IN or FEATURE report from an attached device.
|
||||||
|
* \param[in,out] ReportItem Pointer to the report item of interest in a \ref HID_ReportInfo_t ReportItem array.
|
||||||
|
*
|
||||||
|
* \returns Boolean \c true if the item to retrieve was located in the given report, \c false otherwise.
|
||||||
|
*/
|
||||||
|
bool USB_GetHIDReportItemInfo(uint16_t report_id, const uint8_t *ReportData,
|
||||||
|
HID_ReportItem_t *const ReportItem);
|
||||||
|
|
||||||
|
/** Callback routine for the HID Report Parser. This callback <b>must</b> be implemented by the user code when
|
||||||
|
* the parser is used, to determine what report IN, OUT and FEATURE item's information is stored into the user
|
||||||
|
* \ref HID_ReportInfo_t structure. This can be used to filter only those items the application will be using, so that
|
||||||
|
* no RAM is wasted storing the attributes for report items which will never be referenced by the application.
|
||||||
|
*
|
||||||
|
* Report item pointers passed to this callback function may be cached by the user application for later use
|
||||||
|
* when processing report items. This provides faster report processing in the user application than would
|
||||||
|
* a search of the entire parsed report item table for each received or sent report.
|
||||||
|
*
|
||||||
|
* \param[in] CurrentItem Pointer to the current report item for user checking.
|
||||||
|
*
|
||||||
|
* \return Boolean \c true if the item should be stored into the \ref HID_ReportInfo_t structure, \c false if
|
||||||
|
* it should be ignored.
|
||||||
|
*/
|
||||||
|
bool CALLBACK_HIDParser_FilterHIDReportItem(HID_ReportItem_t *const CurrentItem);
|
||||||
|
|
||||||
|
/** Enum for the different types of HID reports. */
|
||||||
|
enum HID_ReportItemTypes_t
|
||||||
|
{
|
||||||
|
HID_REPORT_ITEM_In = 0, /**< Indicates that the item is an IN report type. */
|
||||||
|
HID_REPORT_ITEM_Out = 1, /**< Indicates that the item is an OUT report type. */
|
||||||
|
HID_REPORT_ITEM_Feature = 2, /**< Indicates that the item is a FEATURE report type. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Private Interface - For use in library only: */
|
||||||
|
#if !defined(__DOXYGEN__)
|
||||||
|
/* Type Defines: */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
HID_ReportItem_Attributes_t Attributes;
|
||||||
|
uint8_t ReportCount;
|
||||||
|
uint8_t ReportID;
|
||||||
|
} HID_StateTable_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Disable C linkage for C++ Compilers: */
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
||||||
Submodule lib/tinyusb updated: 938cae818f...290f4bea91
1
lib/tusb_gamepad
Submodule
1
lib/tusb_gamepad
Submodule
Submodule lib/tusb_gamepad added at 5e5ee00ee8
1
lib/tusb_xinput
Submodule
1
lib/tusb_xinput
Submodule
Submodule lib/tusb_xinput added at b34398847a
@@ -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})
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +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_dualshock4(const sony_ds4_report_t* ds4_data);
|
|
||||||
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_
|
|
||||||
@@ -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)
|
|
||||||
};
|
|
||||||
@@ -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
|
|
||||||
};
|
|
||||||
@@ -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
|
|
||||||
};
|
|
||||||
@@ -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];
|
|
||||||
@@ -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)
|
|
||||||
};
|
|
||||||
@@ -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;
|
|
||||||
@@ -1,22 +1,128 @@
|
|||||||
#include <pico/stdlib.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <stdlib.h>
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "pico/multicore.h"
|
||||||
|
#include "hardware/flash.h"
|
||||||
|
|
||||||
|
#include "tusb.h"
|
||||||
|
|
||||||
#include "input_mode.h"
|
#include "input_mode.h"
|
||||||
|
|
||||||
|
#define AIRCR_REG (*((volatile uint32_t *)(0xE000ED0C))) // Address of the AIRCR register
|
||||||
|
#define AIRCR_SYSRESETREQ (1 << 2) // Position of SYSRESETREQ bit in AIRCR
|
||||||
|
#define AIRCR_VECTKEY (0x5FA << 16) // VECTKEY value
|
||||||
|
|
||||||
|
#define FLASH_TARGET_OFFSET (256 * 1024)
|
||||||
|
#define FLASH_SIZE_BYTES (2 * 1024 * 1024)
|
||||||
|
|
||||||
|
InputMode current_input_mode;
|
||||||
|
|
||||||
|
void system_reset()
|
||||||
|
{
|
||||||
|
AIRCR_REG = AIRCR_VECTKEY | AIRCR_SYSRESETREQ;
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool store_input_mode(enum InputMode new_mode)
|
||||||
|
{
|
||||||
|
int buf[FLASH_PAGE_SIZE/sizeof(int)];
|
||||||
|
memset(buf, 0xFF, FLASH_PAGE_SIZE);
|
||||||
|
int saved_mode = new_mode; // changed to uint8?
|
||||||
|
|
||||||
|
buf[0] = saved_mode;
|
||||||
|
|
||||||
|
uint32_t saved_interrupts = save_and_disable_interrupts();
|
||||||
|
|
||||||
|
flash_range_erase((FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE), FLASH_SECTOR_SIZE);
|
||||||
|
flash_range_program((FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE), (uint8_t *)buf, FLASH_PAGE_SIZE);
|
||||||
|
|
||||||
|
restore_interrupts(saved_interrupts);
|
||||||
|
|
||||||
|
const uint8_t *flash_target_contents = (const uint8_t *)(XIP_BASE + FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE);
|
||||||
|
|
||||||
|
if ((uint8_t)saved_mode != *flash_target_contents)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (buttons.left)
|
||||||
|
{
|
||||||
|
new_mode = INPUT_MODE_DINPUT;
|
||||||
|
}
|
||||||
|
else if (buttons.right)
|
||||||
|
{
|
||||||
|
new_mode = INPUT_MODE_XBOXORIGINAL;
|
||||||
|
}
|
||||||
|
else if (buttons.down)
|
||||||
|
{
|
||||||
|
new_mode = INPUT_MODE_SWITCH;
|
||||||
|
}
|
||||||
|
else if (buttons.a)
|
||||||
|
{
|
||||||
|
new_mode = INPUT_MODE_PSCLASSIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mode_stored = false;
|
||||||
|
|
||||||
|
if (new_mode)
|
||||||
|
{
|
||||||
|
tud_disconnect();
|
||||||
|
sleep_ms(300);
|
||||||
|
multicore_reset_core1(); // stop tusb host
|
||||||
|
|
||||||
|
if (store_input_mode(new_mode))
|
||||||
|
{
|
||||||
|
system_reset(); // reset rp2040
|
||||||
|
mode_stored = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mode_stored;
|
||||||
|
}
|
||||||
|
|
||||||
enum InputMode get_input_mode()
|
enum InputMode get_input_mode()
|
||||||
{
|
{
|
||||||
#ifdef HOST_ORIGINAL_XBOX
|
#if (CDC_DEBUG > 0)
|
||||||
return INPUT_MODE_XBOXORIGINAL;
|
|
||||||
#elif HOST_NINTENDO_SWITCH
|
|
||||||
return INPUT_MODE_SWITCH;
|
|
||||||
#elif HOST_PLAYSTATION_CLASSIC
|
|
||||||
return INPUT_MODE_PSCLASSIC;
|
|
||||||
#elif HOST_PLAYSTATION_3
|
|
||||||
return INPUT_MODE_HID;
|
|
||||||
#elif HOST_DEBUG
|
|
||||||
return INPUT_MODE_USBSERIAL;
|
return INPUT_MODE_USBSERIAL;
|
||||||
#else
|
|
||||||
return INPUT_MODE_XINPUT;
|
|
||||||
#endif
|
#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)
|
||||||
|
{
|
||||||
|
current_input_mode = (enum InputMode)*stored_value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current_input_mode = INPUT_MODE_XBOXORIGINAL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return current_input_mode;
|
||||||
}
|
}
|
||||||
@@ -5,19 +5,10 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum InputMode
|
#include "tusb_gamepad.h"
|
||||||
{
|
|
||||||
INPUT_MODE_XINPUT,
|
|
||||||
INPUT_MODE_SWITCH,
|
|
||||||
INPUT_MODE_HID,
|
|
||||||
// INPUT_MODE_KEYBOARD,
|
|
||||||
INPUT_MODE_PSCLASSIC,
|
|
||||||
INPUT_MODE_XBOXORIGINAL,
|
|
||||||
INPUT_MODE_USBSERIAL,
|
|
||||||
// INPUT_MODE_CONFIG,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum InputMode get_input_mode();
|
enum InputMode get_input_mode();
|
||||||
|
bool change_input_mode(GamepadButtons buttons);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
46
src/main.cpp
46
src/main.cpp
@@ -1,24 +1,18 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "pico/stdlib.h"
|
#include "pico/stdlib.h"
|
||||||
#include "pico/multicore.h"
|
#include "pico/multicore.h"
|
||||||
|
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
#include "bsp/board_api.h"
|
#include "bsp/board_api.h"
|
||||||
|
#include "tusb_gamepad.h"
|
||||||
|
#include "drivermanager.h"
|
||||||
|
#include "drivers/gpdriver.h"
|
||||||
|
|
||||||
#include "usbh/tusb_host.h"
|
#include "usbh/tusb_host.h"
|
||||||
|
|
||||||
#include "usbd/drivermanager.h"
|
|
||||||
#include "usbd/gpdriver.h"
|
|
||||||
|
|
||||||
#include "Gamepad.h"
|
|
||||||
#include "input_mode.h"
|
#include "input_mode.h"
|
||||||
|
|
||||||
Gamepad gamepad;
|
|
||||||
GamepadOut gamepadOut;
|
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
set_sys_clock_khz(120000, true);
|
set_sys_clock_khz(120000, true);
|
||||||
@@ -34,11 +28,39 @@ int main(void)
|
|||||||
multicore_reset_core1();
|
multicore_reset_core1();
|
||||||
multicore_launch_core1(usbh_main);
|
multicore_launch_core1(usbh_main);
|
||||||
|
|
||||||
|
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)
|
while (1)
|
||||||
{
|
{
|
||||||
uint8_t outBuffer[64];
|
for (int i = 0; i < MAX_GAMEPADS; i++)
|
||||||
GPDriver* driver = driverManager.getDriver();
|
{
|
||||||
driver->process(&gamepad, outBuffer);
|
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
|
||||||
|
if (memcmp(&gamepad(0)->buttons, &prev_gamepad_buttons, sizeof(GamepadButtons)) != 0)
|
||||||
|
{
|
||||||
|
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(prev_gamepad_buttons))
|
||||||
|
{
|
||||||
|
last_time_gamepad_changed = get_absolute_time();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last_time_gamepad_checked = get_absolute_time();
|
||||||
|
}
|
||||||
|
|
||||||
sleep_ms(1);
|
sleep_ms(1);
|
||||||
tud_task();
|
tud_task();
|
||||||
|
|||||||
39
src/ogxm_config.h
Normal file
39
src/ogxm_config.h
Normal 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_
|
||||||
@@ -26,6 +26,10 @@
|
|||||||
#ifndef _TUSB_CONFIG_H_
|
#ifndef _TUSB_CONFIG_H_
|
||||||
#define _TUSB_CONFIG_H_
|
#define _TUSB_CONFIG_H_
|
||||||
|
|
||||||
|
#include "board_config.h"
|
||||||
|
|
||||||
|
#define MAX_GAMEPADS 1
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@@ -87,7 +91,7 @@
|
|||||||
#define CFG_TUD_ENABLED 1
|
#define CFG_TUD_ENABLED 1
|
||||||
|
|
||||||
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
|
// 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.
|
/* 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
|
* Tinyusb use follows macros to declare transferring memory so that they can be put
|
||||||
@@ -126,7 +130,7 @@
|
|||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define CFG_TUD_CDC_RX_BUFSIZE 256
|
#define CFG_TUD_CDC_RX_BUFSIZE 256
|
||||||
@@ -135,7 +139,7 @@
|
|||||||
//------------- CLASS -------------//
|
//------------- CLASS -------------//
|
||||||
#define CFG_TUD_CDC 1
|
#define CFG_TUD_CDC 1
|
||||||
#define CFG_TUD_ECM_RNDIS 0
|
#define CFG_TUD_ECM_RNDIS 0
|
||||||
#define CFG_TUD_HID 2
|
#define CFG_TUD_HID (MAX_GAMEPADS + 1)
|
||||||
|
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
// HOST CONFIGURATION
|
// HOST CONFIGURATION
|
||||||
@@ -150,12 +154,12 @@
|
|||||||
// Size of buffer to hold descriptors and other data used for enumeration
|
// Size of buffer to hold descriptors and other data used for enumeration
|
||||||
#define CFG_TUH_ENUMERATION_BUFSIZE 512
|
#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_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_MSC 0
|
||||||
#define CFG_TUH_VENDOR 0
|
#define CFG_TUH_VENDOR 0
|
||||||
#define CFG_TUH_XINPUT 1
|
#define CFG_TUH_XINPUT MAX_GAMEPADS
|
||||||
|
|
||||||
// max device support (excluding hub device)
|
// max device support (excluding hub device)
|
||||||
#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports
|
#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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_
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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_
|
|
||||||
@@ -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_
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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_
|
|
||||||
@@ -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
|
|
||||||
@@ -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_
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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_
|
|
||||||
@@ -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.
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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_
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#include "usbd/xboxog/xid/xid.h"
|
|
||||||
|
|
||||||
uint8_t *xremote_get_rom()
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
@@ -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_
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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
23
src/usbh/GPHostDriver.h
Normal 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
113
src/usbh/n64usb/N64USB.cpp
Normal 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;
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef _N64USB_H_
|
#ifndef _N64USB_H_
|
||||||
#define _N64USB_H_
|
#define _N64USB_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "usbh/tusb_hid/shared.h"
|
#include "usbh/GPHostDriver.h"
|
||||||
|
|
||||||
const usb_vid_pid_t n64_devices[] =
|
const usb_vid_pid_t n64_devices[] =
|
||||||
{
|
{
|
||||||
@@ -46,21 +44,24 @@ typedef struct __attribute__((packed))
|
|||||||
uint16_t buttons;
|
uint16_t buttons;
|
||||||
} N64USBReport;
|
} N64USBReport;
|
||||||
|
|
||||||
struct N64USBState
|
struct N64USBState
|
||||||
{
|
{
|
||||||
uint8_t dev_addr = {0};
|
uint8_t player_id = {0};
|
||||||
uint8_t instance = {0};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class N64USB
|
class N64USB: public GPHostDriver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void init(uint8_t dev_addr, uint8_t instance);
|
~N64USB() override {}
|
||||||
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:
|
private:
|
||||||
N64USBState n64usb;
|
N64USBState n64usb;
|
||||||
void update_gamepad(const N64USBReport* n64_data);
|
void update_gamepad(Gamepad* gp, const N64USBReport* n64_data);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _N64USB_H_
|
#endif // _N64USB_H_
|
||||||
146
src/usbh/ps3/DInput.cpp
Normal file
146
src/usbh/ps3/DInput.cpp
Normal 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
38
src/usbh/ps3/DInput.h
Normal 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
340
src/usbh/ps3/Dualshock3.cpp
Normal 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
39
src/usbh/ps3/Dualshock3.h
Normal 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
152
src/usbh/ps4/Dualshock4.cpp
Normal 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));
|
||||||
|
}
|
||||||
@@ -1,19 +1,16 @@
|
|||||||
#pragma once
|
#ifndef _DUALSHOCK4_H_
|
||||||
|
#define _DUALSHOCK4_H_
|
||||||
#ifndef _PS4_H_
|
|
||||||
#define _PS4_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "usbh/tusb_hid/shared.h"
|
#include "usbh/GPHostDriver.h"
|
||||||
|
|
||||||
#define REPORT_ID_GAMEPAD_STATE 0x11
|
|
||||||
|
|
||||||
const usb_vid_pid_t ps4_devices[] =
|
const usb_vid_pid_t ps4_devices[] =
|
||||||
{
|
{
|
||||||
{0x054C, 0x05C4}, // DS4
|
{0x054C, 0x05C4}, // DS4
|
||||||
{0x054C, 0x09CC}, // DS4
|
{0x054C, 0x09CC}, // DS4
|
||||||
{0x054C, 0x0BA0}, // DS4 wireless adapter
|
{0x054C, 0x0BA0}, // DS4 wireless adapter
|
||||||
|
{0x2563, 0x0357}, // MPOW Wired Gamepad (ShenZhen ShanWan)
|
||||||
{0x0F0D, 0x005E}, // Hori FC4
|
{0x0F0D, 0x005E}, // Hori FC4
|
||||||
{0x0F0D, 0x00EE}, // Hori PS4 Mini (PS4-099U)
|
{0x0F0D, 0x00EE}, // Hori PS4 Mini (PS4-099U)
|
||||||
{0x1F4F, 0x1002} // ASW GG Xrd controller
|
{0x1F4F, 0x1002} // ASW GG Xrd controller
|
||||||
@@ -27,7 +24,7 @@ static const uint8_t led_colors[][3] =
|
|||||||
{ 0x20, 0x00, 0x20 }, // Pink
|
{ 0x20, 0x00, 0x20 }, // Pink
|
||||||
};
|
};
|
||||||
|
|
||||||
enum dualshock4_dpad_mask
|
enum Dualshock4DpadMask
|
||||||
{
|
{
|
||||||
PS4_DPAD_MASK_UP = 0x00,
|
PS4_DPAD_MASK_UP = 0x00,
|
||||||
PS4_DPAD_MASK_UP_RIGHT = 0x01,
|
PS4_DPAD_MASK_UP_RIGHT = 0x01,
|
||||||
@@ -119,21 +116,24 @@ typedef struct __attribute__((packed))
|
|||||||
|
|
||||||
struct Dualshock4State
|
struct Dualshock4State
|
||||||
{
|
{
|
||||||
uint8_t dev_addr = {0};
|
uint8_t player_id = {0};
|
||||||
uint8_t instance = {0};
|
|
||||||
bool leds_set = {false};
|
bool leds_set = {false};
|
||||||
};
|
};
|
||||||
|
|
||||||
class Dualshock4
|
class Dualshock4 : public GPHostDriver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void init(uint8_t dev_addr, uint8_t instance);
|
~Dualshock4() override {}
|
||||||
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:
|
private:
|
||||||
Dualshock4State dualshock4;
|
Dualshock4State dualshock4;
|
||||||
void update_gamepad(const Dualshock4Report* ds4_data);
|
void update_gamepad(Gamepad* gp, const Dualshock4Report* ds4_data);
|
||||||
bool set_leds();
|
bool set_leds(uint8_t dev_addr, uint8_t instance);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _PS4_H_
|
#endif
|
||||||
115
src/usbh/ps5/Dualsense.cpp
Normal file
115
src/usbh/ps5/Dualsense.cpp
Normal 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));
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
#pragma once
|
#ifndef _DUALSENSE_H_
|
||||||
|
#define _DUALSENSE_H_
|
||||||
#ifndef _PS5_H_
|
|
||||||
#define _PS5_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "usbh/tusb_hid/shared.h"
|
#include "usbh/GPHostDriver.h"
|
||||||
|
|
||||||
const usb_vid_pid_t ps5_devices[] =
|
const usb_vid_pid_t ps5_devices[] =
|
||||||
{
|
{
|
||||||
@@ -131,21 +129,22 @@ struct DualsenseOutReport {
|
|||||||
uint8_t lightbar_blue;
|
uint8_t lightbar_blue;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct DualsenseState
|
struct DualsenseState
|
||||||
{
|
{
|
||||||
uint8_t dev_addr = {0};
|
uint8_t player_id = {0};
|
||||||
uint8_t instance = {0};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Dualsense
|
class Dualsense : public GPHostDriver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void init(uint8_t dev_addr, uint8_t instance);
|
virtual void init(uint8_t player_id, uint8_t dev_addr, uint8_t instance);
|
||||||
void process_report(uint8_t const* report, uint16_t len);
|
virtual void process_hid_report(Gamepad* gamepad, uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
|
||||||
bool send_fb_data();
|
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:
|
private:
|
||||||
DualsenseState dualsense;
|
DualsenseState dualsense;
|
||||||
void update_gamepad(const DualsenseReport* ds_data);
|
void update_gamepad(Gamepad* gp, const DualsenseReport* ds_report);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _PS5_H_
|
#endif // _DUALSENSE_H_
|
||||||
107
src/usbh/psclassic/PSClassic.cpp
Normal file
107
src/usbh/psclassic/PSClassic.cpp
Normal 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;
|
||||||
|
}
|
||||||
34
src/usbh/psclassic/PSClassic.h
Normal file
34
src/usbh/psclassic/PSClassic.h
Normal 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_
|
||||||
13
src/usbh/shared/hid_class_driver.c
Normal file
13
src/usbh/shared/hid_class_driver.c
Normal 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
|
||||||
|
};
|
||||||
8
src/usbh/shared/hid_class_driver.h
Normal file
8
src/usbh/shared/hid_class_driver.h
Normal 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
|
||||||
@@ -1,26 +1,4 @@
|
|||||||
#include "utilities/scaling.h"
|
#include "usbh/shared/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);
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t scale_uint8_to_int16(uint8_t value, bool invert)
|
int16_t scale_uint8_to_int16(uint8_t value, bool invert)
|
||||||
{
|
{
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef _SCALING_H_
|
#ifndef _SCALING_H_
|
||||||
#define _SCALING_H_
|
#define _SCALING_H_
|
||||||
|
|
||||||
#include <stdint.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);
|
int16_t scale_uint8_to_int16(uint8_t value, bool invert);
|
||||||
|
|
||||||
#endif // _SCALING_H_
|
#endif // _SCALING_H_
|
||||||
260
src/usbh/switch/SwitchPro.cpp
Normal file
260
src/usbh/switch/SwitchPro.cpp
Normal 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);
|
||||||
|
}
|
||||||
46
src/usbh/switch/SwitchPro.h
Normal file
46
src/usbh/switch/SwitchPro.h
Normal 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_
|
||||||
121
src/usbh/switch/SwitchWired.cpp
Normal file
121
src/usbh/switch/SwitchWired.cpp
Normal 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
|
||||||
|
}
|
||||||
67
src/usbh/switch/SwitchWired.h
Normal file
67
src/usbh/switch/SwitchWired.h
Normal 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_
|
||||||
@@ -1,208 +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 = false;
|
|
||||||
static uint8_t gamepad_dev_addr = 0;
|
|
||||||
static uint8_t gamepad_instance = 0;
|
|
||||||
|
|
||||||
usbh_class_driver_t const usbh_hid_driver =
|
|
||||||
{
|
|
||||||
.init = hidh_init,
|
|
||||||
.open = hidh_open,
|
|
||||||
.set_config = hidh_set_config,
|
|
||||||
.xfer_cb = hidh_xfer_cb,
|
|
||||||
.close = hidh_close
|
|
||||||
};
|
|
||||||
|
|
||||||
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
|
|
||||||
{
|
|
||||||
(void)desc_report;
|
|
||||||
(void)desc_len;
|
|
||||||
|
|
||||||
if (!gamepad_mounted)
|
|
||||||
{
|
|
||||||
gamepad_dev_addr = dev_addr;
|
|
||||||
gamepad_instance = instance;
|
|
||||||
gamepad_mounted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (host_mode == HOST_MODE_HID_SWITCH_PRO && !switch_pro)
|
|
||||||
{
|
|
||||||
switch_pro = new SwitchPro();
|
|
||||||
switch_pro->init(dev_addr, instance);
|
|
||||||
}
|
|
||||||
else if (host_mode == HOST_MODE_HID_SWITCH_WIRED && !switch_wired)
|
|
||||||
{
|
|
||||||
switch_wired = new SwitchWired();
|
|
||||||
switch_wired->init(dev_addr, instance);
|
|
||||||
}
|
|
||||||
else if (host_mode == HOST_MODE_HID_PS3 && !dualshock3)
|
|
||||||
{
|
|
||||||
dualshock3 = new Dualshock3();
|
|
||||||
dualshock3->init(dev_addr, instance);
|
|
||||||
}
|
|
||||||
else if (host_mode == HOST_MODE_HID_PS4 && !dualshock4)
|
|
||||||
{
|
|
||||||
dualshock4 = new Dualshock4();
|
|
||||||
dualshock4->init(dev_addr, instance);
|
|
||||||
}
|
|
||||||
else if (host_mode == HOST_MODE_HID_PS5 && !dualsense)
|
|
||||||
{
|
|
||||||
dualsense = new Dualsense();
|
|
||||||
dualsense->init(dev_addr, instance);
|
|
||||||
}
|
|
||||||
else if (host_mode == HOST_MODE_HID_PSCLASSIC && !psclassic)
|
|
||||||
{
|
|
||||||
psclassic = new PSClassic();
|
|
||||||
psclassic->init(dev_addr, instance);
|
|
||||||
}
|
|
||||||
else if (host_mode == HOST_MODE_HID_N64USB && !n64usb)
|
|
||||||
{
|
|
||||||
n64usb = new N64USB();
|
|
||||||
n64usb->init(dev_addr, instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// send rumble data
|
|
||||||
bool send_fb_data_to_hid_gamepad()
|
|
||||||
{
|
|
||||||
if (!gamepad_mounted)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rumble_sent = false;
|
|
||||||
|
|
||||||
if (tuh_hid_send_ready)
|
|
||||||
{
|
|
||||||
switch(host_mode)
|
|
||||||
{
|
|
||||||
case HOST_MODE_HID_PSCLASSIC:
|
|
||||||
rumble_sent = psclassic->send_fb_data();
|
|
||||||
break;
|
|
||||||
case HOST_MODE_HID_PS3:
|
|
||||||
rumble_sent = dualshock3->send_fb_data();
|
|
||||||
break;
|
|
||||||
case HOST_MODE_HID_PS4:
|
|
||||||
rumble_sent = dualshock4->send_fb_data();
|
|
||||||
break;
|
|
||||||
case HOST_MODE_HID_PS5:
|
|
||||||
rumble_sent = dualsense->send_fb_data();
|
|
||||||
break;
|
|
||||||
case HOST_MODE_HID_SWITCH_PRO:
|
|
||||||
rumble_sent = switch_pro->send_fb_data();
|
|
||||||
break;
|
|
||||||
case HOST_MODE_HID_SWITCH_WIRED:
|
|
||||||
rumble_sent = switch_wired->send_fb_data();
|
|
||||||
break;
|
|
||||||
case HOST_MODE_HID_N64USB:
|
|
||||||
rumble_sent = n64usb->send_fb_data();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rumble_sent;
|
|
||||||
}
|
|
||||||
@@ -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_
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "pico/stdlib.h"
|
|
||||||
|
|
||||||
#include "tusb.h"
|
|
||||||
#include "class/hid/hid_host.h"
|
|
||||||
|
|
||||||
#include "utilities/scaling.h"
|
|
||||||
|
|
||||||
#include "usbh/tusb_hid/ps3.h"
|
|
||||||
|
|
||||||
#include "Gamepad.h"
|
|
||||||
|
|
||||||
#include "utilities/log.h"
|
|
||||||
|
|
||||||
/* ---------------------------- */
|
|
||||||
/* this does not work currently */
|
|
||||||
/* ---------------------------- */
|
|
||||||
|
|
||||||
void Dualshock3::init(uint8_t dev_addr, uint8_t instance)
|
|
||||||
{
|
|
||||||
dualshock3.dev_addr = dev_addr;
|
|
||||||
dualshock3.instance = instance;
|
|
||||||
|
|
||||||
// tuh_hid_set_protocol(dualshock3.dev_addr, dualshock3.instance, HID_PROTOCOL_REPORT);
|
|
||||||
// sleep_ms(200);
|
|
||||||
if (enable_reports())
|
|
||||||
{
|
|
||||||
log("reports enabled, addr: %02X inst: %02X", dev_addr, instance);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log("reports enable failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Dualshock3::enable_reports()
|
|
||||||
{
|
|
||||||
// uint8_t cmd_buf[4];
|
|
||||||
// cmd_buf[0] = 0x42;
|
|
||||||
// cmd_buf[1] = 0x0c;
|
|
||||||
// cmd_buf[2] = 0x00;
|
|
||||||
// cmd_buf[3] = 0x00;
|
|
||||||
|
|
||||||
// // if (tuh_hid_send_ready)
|
|
||||||
// // {
|
|
||||||
// dualshock3.report_enabled = tuh_hid_set_report(dualshock3.dev_addr, dualshock3.instance, 0xF4, HID_REPORT_TYPE_FEATURE, &cmd_buf, sizeof(cmd_buf));
|
|
||||||
// // }
|
|
||||||
|
|
||||||
static uint8_t buffer[5] = {};
|
|
||||||
buffer[0] = 0xF4;
|
|
||||||
buffer[1] = 0x42;
|
|
||||||
buffer[2] = 0x03;
|
|
||||||
buffer[3] = 0x00;
|
|
||||||
buffer[4] = 0x00;
|
|
||||||
|
|
||||||
dualshock3.report_enabled = tuh_hid_set_report(dualshock3.dev_addr, dualshock3.instance, 0, HID_REPORT_TYPE_FEATURE, buffer, sizeof(buffer));
|
|
||||||
|
|
||||||
tuh_hid_send_report(dualshock3.dev_addr, dualshock3.instance, 0, buffer, sizeof(buffer));
|
|
||||||
|
|
||||||
if (dualshock3.report_enabled)
|
|
||||||
{
|
|
||||||
tuh_hid_receive_report(dualshock3.dev_addr, dualshock3.instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dualshock3.report_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
// void Dualshock3::reset_state()
|
|
||||||
// {
|
|
||||||
// dualshock3.report_enabled = false;
|
|
||||||
// dualshock3.dev_addr = 0;
|
|
||||||
// dualshock3.instance = 0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
void Dualshock3::update_gamepad(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->leftX, false);
|
|
||||||
gamepad.state.ly = scale_uint8_to_int16(ds3_data->leftY, true);
|
|
||||||
gamepad.state.rx = scale_uint8_to_int16(ds3_data->rightX, false);
|
|
||||||
gamepad.state.ry = scale_uint8_to_int16(ds3_data->rightY, 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);
|
|
||||||
|
|
||||||
static DualShock3Report prev_report = { 0 };
|
|
||||||
|
|
||||||
DualShock3Report ds3_report;
|
|
||||||
memcpy(&ds3_report, report, sizeof(ds3_report));
|
|
||||||
|
|
||||||
// if (memcmp(&ds3_report, &prev_report, sizeof(ds3_report)) != 0)
|
|
||||||
// {
|
|
||||||
update_gamepad(&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 default_output_report;
|
|
||||||
|
|
||||||
memcpy(&default_output_report, default_report, sizeof(default_output_report));
|
|
||||||
|
|
||||||
default_output_report.leds_bitmap |= 0x1 << (dualshock3.instance+1);
|
|
||||||
|
|
||||||
bool rumble_sent = tuh_hid_send_report(dualshock3.dev_addr, dualshock3.instance, 0x1, &default_output_report, sizeof(default_output_report));
|
|
||||||
|
|
||||||
return rumble_sent;
|
|
||||||
// return true;
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef _PS3_H_
|
|
||||||
#define _PS3_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "usbh/tusb_hid/shared.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 DualShock3Report
|
|
||||||
{
|
|
||||||
uint8_t reportId;
|
|
||||||
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 unk1;
|
|
||||||
|
|
||||||
uint8_t leftX;
|
|
||||||
uint8_t leftY;
|
|
||||||
uint8_t rightX;
|
|
||||||
uint8_t rightY;
|
|
||||||
|
|
||||||
uint8_t unk2[31];
|
|
||||||
|
|
||||||
int16_t accelerX;
|
|
||||||
int16_t accelerY;
|
|
||||||
int16_t accelerZ;
|
|
||||||
|
|
||||||
int16_t velocityZ;
|
|
||||||
}
|
|
||||||
__attribute__((packed));
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
struct Dualshock3State
|
|
||||||
{
|
|
||||||
bool report_enabled {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);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // _PS3_H_
|
|
||||||
@@ -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));
|
|
||||||
}
|
|
||||||
@@ -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));
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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_
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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_
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
Reference in New Issue
Block a user