v1.0.0-alpha
This commit is contained in:
212
.clang-format
212
.clang-format
@@ -1,212 +0,0 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Google
|
||||
AccessModifierOffset: -1
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveMacros: true
|
||||
AlignConsecutiveAssignments: true
|
||||
AlignConsecutiveBitFields: true
|
||||
AlignConsecutiveDeclarations: true
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: true
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 160
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: true
|
||||
DisableFormat: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IfMacros:
|
||||
- KJ_IF_MAYBE
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^<ext/.*\.h>'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^<.*'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseLabels: true
|
||||
IndentCaseBlocks: true
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentRequires: false
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertTrailingCommas: None
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
LambdaBodyIndentation: Signature
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Never
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PointerAlignment: Left
|
||||
PPIndentWidth: -1
|
||||
RawStringFormats:
|
||||
- Language: Cpp
|
||||
Delimiters:
|
||||
- cc
|
||||
- CC
|
||||
- cpp
|
||||
- Cpp
|
||||
- CPP
|
||||
- 'c++'
|
||||
- 'C++'
|
||||
CanonicalDelimiter: ''
|
||||
BasedOnStyle: google
|
||||
- Language: TextProto
|
||||
Delimiters:
|
||||
- pb
|
||||
- PB
|
||||
- proto
|
||||
- PROTO
|
||||
EnclosingFunctions:
|
||||
- EqualsProto
|
||||
- EquivToProto
|
||||
- PARSE_PARTIAL_TEXT_PROTO
|
||||
- PARSE_TEST_PROTO
|
||||
- PARSE_TEXT_PROTO
|
||||
- ParseTextOrDie
|
||||
- ParseTextProtoOrDie
|
||||
- ParseTestProto
|
||||
- ParsePartialTestProto
|
||||
CanonicalDelimiter: pb
|
||||
BasedOnStyle: google
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: true
|
||||
ShortNamespaceLines: 1
|
||||
SortIncludes: CaseSensitive
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: Never
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeSquareBrackets: false
|
||||
BitFieldColonSpacing: Both
|
||||
Standard: Auto
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 8
|
||||
UseCRLF: false
|
||||
UseTab: Never
|
||||
WhitespaceSensitiveMacros:
|
||||
- STRINGIZE
|
||||
- PP_STRINGIZE
|
||||
- BOOST_PP_STRINGIZE
|
||||
- NS_SWIFT_NAME
|
||||
- CF_SWIFT_NAME
|
||||
...
|
||||
|
||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,6 +1,10 @@
|
||||
build
|
||||
.vscode
|
||||
release
|
||||
generated
|
||||
tools
|
||||
.ignore
|
||||
Firmware/RP2040/build
|
||||
Firmware/RP2040/.ignore
|
||||
Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid_xremote_rom.h
|
||||
|
||||
Firmware/ESP32/.ignore
|
||||
Firmware/ESP32/build
|
||||
Firmware/ESP32/components/btstack
|
||||
Firmware/ESP32/sdkconfig.old
|
||||
|
||||
Tools/dvd-dongle-rom.bin
|
||||
22
.gitmodules
vendored
22
.gitmodules
vendored
@@ -1,12 +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
|
||||
[submodule "Firmware/external/bluepad32"]
|
||||
path = Firmware/external/bluepad32
|
||||
url = https://github.com/ricardoquesada/bluepad32.git
|
||||
[submodule "Firmware/external/tinyusb"]
|
||||
path = Firmware/external/tinyusb
|
||||
url = https://github.com/hathach/tinyusb.git
|
||||
[submodule "src/usbd"]
|
||||
path = src/usbd
|
||||
url = https://github.com/wiredopposite/usbd.git
|
||||
[submodule "lib/tusb_xinput"]
|
||||
path = lib/tusb_xinput
|
||||
url = https://github.com/Ryzee119/tusb_xinput.git
|
||||
[submodule "Firmware/external/Pico-PIO-USB"]
|
||||
path = Firmware/external/Pico-PIO-USB
|
||||
url = https://github.com/sekigon-gonnoc/Pico-PIO-USB.git
|
||||
[submodule "Tools/dump-dvd-kit"]
|
||||
path = Tools/dump-dvd-kit
|
||||
url = https://github.com/XboxDev/dump-dvd-kit.git
|
||||
|
||||
107
CMakeLists.txt
107
CMakeLists.txt
@@ -1,107 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
message("Build type: \"${CMAKE_BUILD_TYPE}\"")
|
||||
|
||||
# Project name
|
||||
set(NAME OGX-Mini)
|
||||
|
||||
# Board type
|
||||
set(PICO_BOARD none)
|
||||
|
||||
# Fixes that allow some MCH2022 badges with a slowly starting oscillator to boot properly
|
||||
add_compile_definitions(PICO_BOOT_STAGE2_CHOOSE_GENERIC_03H=1 PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64)
|
||||
|
||||
# SDK
|
||||
include($ENV{PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||
|
||||
project(${NAME} C CXX ASM)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if (PICO_SDK_VERSION_STRING VERSION_LESS "1.3.0")
|
||||
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.3.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
|
||||
endif()
|
||||
|
||||
set(PICO_PIO_USB_PATH "${CMAKE_CURRENT_LIST_DIR}/lib/Pico-PIO-USB")
|
||||
set(PICO_TINYUSB_PATH "${CMAKE_CURRENT_LIST_DIR}/lib/tinyusb")
|
||||
set(TUSB_XINPUT_PATH "${CMAKE_CURRENT_LIST_DIR}/lib/tusb_xinput")
|
||||
add_subdirectory(${TUSB_XINPUT_PATH} xinput_host)
|
||||
|
||||
pico_sdk_init()
|
||||
|
||||
add_subdirectory(lib/Pico-PIO-USB)
|
||||
add_subdirectory(lib)
|
||||
|
||||
file(GLOB_RECURSE SOURCES
|
||||
"src/main.cpp"
|
||||
"src/input_mode.cpp"
|
||||
"src/Gamepad.cpp"
|
||||
|
||||
"src/utilities/log.cpp"
|
||||
|
||||
"src/usbh/tusb_host_manager.cpp"
|
||||
"src/usbh/tusb_host.cpp"
|
||||
"src/usbh/n64usb/N64USB.cpp"
|
||||
"src/usbh/ps3/Dualshock3.cpp"
|
||||
"src/usbh/ps3/DInput.cpp"
|
||||
"src/usbh/ps4/Dualshock4.cpp"
|
||||
"src/usbh/ps5/Dualsense.cpp"
|
||||
"src/usbh/psclassic/PSClassic.cpp"
|
||||
"src/usbh/switch/SwitchPro.cpp"
|
||||
"src/usbh/switch/SwitchWired.cpp"
|
||||
"src/usbh/xinput/XInput.cpp"
|
||||
"src/usbh/shared/hid_class_driver.c"
|
||||
"src/usbh/shared/scaling.cpp"
|
||||
|
||||
"src/usbd/usbdriver.cpp"
|
||||
"src/usbd/drivermanager.cpp"
|
||||
"src/usbd/drivers/shared/driverhelper.h"
|
||||
"src/usbd/drivers/shared/scaling.cpp"
|
||||
"src/usbd/drivers/dinput/DInputDriver.cpp"
|
||||
# "src/usbd/drivers/hid/HIDDriver.cpp"
|
||||
# "src/usbd/drivers/ps3/PS3Driver.cpp"
|
||||
"src/usbd/drivers/psclassic/PSClassicDriver.cpp"
|
||||
"src/usbd/drivers/switch/SwitchDriver.cpp"
|
||||
"src/usbd/drivers/usbserial/USBSerialDriver.cpp"
|
||||
"src/usbd/drivers/xinput/XInputDriver.cpp"
|
||||
"src/usbd/drivers/xboxog/XboxOriginalDriver.cpp"
|
||||
"src/usbd/drivers/xboxog/xid/xid.c"
|
||||
"src/usbd/drivers/xboxog/xid/xid_driver.c"
|
||||
"src/usbd/drivers/xboxog/xid/xid_gamepad.c"
|
||||
"src/usbd/drivers/xboxog/xid/xid_remote.c"
|
||||
"src/usbd/drivers/xboxog/xid/xid_steelbattalion.c"
|
||||
)
|
||||
|
||||
# Firmware
|
||||
add_executable(${NAME}
|
||||
${SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(${NAME} PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/src ${CMAKE_CURRENT_LIST_DIR}/lib)
|
||||
|
||||
include_directories(
|
||||
lib/)
|
||||
|
||||
target_link_libraries(${NAME}
|
||||
pico_stdlib
|
||||
pico_unique_id
|
||||
pico_multicore
|
||||
hardware_watchdog
|
||||
hardware_flash
|
||||
hardware_sync
|
||||
hardware_uart
|
||||
hardware_pio
|
||||
hardware_pwm
|
||||
hardware_adc
|
||||
hardware_i2c
|
||||
tinyusb_device
|
||||
tinyusb_board
|
||||
tinyusb_host
|
||||
tinyusb_pico_pio_usb
|
||||
CRC32
|
||||
cmsis_core
|
||||
xinput_host
|
||||
)
|
||||
|
||||
pico_add_extra_outputs(${NAME})
|
||||
23
Firmware/ESP32/.vscode/c_cpp_properties.json
vendored
Normal file
23
Firmware/ESP32/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ESP-IDF",
|
||||
"compilerPath": "${default}",
|
||||
"compileCommands": "${config:idf.buildPath}/compile_commands.json",
|
||||
"includePath": [
|
||||
"${config:idf.espIdfPath}/components/**",
|
||||
"${config:idf.espIdfPathWin}/components/**",
|
||||
"${workspaceFolder}/main/**"
|
||||
],
|
||||
"browse": {
|
||||
"path": [
|
||||
"${config:idf.espIdfPath}/components",
|
||||
"${config:idf.espIdfPathWin}/components",
|
||||
"${workspaceFolder}/main"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
104
Firmware/ESP32/.vscode/settings.json
vendored
Normal file
104
Firmware/ESP32/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"idf.adapterTargetName": "esp32",
|
||||
"cmake.configureOnOpen": false,
|
||||
"files.associations": {
|
||||
"freertos.h": "c",
|
||||
"stdint.h": "c",
|
||||
"stdlib.h": "c",
|
||||
"atomic": "cpp",
|
||||
"array": "cpp",
|
||||
"task.h": "c",
|
||||
"bit": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"bitset": "cpp",
|
||||
"cctype": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"compare": "cpp",
|
||||
"complex": "cpp",
|
||||
"concepts": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"list": "cpp",
|
||||
"map": "cpp",
|
||||
"set": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"netfwd": "cpp",
|
||||
"numeric": "cpp",
|
||||
"random": "cpp",
|
||||
"ratio": "cpp",
|
||||
"regex": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"fstream": "cpp",
|
||||
"future": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"numbers": "cpp",
|
||||
"ostream": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"thread": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"c_wrapper.h": "c",
|
||||
"queue": "cpp",
|
||||
"charconv": "cpp",
|
||||
"format": "cpp",
|
||||
"forward_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"ios": "cpp",
|
||||
"locale": "cpp",
|
||||
"optional": "cpp",
|
||||
"ranges": "cpp",
|
||||
"span": "cpp",
|
||||
"stack": "cpp",
|
||||
"xfacet": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xiosbase": "cpp",
|
||||
"xlocale": "cpp",
|
||||
"xlocbuf": "cpp",
|
||||
"xlocinfo": "cpp",
|
||||
"xlocmes": "cpp",
|
||||
"xlocmon": "cpp",
|
||||
"xlocnum": "cpp",
|
||||
"xloctime": "cpp",
|
||||
"xmemory": "cpp",
|
||||
"xstring": "cpp",
|
||||
"xtr1common": "cpp",
|
||||
"xtree": "cpp",
|
||||
"xutility": "cpp"
|
||||
},
|
||||
"idf.portWin": "COM26",
|
||||
"idf.flashType": "UART",
|
||||
"C_Cpp.intelliSenseEngine": "default"
|
||||
}
|
||||
25
Firmware/ESP32/CMakeLists.txt
Normal file
25
Firmware/ESP32/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
set(EXTERNAL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../external)
|
||||
|
||||
include(${EXTERNAL_DIR}/patch_libs.cmake)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/integrate_btstack.cmake)
|
||||
|
||||
apply_lib_patches(${EXTERNAL_DIR})
|
||||
integrate_btstack(${EXTERNAL_DIR})
|
||||
|
||||
set(BLUEPAD32_ROOT ${EXTERNAL_DIR}/bluepad32/src/components/bluepad32)
|
||||
|
||||
if(NOT EXISTS ${BLUEPAD32_ROOT})
|
||||
message(FATAL_ERROR "External directory not found: ${BLUEPAD32_ROOT}")
|
||||
endif()
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
${BLUEPAD32_ROOT}/..
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/components
|
||||
)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(OGX-Mini-ESP32)
|
||||
50
Firmware/ESP32/cmake/integrate_btstack.cmake
Normal file
50
Firmware/ESP32/cmake/integrate_btstack.cmake
Normal file
@@ -0,0 +1,50 @@
|
||||
function(integrate_btstack EXTERNAL_DIR)
|
||||
if(NOT EXISTS ${EXTERNAL_DIR})
|
||||
message(FATAL_ERROR "External directory not found: ${EXTERNAL_DIR}")
|
||||
else()
|
||||
message(STATUS "External directory found: ${EXTERNAL_DIR}")
|
||||
endif()
|
||||
|
||||
set(PROJECT_COMPONENTS ${CMAKE_CURRENT_LIST_DIR}/components)
|
||||
set(BTSTACK_DIR ${EXTERNAL_DIR}/bluepad32/external/btstack)
|
||||
set(PROJECT_BTSTACK ${PROJECT_COMPONENTS}/btstack)
|
||||
|
||||
if (NOT EXISTS ${PROJECT_COMPONENTS})
|
||||
message(STATUS "Creating components folder at ${PROJECT_COMPONENTS}")
|
||||
file(MAKE_DIRECTORY ${PROJECT_COMPONENTS})
|
||||
endif()
|
||||
|
||||
if (NOT EXISTS ${BTSTACK_DIR})
|
||||
message(FATAL_ERROR "Error: No BTstack folder at ${BTSTACK_DIR}")
|
||||
endif()
|
||||
|
||||
if (NOT EXISTS ${PROJECT_BTSTACK})
|
||||
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
||||
|
||||
if(Python3_Interpreter_FOUND)
|
||||
message(STATUS "Python3 found: ${Python3_EXECUTABLE}")
|
||||
message(STATUS "Python3 version: ${Python3_VERSION}")
|
||||
else()
|
||||
message(FATAL_ERROR "Python3 not found! Python is required to integrate BTStack with ESP-IDF")
|
||||
endif()
|
||||
|
||||
set(BTSTACK_SCRIPT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/components/integrate_btstack.py)
|
||||
|
||||
message(STATUS "Integrating BTStack with project")
|
||||
execute_process(
|
||||
COMMAND ${Python3_EXECUTABLE} ${BTSTACK_SCRIPT_PATH} ${BTSTACK_DIR} ${PROJECT_COMPONENTS}
|
||||
WORKING_DIRECTORY ${BTSTACK_SCRIPT_PATH}/..
|
||||
RESULT_VARIABLE PYTHON_RESULT
|
||||
OUTPUT_VARIABLE PYTHON_OUTPUT
|
||||
ERROR_VARIABLE PYTHON_ERROR
|
||||
)
|
||||
|
||||
if(PYTHON_RESULT EQUAL 0)
|
||||
message(STATUS "BTStack integration successful:\n${PYTHON_OUTPUT}")
|
||||
else()
|
||||
message(FATAL_ERROR "BTStack integration failed:\n${PYTHON_ERROR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "BTstack setup complete")
|
||||
endfunction()
|
||||
77
Firmware/ESP32/components/integrate_btstack.py
Normal file
77
Firmware/ESP32/components/integrate_btstack.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# This script copies BTstack ESP32 port files to this project's components folder, ESP-IDF freaks out otherwise
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
def main(btstack_root, components_dir):
|
||||
PROJECT_COMPONENTS = os.path.abspath(components_dir)
|
||||
BTSTACK_DIR = os.path.abspath(btstack_root)
|
||||
BTSTACK_PORT_DIR = os.path.abspath(os.path.join(BTSTACK_DIR, 'port', 'esp32'))
|
||||
|
||||
if not os.path.exists(PROJECT_COMPONENTS):
|
||||
print(f"Error: No components folder at {PROJECT_COMPONENTS}")
|
||||
sys.exit(10)
|
||||
|
||||
if not os.path.exists(BTSTACK_DIR):
|
||||
print(f"Error: No BTstack folder at {BTSTACK_DIR}")
|
||||
sys.exit(10)
|
||||
|
||||
PROJECT_BTSTACK = os.path.join(PROJECT_COMPONENTS, "btstack")
|
||||
|
||||
if os.path.exists(PROJECT_BTSTACK):
|
||||
print("Deleting old BTstack component %s" % PROJECT_BTSTACK)
|
||||
shutil.rmtree(PROJECT_BTSTACK)
|
||||
|
||||
# create components/btstack
|
||||
print("Creating BTstack component at %s" % PROJECT_COMPONENTS)
|
||||
shutil.copytree(os.path.join(BTSTACK_PORT_DIR, 'components', 'btstack'), PROJECT_BTSTACK)
|
||||
|
||||
dirs_to_copy = [
|
||||
'src',
|
||||
'3rd-party/bluedroid',
|
||||
'3rd-party/hxcmod-player',
|
||||
'3rd-party/lwip/dhcp-server',
|
||||
'3rd-party/lc3-google',
|
||||
'3rd-party/md5',
|
||||
'3rd-party/micro-ecc',
|
||||
'3rd-party/yxml',
|
||||
'platform/freertos',
|
||||
'platform/lwip',
|
||||
'tool'
|
||||
]
|
||||
|
||||
for dir in dirs_to_copy:
|
||||
print('- %s' % dir)
|
||||
shutil.copytree(os.path.join(BTSTACK_PORT_DIR, '..', '..', dir), os.path.join(PROJECT_BTSTACK, dir))
|
||||
|
||||
# manually prepare platform/embedded
|
||||
print('- platform/embedded')
|
||||
platform_embedded_path = PROJECT_BTSTACK + '/platform/embedded'
|
||||
os.makedirs(platform_embedded_path)
|
||||
platform_embedded_files_to_copy = [
|
||||
'hal_time_ms.h',
|
||||
'hal_uart_dma.h',
|
||||
'hci_dump_embedded_stdout.h',
|
||||
'hci_dump_embedded_stdout.c',
|
||||
]
|
||||
for file in platform_embedded_files_to_copy:
|
||||
shutil.copy(os.path.join(BTSTACK_PORT_DIR, '..', '..', 'platform', 'embedded', file), platform_embedded_path)
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: script.py <path_to_btstack> <path_to_project_components>")
|
||||
sys.exit(1)
|
||||
|
||||
btstack_root = sys.argv[1]
|
||||
project_components = sys.argv[2]
|
||||
|
||||
if not os.path.exists(btstack_root):
|
||||
print(f"Error: Specified external directory does not exist: {btstack_root}")
|
||||
sys.exit(2)
|
||||
|
||||
if not os.path.exists(project_components):
|
||||
print(f"Error: Specified components directory does not exist: {project_components}")
|
||||
sys.exit(3)
|
||||
|
||||
main(btstack_root, project_components)
|
||||
10
Firmware/ESP32/dependencies.lock
Normal file
10
Firmware/ESP32/dependencies.lock
Normal file
@@ -0,0 +1,10 @@
|
||||
dependencies:
|
||||
idf:
|
||||
source:
|
||||
type: idf
|
||||
version: 5.1.5
|
||||
direct_dependencies:
|
||||
- idf
|
||||
manifest_hash: ed38c883314ee552977d073f5c9d0e2ab9acc3248eefbeb203f3439de3e31b6e
|
||||
target: esp32
|
||||
version: 2.0.0
|
||||
644
Firmware/ESP32/main/Bluepad32/Bluepad32.cpp
Normal file
644
Firmware/ESP32/main/Bluepad32/Bluepad32.cpp
Normal file
@@ -0,0 +1,644 @@
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "btstack_port_esp32.h"
|
||||
#include "btstack_run_loop.h"
|
||||
#include "btstack_stdio_esp32.h"
|
||||
#include "uni.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "Board/board_api.h"
|
||||
#include "I2CDriver/I2CDriver.h"
|
||||
#include "Bluepad32/Gamepad.h"
|
||||
#include "Bluepad32/Bluepad32.h"
|
||||
|
||||
namespace bluepad32 {
|
||||
|
||||
static constexpr uint32_t FEEDBACK_TIME_MS = 200;
|
||||
static constexpr uint32_t LED_TIME_MS = 500;
|
||||
|
||||
struct Device
|
||||
{
|
||||
std::atomic<bool> connected{false};
|
||||
std::atomic<ReportIn> report_in;
|
||||
std::atomic<ReportOut> report_out;
|
||||
};
|
||||
|
||||
std::array<Device, CONFIG_BLUEPAD32_MAX_DEVICES> devices_;
|
||||
|
||||
static inline void send_feedback_cb(btstack_timer_source *ts)
|
||||
{
|
||||
uni_hid_device_t* bp_device;
|
||||
|
||||
for (uint8_t i = 0; i < CONFIG_BLUEPAD32_MAX_DEVICES; ++i)
|
||||
{
|
||||
bp_device = uni_hid_device_get_instance_for_idx(i);
|
||||
if (!bp_device || !bp_device->report_parser.play_dual_rumble)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ReportOut report_out = devices_[i].report_out.load();
|
||||
bp_device->report_parser.play_dual_rumble(bp_device, 0, FEEDBACK_TIME_MS, report_out.rumble_l, report_out.rumble_r);
|
||||
}
|
||||
// btstack_run_loop_remove_timer(ts);
|
||||
btstack_run_loop_set_timer(ts, FEEDBACK_TIME_MS);
|
||||
btstack_run_loop_add_timer(ts);
|
||||
}
|
||||
|
||||
static inline void check_led_cb(btstack_timer_source *ts)
|
||||
{
|
||||
static bool led_state = false;
|
||||
led_state = !led_state;
|
||||
|
||||
if constexpr (board_api::NUM_LEDS == 1)
|
||||
{
|
||||
board_api::set_led(any_connected() ? 1 : (led_state ? 1 : 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint8_t i = 0; i < board_api::NUM_LEDS; ++i)
|
||||
{
|
||||
board_api::set_led(i, devices_[i].connected.load() ? 1 : (led_state ? 1 : 0));
|
||||
}
|
||||
}
|
||||
// btstack_run_loop_remove_timer(ts);
|
||||
btstack_run_loop_set_timer(ts, LED_TIME_MS);
|
||||
btstack_run_loop_add_timer(ts);
|
||||
}
|
||||
|
||||
//BT Driver
|
||||
|
||||
static void init(int argc, const char** arg_V)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void init_complete_cb(void)
|
||||
{
|
||||
uni_bt_enable_new_connections_unsafe(true);
|
||||
|
||||
// Based on runtime condition, you can delete or list the stored BT keys.
|
||||
if (1)
|
||||
{
|
||||
uni_bt_del_keys_unsafe();
|
||||
}
|
||||
else
|
||||
{
|
||||
uni_bt_list_keys_unsafe();
|
||||
}
|
||||
|
||||
uni_property_dump_all();
|
||||
}
|
||||
|
||||
static uni_error_t device_discovered_cb(bd_addr_t addr, const char* name, uint16_t cod, uint8_t rssi)
|
||||
{
|
||||
if (!((cod & UNI_BT_COD_MINOR_MASK) & UNI_BT_COD_MINOR_GAMEPAD))
|
||||
{
|
||||
return UNI_ERROR_IGNORE_DEVICE;
|
||||
}
|
||||
return UNI_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static void device_connected_cb(uni_hid_device_t* device)
|
||||
{
|
||||
#ifdef CONFIG_BLUEPAD32_USB_CONSOLE_ENABLE
|
||||
logd("BP32", "Device connected, addr: %p, index: %i\n", device, uni_hid_device_get_idx_for_instance(device));
|
||||
#endif
|
||||
|
||||
int idx = uni_hid_device_get_idx_for_instance(device);
|
||||
if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
devices_[idx].connected.store(true);
|
||||
}
|
||||
|
||||
static void device_disconnected_cb(uni_hid_device_t* device)
|
||||
{
|
||||
#ifdef CONFIG_BLUEPAD32_USB_CONSOLE_ENABLE
|
||||
logd("BP32", "Device disconnected, addr: %p, index: %i\n", device, uni_hid_device_get_idx_for_instance(device));
|
||||
#endif
|
||||
|
||||
int idx = uni_hid_device_get_idx_for_instance(device);
|
||||
if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReportIn report_in;
|
||||
report_in.index = static_cast<uint8_t>(idx);
|
||||
devices_[idx].report_in.store(report_in);
|
||||
devices_[idx].connected.store(false);
|
||||
}
|
||||
|
||||
static uni_error_t device_ready_cb(uni_hid_device_t* device)
|
||||
{
|
||||
return UNI_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static void oob_event_cb(uni_platform_oob_event_t event, void* data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static void controller_data_cb(uni_hid_device_t* device, uni_controller_t* controller)
|
||||
{
|
||||
static uni_gamepad_t prev_uni_gp[CONFIG_BLUEPAD32_MAX_DEVICES] = {};
|
||||
|
||||
if (controller->klass != UNI_CONTROLLER_CLASS_GAMEPAD)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uni_gamepad_t *uni_gp = &controller->gamepad;
|
||||
int idx = uni_hid_device_get_idx_for_instance(device);
|
||||
|
||||
if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0 || std::memcmp(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t)) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReportIn report_in;
|
||||
report_in.index = static_cast<uint8_t>(idx);
|
||||
|
||||
switch (uni_gp->dpad)
|
||||
{
|
||||
case DPAD_UP:
|
||||
report_in.dpad = Gamepad::DPad::UP;
|
||||
break;
|
||||
case DPAD_DOWN:
|
||||
report_in.dpad = Gamepad::DPad::DOWN;
|
||||
break;
|
||||
case DPAD_LEFT:
|
||||
report_in.dpad = Gamepad::DPad::LEFT;
|
||||
break;
|
||||
case DPAD_RIGHT:
|
||||
report_in.dpad = Gamepad::DPad::RIGHT;
|
||||
break;
|
||||
case DPAD_UP | DPAD_RIGHT:
|
||||
report_in.dpad = Gamepad::DPad::UP_RIGHT;
|
||||
break;
|
||||
case DPAD_DOWN | DPAD_RIGHT:
|
||||
report_in.dpad = Gamepad::DPad::DOWN_RIGHT;
|
||||
break;
|
||||
case DPAD_DOWN | DPAD_LEFT:
|
||||
report_in.dpad = Gamepad::DPad::DOWN_LEFT;
|
||||
break;
|
||||
case DPAD_UP | DPAD_LEFT:
|
||||
report_in.dpad = Gamepad::DPad::UP_LEFT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (uni_gp->buttons & BUTTON_A) report_in.buttons |= Gamepad::Button::A;
|
||||
if (uni_gp->buttons & BUTTON_B) report_in.buttons |= Gamepad::Button::B;
|
||||
if (uni_gp->buttons & BUTTON_X) report_in.buttons |= Gamepad::Button::X;
|
||||
if (uni_gp->buttons & BUTTON_Y) report_in.buttons |= Gamepad::Button::Y;
|
||||
if (uni_gp->buttons & BUTTON_SHOULDER_L) report_in.buttons |= Gamepad::Button::LB;
|
||||
if (uni_gp->buttons & BUTTON_SHOULDER_R) report_in.buttons |= Gamepad::Button::RB;
|
||||
if (uni_gp->buttons & BUTTON_THUMB_L) report_in.buttons |= Gamepad::Button::L3;
|
||||
if (uni_gp->buttons & BUTTON_THUMB_R) report_in.buttons |= Gamepad::Button::R3;
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_BACK) report_in.buttons |= Gamepad::Button::BACK;
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_START) report_in.buttons |= Gamepad::Button::START;
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_SYSTEM) report_in.buttons |= Gamepad::Button::SYS;
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_CAPTURE) report_in.buttons |= Gamepad::Button::MISC;
|
||||
|
||||
report_in.trigger_l = Gamepad::scale_trigger(uni_gp->brake);
|
||||
report_in.trigger_r = Gamepad::scale_trigger(uni_gp->throttle);
|
||||
|
||||
report_in.joystick_lx = static_cast<int16_t>(uni_gp->axis_x);
|
||||
report_in.joystick_ly = static_cast<int16_t>(uni_gp->axis_y);
|
||||
report_in.joystick_rx = static_cast<int16_t>(uni_gp->axis_rx);
|
||||
report_in.joystick_ry = static_cast<int16_t>(uni_gp->axis_ry);
|
||||
|
||||
devices_[idx].report_in.store(report_in);
|
||||
|
||||
std::memcpy(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t));
|
||||
}
|
||||
|
||||
const uni_property_t* get_property_cb(uni_property_idx_t idx)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uni_platform* get_driver()
|
||||
{
|
||||
static uni_platform driver =
|
||||
{
|
||||
.name = "OGXMiniW",
|
||||
.init = init,
|
||||
.on_init_complete = init_complete_cb,
|
||||
.on_device_discovered = device_discovered_cb,
|
||||
.on_device_connected = device_connected_cb,
|
||||
.on_device_disconnected = device_disconnected_cb,
|
||||
.on_device_ready = device_ready_cb,
|
||||
.on_controller_data = controller_data_cb,
|
||||
.get_property = get_property_cb,
|
||||
.on_oob_event = oob_event_cb,
|
||||
};
|
||||
return &driver;
|
||||
}
|
||||
|
||||
//Public
|
||||
|
||||
ReportIn get_report_in(uint8_t index)
|
||||
{
|
||||
// if (index >= CONFIG_BLUEPAD32_MAX_DEVICES)
|
||||
// {
|
||||
// return ReportIn();
|
||||
// }
|
||||
return devices_[index].report_in.load();
|
||||
}
|
||||
|
||||
void set_report_out(const ReportOut& report_out)
|
||||
{
|
||||
if (report_out.index >= CONFIG_BLUEPAD32_MAX_DEVICES)
|
||||
{
|
||||
return;
|
||||
}
|
||||
devices_[report_out.index].report_out.store(report_out);
|
||||
}
|
||||
|
||||
void run_task()
|
||||
{
|
||||
board_api::init_pins();
|
||||
|
||||
btstack_init();
|
||||
|
||||
uni_platform_set_custom(get_driver());
|
||||
uni_init(0, nullptr);
|
||||
|
||||
btstack_timer_source_t feedback_timer;
|
||||
feedback_timer.process = send_feedback_cb;
|
||||
feedback_timer.context = nullptr;
|
||||
|
||||
btstack_run_loop_set_timer(&feedback_timer, FEEDBACK_TIME_MS);
|
||||
btstack_run_loop_add_timer(&feedback_timer);
|
||||
|
||||
btstack_timer_source_t led_timer;
|
||||
led_timer.process = check_led_cb;
|
||||
led_timer.context = nullptr;
|
||||
|
||||
btstack_run_loop_set_timer(&led_timer, LED_TIME_MS);
|
||||
btstack_run_loop_add_timer(&led_timer);
|
||||
|
||||
btstack_run_loop_execute();
|
||||
}
|
||||
|
||||
//Thread safe
|
||||
bool any_connected()
|
||||
{
|
||||
for (auto& device : devices_)
|
||||
{
|
||||
if (device.connected.load())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool connected(uint8_t index)
|
||||
{
|
||||
return devices_[index].connected.load();
|
||||
}
|
||||
|
||||
} // namespace bluepad32
|
||||
|
||||
// #include <cstdint>
|
||||
// #include <atomic>
|
||||
// #include <cstring>
|
||||
// #include <array>
|
||||
// #include <freertos/FreeRTOS.h>
|
||||
// #include <freertos/task.h>
|
||||
// #include <freertos/timers.h>
|
||||
// #include <freertos/queue.h>
|
||||
// #include <esp_log.h>
|
||||
|
||||
// #include "btstack_port_esp32.h"
|
||||
// #include "btstack_run_loop.h"
|
||||
// #include "btstack_stdio_esp32.h"
|
||||
// #include "uni.h"
|
||||
|
||||
// #include "sdkconfig.h"
|
||||
// #include "Board/board_api.h"
|
||||
// #include "I2CDriver/I2CDriver.h"
|
||||
// #include "Bluepad32/Gamepad.h"
|
||||
// #include "Bluepad32/Bluepad32.h"
|
||||
|
||||
// namespace bluepad32 {
|
||||
|
||||
// static constexpr uint32_t FEEDBACK_TIME_MS = 200;
|
||||
// static constexpr uint32_t LED_TIME_MS = 500;
|
||||
|
||||
// struct Device
|
||||
// {
|
||||
// std::atomic<bool> connected{false};
|
||||
// std::atomic<ReportIn> report_in;
|
||||
// std::atomic<bool> new_report_in{false};
|
||||
// std::atomic<ReportOut> report_out;
|
||||
// };
|
||||
|
||||
// std::array<Device, CONFIG_BLUEPAD32_MAX_DEVICES> devices_;
|
||||
|
||||
// static inline void send_feedback_cb(btstack_timer_source *ts)
|
||||
// {
|
||||
// uni_hid_device_t* bp_device;
|
||||
|
||||
// for (uint8_t i = 0; i < CONFIG_BLUEPAD32_MAX_DEVICES; ++i)
|
||||
// {
|
||||
// bp_device = uni_hid_device_get_instance_for_idx(i);
|
||||
// if (!bp_device || !bp_device->report_parser.play_dual_rumble)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// ReportOut report_out = devices_[i].report_out.load();
|
||||
// bp_device->report_parser.play_dual_rumble(bp_device, 0, FEEDBACK_TIME_MS, report_out.rumble_l, report_out.rumble_r);
|
||||
// }
|
||||
|
||||
// btstack_run_loop_remove_timer(ts);
|
||||
// btstack_run_loop_set_timer(ts, FEEDBACK_TIME_MS);
|
||||
// btstack_run_loop_add_timer(ts);
|
||||
// }
|
||||
|
||||
// static inline void check_led_cb(btstack_timer_source *ts)
|
||||
// {
|
||||
// static bool led_state = false;
|
||||
// led_state = !led_state;
|
||||
|
||||
// if constexpr (board_api::NUM_LEDS == 1)
|
||||
// {
|
||||
// board_api::set_led(any_connected() ? 1 : (led_state ? 1 : 0));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// for (uint8_t i = 0; i < board_api::NUM_LEDS; ++i)
|
||||
// {
|
||||
// board_api::set_led(i, devices_[i].connected.load() ? 1 : (led_state ? 1 : 0));
|
||||
// }
|
||||
// }
|
||||
|
||||
// btstack_run_loop_remove_timer(ts);
|
||||
// btstack_run_loop_set_timer(ts, LED_TIME_MS);
|
||||
// btstack_run_loop_add_timer(ts);
|
||||
// }
|
||||
|
||||
// //BT Driver
|
||||
|
||||
// static void init(int argc, const char** arg_V)
|
||||
// {
|
||||
|
||||
// }
|
||||
|
||||
// static void init_complete_cb(void)
|
||||
// {
|
||||
// uni_bt_enable_new_connections_unsafe(true);
|
||||
|
||||
// // Based on runtime condition, you can delete or list the stored BT keys.
|
||||
// if (1)
|
||||
// {
|
||||
// uni_bt_del_keys_unsafe();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// uni_bt_list_keys_unsafe();
|
||||
// }
|
||||
|
||||
// uni_property_dump_all();
|
||||
// }
|
||||
|
||||
// static uni_error_t device_discovered_cb(bd_addr_t addr, const char* name, uint16_t cod, uint8_t rssi)
|
||||
// {
|
||||
// if (!((cod & UNI_BT_COD_MINOR_MASK) & UNI_BT_COD_MINOR_GAMEPAD))
|
||||
// {
|
||||
// return UNI_ERROR_IGNORE_DEVICE;
|
||||
// }
|
||||
// return UNI_ERROR_SUCCESS;
|
||||
// }
|
||||
|
||||
// static void device_connected_cb(uni_hid_device_t* device)
|
||||
// {
|
||||
// #ifdef CONFIG_BLUEPAD32_USB_CONSOLE_ENABLE
|
||||
// logd("BP32", "Device connected, addr: %p, index: %i\n", device, uni_hid_device_get_idx_for_instance(device));
|
||||
// #endif
|
||||
|
||||
// int idx = uni_hid_device_get_idx_for_instance(device);
|
||||
// if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// devices_[idx].connected.store(true);
|
||||
// }
|
||||
|
||||
// static void device_disconnected_cb(uni_hid_device_t* device)
|
||||
// {
|
||||
// #ifdef CONFIG_BLUEPAD32_USB_CONSOLE_ENABLE
|
||||
// logd("BP32", "Device disconnected, addr: %p, index: %i\n", device, uni_hid_device_get_idx_for_instance(device));
|
||||
// #endif
|
||||
|
||||
// int idx = uni_hid_device_get_idx_for_instance(device);
|
||||
// if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// ReportIn report_in = ReportIn();
|
||||
// report_in.index = static_cast<uint8_t>(idx);
|
||||
// devices_[idx].report_in.store(report_in);
|
||||
// devices_[idx].connected.store(false);
|
||||
// }
|
||||
|
||||
// static uni_error_t device_ready_cb(uni_hid_device_t* device)
|
||||
// {
|
||||
// return UNI_ERROR_SUCCESS;
|
||||
// }
|
||||
|
||||
// static void oob_event_cb(uni_platform_oob_event_t event, void* data)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// static void controller_data_cb(uni_hid_device_t* device, uni_controller_t* controller)
|
||||
// {
|
||||
// static uni_gamepad_t prev_uni_gp[CONFIG_BLUEPAD32_MAX_DEVICES] = {};
|
||||
|
||||
// if (!device || !controller || controller->klass != UNI_CONTROLLER_CLASS_GAMEPAD)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// uni_gamepad_t *uni_gp = &controller->gamepad;
|
||||
// int idx = uni_hid_device_get_idx_for_instance(device);
|
||||
|
||||
// if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0 || std::memcmp(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t)) == 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// #ifdef CONFIG_BLUEPAD32_USB_CONSOLE_ENABLE
|
||||
// logi("BP32 Stack", "Remaining stack: %d\n", uxTaskGetStackHighWaterMark(NULL));
|
||||
// #endif
|
||||
|
||||
// ReportIn report_in;
|
||||
// report_in.index = static_cast<uint8_t>(idx);
|
||||
|
||||
// switch (uni_gp->dpad)
|
||||
// {
|
||||
// case DPAD_UP:
|
||||
// report_in.dpad = Gamepad::DPad::UP;
|
||||
// break;
|
||||
// case DPAD_DOWN:
|
||||
// report_in.dpad = Gamepad::DPad::DOWN;
|
||||
// break;
|
||||
// case DPAD_LEFT:
|
||||
// report_in.dpad = Gamepad::DPad::LEFT;
|
||||
// break;
|
||||
// case DPAD_RIGHT:
|
||||
// report_in.dpad = Gamepad::DPad::RIGHT;
|
||||
// break;
|
||||
// case DPAD_UP | DPAD_RIGHT:
|
||||
// report_in.dpad = Gamepad::DPad::UP_RIGHT;
|
||||
// break;
|
||||
// case DPAD_DOWN | DPAD_RIGHT:
|
||||
// report_in.dpad = Gamepad::DPad::DOWN_RIGHT;
|
||||
// break;
|
||||
// case DPAD_DOWN | DPAD_LEFT:
|
||||
// report_in.dpad = Gamepad::DPad::DOWN_LEFT;
|
||||
// break;
|
||||
// case DPAD_UP | DPAD_LEFT:
|
||||
// report_in.dpad = Gamepad::DPad::UP_LEFT;
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
|
||||
// if (uni_gp->buttons & BUTTON_A) report_in.buttons |= Gamepad::Button::A;
|
||||
// if (uni_gp->buttons & BUTTON_B) report_in.buttons |= Gamepad::Button::B;
|
||||
// if (uni_gp->buttons & BUTTON_X) report_in.buttons |= Gamepad::Button::X;
|
||||
// if (uni_gp->buttons & BUTTON_Y) report_in.buttons |= Gamepad::Button::Y;
|
||||
// if (uni_gp->buttons & BUTTON_SHOULDER_L) report_in.buttons |= Gamepad::Button::LB;
|
||||
// if (uni_gp->buttons & BUTTON_SHOULDER_R) report_in.buttons |= Gamepad::Button::RB;
|
||||
// if (uni_gp->buttons & BUTTON_THUMB_L) report_in.buttons |= Gamepad::Button::L3;
|
||||
// if (uni_gp->buttons & BUTTON_THUMB_R) report_in.buttons |= Gamepad::Button::R3;
|
||||
// if (uni_gp->misc_buttons & MISC_BUTTON_BACK) report_in.buttons |= Gamepad::Button::BACK;
|
||||
// if (uni_gp->misc_buttons & MISC_BUTTON_START) report_in.buttons |= Gamepad::Button::START;
|
||||
// if (uni_gp->misc_buttons & MISC_BUTTON_SYSTEM) report_in.buttons |= Gamepad::Button::SYS;
|
||||
// if (uni_gp->misc_buttons & MISC_BUTTON_CAPTURE) report_in.buttons |= Gamepad::Button::MISC;
|
||||
|
||||
// report_in.trigger_l = Gamepad::scale_trigger(uni_gp->brake);
|
||||
// report_in.trigger_r = Gamepad::scale_trigger(uni_gp->throttle);
|
||||
|
||||
// report_in.joystick_lx = static_cast<int16_t>(uni_gp->axis_x);
|
||||
// report_in.joystick_ly = static_cast<int16_t>(uni_gp->axis_y);
|
||||
// report_in.joystick_rx = static_cast<int16_t>(uni_gp->axis_rx);
|
||||
// report_in.joystick_ry = static_cast<int16_t>(uni_gp->axis_ry);
|
||||
|
||||
// devices_[idx].report_in.store(report_in);
|
||||
// devices_[idx].new_report_in.store(true);
|
||||
|
||||
// std::memcpy(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t));
|
||||
// }
|
||||
|
||||
// const uni_property_t* get_property_cb(uni_property_idx_t idx)
|
||||
// {
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
// uni_platform* get_driver()
|
||||
// {
|
||||
// static uni_platform driver =
|
||||
// {
|
||||
// .name = "OGXMiniW",
|
||||
// .init = init,
|
||||
// .on_init_complete = init_complete_cb,
|
||||
// .on_device_discovered = device_discovered_cb,
|
||||
// .on_device_connected = device_connected_cb,
|
||||
// .on_device_disconnected = device_disconnected_cb,
|
||||
// .on_device_ready = device_ready_cb,
|
||||
// .on_controller_data = controller_data_cb,
|
||||
// .get_property = get_property_cb,
|
||||
// .on_oob_event = oob_event_cb,
|
||||
// };
|
||||
// return &driver;
|
||||
// }
|
||||
|
||||
// //Public
|
||||
|
||||
// void run_task()
|
||||
// {
|
||||
// board_api::init_pins();
|
||||
|
||||
// btstack_init();
|
||||
|
||||
// uni_platform_set_custom(get_driver());
|
||||
// uni_init(0, nullptr);
|
||||
|
||||
// btstack_timer_source_t feedback_timer;
|
||||
// feedback_timer.process = send_feedback_cb;
|
||||
// feedback_timer.context = nullptr;
|
||||
|
||||
// btstack_run_loop_set_timer(&feedback_timer, FEEDBACK_TIME_MS);
|
||||
// btstack_run_loop_add_timer(&feedback_timer);
|
||||
|
||||
// btstack_timer_source_t led_timer;
|
||||
// led_timer.process = check_led_cb;
|
||||
// led_timer.context = nullptr;
|
||||
|
||||
// btstack_run_loop_set_timer(&led_timer, LED_TIME_MS);
|
||||
// btstack_run_loop_add_timer(&led_timer);
|
||||
|
||||
// btstack_run_loop_execute();
|
||||
// }
|
||||
|
||||
// //Thread safe
|
||||
// bool any_connected()
|
||||
// {
|
||||
// for (auto& device : devices_)
|
||||
// {
|
||||
// if (device.connected.load())
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// bool connected(uint8_t index)
|
||||
// {
|
||||
// return devices_[index].connected.load();
|
||||
// }
|
||||
|
||||
// bool get_report_in(uint8_t index, ReportIn& report)
|
||||
// {
|
||||
// if (index >= CONFIG_BLUEPAD32_MAX_DEVICES)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// if (devices_[index].new_report_in.exchange(false))
|
||||
// {
|
||||
// report = devices_[index].report_in.load();
|
||||
// return true;
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// void set_report_out(const ReportOut& report)
|
||||
// {
|
||||
// if (report.index >= CONFIG_BLUEPAD32_MAX_DEVICES)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// devices_[report.index].report_out.store(report);
|
||||
// }
|
||||
|
||||
// } // namespace bluepad32
|
||||
19
Firmware/ESP32/main/Bluepad32/Bluepad32.h
Normal file
19
Firmware/ESP32/main/Bluepad32/Bluepad32.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef _BLUEPAD32_DRIVER_H_
|
||||
#define _BLUEPAD32_DRIVER_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "I2CDriver/I2CDriver.h"
|
||||
#include "Reports.h"
|
||||
|
||||
namespace bluepad32
|
||||
{
|
||||
void run_task();
|
||||
bool connected(uint8_t index);
|
||||
bool any_connected();
|
||||
ReportIn get_report_in(uint8_t index);
|
||||
void set_report_out(const ReportOut& report);
|
||||
}
|
||||
|
||||
#endif // _BLUEPAD32_DRIVER_H_
|
||||
82
Firmware/ESP32/main/Bluepad32/Gamepad.h
Normal file
82
Firmware/ESP32/main/Bluepad32/Gamepad.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#ifndef GAMEPAD_H
|
||||
#define GAMEPAD_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Gamepad
|
||||
{
|
||||
namespace DPad
|
||||
{
|
||||
static constexpr uint8_t UP = 0x01;
|
||||
static constexpr uint8_t DOWN = 0x02;
|
||||
static constexpr uint8_t LEFT = 0x04;
|
||||
static constexpr uint8_t RIGHT = 0x08;
|
||||
static constexpr uint8_t UP_LEFT = UP | LEFT;
|
||||
static constexpr uint8_t UP_RIGHT = UP | RIGHT;
|
||||
static constexpr uint8_t DOWN_LEFT = DOWN | LEFT;
|
||||
static constexpr uint8_t DOWN_RIGHT = DOWN | RIGHT;
|
||||
static constexpr uint8_t NONE = 0x00;
|
||||
}
|
||||
namespace Button
|
||||
{
|
||||
static constexpr uint16_t A = 0x0001;
|
||||
static constexpr uint16_t B = 0x0002;
|
||||
static constexpr uint16_t X = 0x0004;
|
||||
static constexpr uint16_t Y = 0x0008;
|
||||
static constexpr uint16_t L3 = 0x0010;
|
||||
static constexpr uint16_t R3 = 0x0020;
|
||||
static constexpr uint16_t BACK = 0x0040;
|
||||
static constexpr uint16_t START = 0x0080;
|
||||
static constexpr uint16_t LB = 0x0100;
|
||||
static constexpr uint16_t RB = 0x0200;
|
||||
static constexpr uint16_t SYS = 0x0400;
|
||||
static constexpr uint16_t MISC = 0x0800;
|
||||
}
|
||||
|
||||
namespace UINT_8
|
||||
{
|
||||
static constexpr uint8_t MID = 0x80;
|
||||
static constexpr uint8_t MAX = 0xFF;
|
||||
}
|
||||
|
||||
namespace INT_10
|
||||
{
|
||||
static constexpr int32_t MIN = -512;
|
||||
static constexpr int32_t MAX = 511;
|
||||
}
|
||||
|
||||
namespace UINT_10
|
||||
{
|
||||
static constexpr int32_t MAX = 1023;
|
||||
}
|
||||
|
||||
static inline uint8_t scale_joystick(int32_t value)
|
||||
{
|
||||
value = value - INT_10::MIN;
|
||||
|
||||
if (value >= UINT_10::MAX)
|
||||
{
|
||||
return UINT_8::MAX;
|
||||
}
|
||||
else if (value <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return static_cast<uint8_t>(value >> 2);
|
||||
}
|
||||
static inline uint8_t scale_trigger(int32_t value)
|
||||
{
|
||||
if (value >= UINT_10::MAX)
|
||||
{
|
||||
return UINT_8::MAX;
|
||||
}
|
||||
else if (value <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return static_cast<uint8_t>(value >> 2);
|
||||
}
|
||||
|
||||
} // namespace Gamepad
|
||||
|
||||
#endif // GAMEPAD_H
|
||||
76
Firmware/ESP32/main/Board/board_api.cpp
Normal file
76
Firmware/ESP32/main/Board/board_api.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "board/board_api.h"
|
||||
|
||||
namespace board_api {
|
||||
|
||||
SemaphoreHandle_t leds_mutex_ = nullptr;
|
||||
SemaphoreHandle_t reset_mutex_ = nullptr;
|
||||
|
||||
void init_pins()
|
||||
{
|
||||
if (leds_mutex_ == nullptr)
|
||||
{
|
||||
leds_mutex_ = xSemaphoreCreateMutex();
|
||||
}
|
||||
if (reset_mutex_ == nullptr)
|
||||
{
|
||||
reset_mutex_ = xSemaphoreCreateMutex();
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(leds_mutex_, portMAX_DELAY))
|
||||
{
|
||||
for (const auto& LED_PIN : LED_PINS)
|
||||
{
|
||||
gpio_reset_pin(LED_PIN);
|
||||
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(LED_PIN, 0);
|
||||
}
|
||||
xSemaphoreGive(leds_mutex_);
|
||||
}
|
||||
// if (xSemaphoreTake(reset_mutex_, portMAX_DELAY))
|
||||
// {
|
||||
// gpio_reset_pin(RESET_PIN);
|
||||
// gpio_set_direction(RESET_PIN, GPIO_MODE_INPUT);
|
||||
// gpio_set_level(RESET_PIN, 1);
|
||||
// xSemaphoreGive(reset_mutex_);
|
||||
// }
|
||||
}
|
||||
|
||||
//Set LED by index
|
||||
void set_led(uint8_t index, bool state)
|
||||
{
|
||||
if constexpr (NUM_LEDS < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (index >= NUM_LEDS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (xSemaphoreTake(leds_mutex_, portMAX_DELAY))
|
||||
{
|
||||
gpio_set_level(LED_PINS[index], state ? 1 : 0);
|
||||
xSemaphoreGive(leds_mutex_);
|
||||
}
|
||||
}
|
||||
|
||||
//Set first LED
|
||||
void set_led(bool state)
|
||||
{
|
||||
if constexpr (NUM_LEDS < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (xSemaphoreTake(leds_mutex_, portMAX_DELAY))
|
||||
{
|
||||
gpio_set_level(LED_PINS[0], state ? 1 : 0);
|
||||
xSemaphoreGive(leds_mutex_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace board_api
|
||||
44
Firmware/ESP32/main/Board/board_api.h
Normal file
44
Firmware/ESP32/main/Board/board_api.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef _BOARD_API_H_
|
||||
#define _BOARD_API_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define MAX_GPIO_NUM 40
|
||||
|
||||
namespace board_api
|
||||
{
|
||||
static constexpr gpio_num_t RESET_PIN = static_cast<gpio_num_t>(CONFIG_RESET_PIN);
|
||||
static_assert(RESET_PIN < MAX_GPIO_NUM, "Invalid RESET_PIN");
|
||||
|
||||
static constexpr gpio_num_t LED_PINS[] =
|
||||
{
|
||||
#if defined(CONFIG_ENABLE_LED_1) && (CONFIG_LED_PIN_1 < MAX_GPIO_NUM) && (CONFIG_BLUEPAD32_MAX_DEVICES > 0)
|
||||
static_cast<gpio_num_t>(CONFIG_LED_PIN_1),
|
||||
|
||||
#if defined(CONFIG_ENABLE_LED_2) && (CONFIG_LED_PIN_2 < MAX_GPIO_NUM) && (CONFIG_BLUEPAD32_MAX_DEVICES > 1)
|
||||
static_cast<gpio_num_t>(CONFIG_LED_PIN_2),
|
||||
|
||||
#if defined(CONFIG_ENABLE_LED_3) && (CONFIG_LED_PIN_3 < MAX_GPIO_NUM) && (CONFIG_BLUEPAD32_MAX_DEVICES > 2)
|
||||
static_cast<gpio_num_t>(CONFIG_LED_PIN_3),
|
||||
|
||||
#if defined(CONFIG_ENABLE_LED_4) && (CONFIG_LED_PIN_4 < MAX_GPIO_NUM) && (CONFIG_BLUEPAD32_MAX_DEVICES > 3)
|
||||
static_cast<gpio_num_t>(CONFIG_LED_PIN_4),
|
||||
|
||||
#endif // CONFIG_ENABLE_LED_4
|
||||
#endif // CONFIG_ENABLE_LED_3
|
||||
#endif // CONFIG_ENABLE_LED_2
|
||||
#endif // CONFIG_ENABLE_LED_1
|
||||
};
|
||||
|
||||
static constexpr uint8_t NUM_LEDS = sizeof(LED_PINS) / sizeof(LED_PINS[0]);
|
||||
|
||||
void init_pins();
|
||||
void set_led(uint8_t index, bool state);
|
||||
void set_led(bool state);
|
||||
void check_reset_pin();
|
||||
}
|
||||
|
||||
#endif // _BOARD_API_H_
|
||||
15
Firmware/ESP32/main/CMakeLists.txt
Normal file
15
Firmware/ESP32/main/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"main.c"
|
||||
"c_wrapper.cpp"
|
||||
"Board/board_api.cpp"
|
||||
"Bluepad32/Bluepad32.cpp"
|
||||
"I2CDriver/I2CDriver.cpp"
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
REQUIRES
|
||||
bluepad32
|
||||
btstack
|
||||
driver
|
||||
nvs_flash
|
||||
)
|
||||
210
Firmware/ESP32/main/I2CDriver/I2CDriver.cpp
Normal file
210
Firmware/ESP32/main/I2CDriver/I2CDriver.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
// #include <cstring>
|
||||
// #include <atomic>
|
||||
// #include <array>
|
||||
// #include <freertos/FreeRTOS.h>
|
||||
// #include <freertos/task.h>
|
||||
// #include <freertos/timers.h>
|
||||
// #include <driver/i2c.h>
|
||||
// #include <driver/gpio.h>
|
||||
// #include <esp_log.h>
|
||||
|
||||
// #include "sdkconfig.h"
|
||||
// #include "Board/board_api.h"
|
||||
// #include "I2CDriver/I2CDriver.h"
|
||||
// #include "Bluepad32/Bluepad32.h"
|
||||
|
||||
// namespace i2c_driver {
|
||||
|
||||
// static constexpr bool MULTI_SLAVE =
|
||||
// #if CONFIG_MULTI_SLAVE_MODE == 0
|
||||
// false;
|
||||
// #else
|
||||
// true;
|
||||
// #endif
|
||||
|
||||
// std::atomic<bool> poll_rumble_{false};
|
||||
|
||||
// void i2c_initialize()
|
||||
// {
|
||||
// i2c_config_t conf;
|
||||
// std::memset(&conf, 0, sizeof(i2c_config_t));
|
||||
// conf.mode = I2C_MODE_MASTER;
|
||||
// conf.sda_io_num = GPIO_NUM_21;
|
||||
// conf.scl_io_num = GPIO_NUM_22;
|
||||
// conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
// conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
// conf.master.clk_speed = 400 * 1000;
|
||||
|
||||
// i2c_param_config(I2C_NUM_0, &conf);
|
||||
// i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0);
|
||||
|
||||
// ESP_LOGD("I2C", "I2C initialized at pins {SDA: %d, SCL: %d}", conf.sda_io_num, conf.scl_io_num);
|
||||
// }
|
||||
|
||||
// static inline esp_err_t i2c_write_blocking(uint8_t slave_address, uint8_t* data, size_t data_len)
|
||||
// {
|
||||
// i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
// i2c_master_start(cmd);
|
||||
// i2c_master_write_byte(cmd, (slave_address << 1) | I2C_MASTER_WRITE, true);
|
||||
// i2c_master_write(cmd, data, data_len, true);
|
||||
// i2c_master_stop(cmd);
|
||||
|
||||
// esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(2));
|
||||
// i2c_cmd_link_delete(cmd);
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
// static inline esp_err_t i2c_read_blocking(uint8_t slave_address, uint8_t* data, size_t data_len)
|
||||
// {
|
||||
// i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
// i2c_master_start(cmd);
|
||||
// i2c_master_write_byte(cmd, (slave_address << 1) | I2C_MASTER_READ, true);
|
||||
|
||||
// if (data_len > 1)
|
||||
// {
|
||||
// i2c_master_read(cmd, data, data_len - 1, I2C_MASTER_ACK);
|
||||
// }
|
||||
|
||||
// i2c_master_read_byte(cmd, data + data_len - 1, I2C_MASTER_NACK);
|
||||
// i2c_master_stop(cmd);
|
||||
|
||||
// esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(2));
|
||||
// i2c_cmd_link_delete(cmd);
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
// void run_loop(void* queue)
|
||||
// {
|
||||
// ReportIn report_in = ReportIn();
|
||||
// ReportOut report_out = ReportOut();
|
||||
|
||||
// i2c_initialize();
|
||||
|
||||
// ESP_LOGD("I2C Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL));
|
||||
|
||||
// while (true)
|
||||
// {
|
||||
// for (uint8_t i = 0; i < CONFIG_BLUEPAD32_MAX_DEVICES; ++i)
|
||||
// {
|
||||
// vTaskDelay(1);
|
||||
|
||||
// if (!bluepad32::connected(i))
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// report_in = bluepad32::get_report_in(i);
|
||||
// if (i2c_write_blocking(MULTI_SLAVE ? (report_in.index + 1) : 0x01, reinterpret_cast<uint8_t*>(&report_in), sizeof(ReportIn)) != ESP_OK)
|
||||
// {
|
||||
// ESP_LOGD("I2C", "Failed to write report_in");
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// vTaskDelay(1);
|
||||
|
||||
// if (i2c_read_blocking(MULTI_SLAVE ? (report_in.index + 1) : 0x01, reinterpret_cast<uint8_t*>(&report_out), sizeof(ReportOut)) != ESP_OK)
|
||||
// {
|
||||
// ESP_LOGD("I2C", "Failed to read report_out");
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// bluepad32::set_report_out(report_out);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // void run_loop()
|
||||
// // {
|
||||
// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL));
|
||||
|
||||
// // ReportIn report_in = ReportIn();
|
||||
|
||||
// // ESP_LOGD("I2C", "ReportIn 1 created: size: %d", sizeof(report_in));
|
||||
// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL));
|
||||
|
||||
// // ReportIn different_rep = ReportIn();
|
||||
|
||||
// // ESP_LOGD("I2C", "ReportIn 2 created: size: %d", sizeof(different_rep));
|
||||
// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL));
|
||||
|
||||
// // ReportOut report_out = ReportOut();
|
||||
|
||||
// // ESP_LOGD("I2C", "ReportOut created: size: %d", sizeof(report_out));
|
||||
// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL));
|
||||
|
||||
// // i2c_initialize();
|
||||
// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL));
|
||||
|
||||
// // while (true)
|
||||
// // {
|
||||
// // i2c_write_blocking(MULTI_SLAVE ? (report_in.index + 1) : 0x01, reinterpret_cast<uint8_t*>(&report_in), sizeof(ReportIn));
|
||||
// // vTaskDelay(1);
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// } // namespace I2CDriver
|
||||
|
||||
#include <array>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "I2CDriver/I2CDriver.h"
|
||||
#include "Bluepad32/Bluepad32.h"
|
||||
|
||||
void I2CDriver::initialize_i2c()
|
||||
{
|
||||
i2c_config_t conf;
|
||||
std::memset(&conf, 0, sizeof(i2c_config_t));
|
||||
|
||||
conf.mode = I2C_MODE_MASTER;
|
||||
conf.sda_io_num = GPIO_NUM_21;
|
||||
conf.scl_io_num = GPIO_NUM_22;
|
||||
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf.master.clk_speed = 400 * 1000;
|
||||
|
||||
i2c_param_config(I2C_NUM_0, &conf);
|
||||
i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0);
|
||||
}
|
||||
|
||||
void I2CDriver::run_task()
|
||||
{
|
||||
ReportIn report_in;
|
||||
ReportOut report_out;
|
||||
|
||||
initialize_i2c();
|
||||
|
||||
while (true)
|
||||
{
|
||||
for (uint8_t i = 0; i < CONFIG_BLUEPAD32_MAX_DEVICES; ++i)
|
||||
{
|
||||
if (!bluepad32::connected(i))
|
||||
{
|
||||
vTaskDelay(1);
|
||||
continue;
|
||||
}
|
||||
// if (bluepad32::get_report_in(i, report_in))
|
||||
// {
|
||||
report_in = bluepad32::get_report_in(i);
|
||||
if (i2c_write_blocking(MULTI_SLAVE ? (i + 1) : 0x01, reinterpret_cast<const uint8_t*>(&report_in), sizeof(ReportIn)) != ESP_OK)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// }
|
||||
|
||||
vTaskDelay(1);
|
||||
|
||||
if (i2c_read_blocking(MULTI_SLAVE ? (i + 1) : 0x01, reinterpret_cast<uint8_t*>(&report_out), sizeof(ReportOut)) != ESP_OK)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bluepad32::set_report_out(report_out);
|
||||
}
|
||||
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
62
Firmware/ESP32/main/I2CDriver/I2CDriver.h
Normal file
62
Firmware/ESP32/main/I2CDriver/I2CDriver.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef _I2C_DRIVER_H_
|
||||
#define _I2C_DRIVER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <atomic>
|
||||
#include <driver/i2c.h>
|
||||
|
||||
#include "Reports.h"
|
||||
|
||||
class I2CDriver
|
||||
{
|
||||
public:
|
||||
I2CDriver() = default;
|
||||
~I2CDriver() { i2c_driver_delete(I2C_NUM_0); }
|
||||
|
||||
void run_task();
|
||||
|
||||
private:
|
||||
static constexpr bool MULTI_SLAVE =
|
||||
#if CONFIG_MULTI_SLAVE_MODE == 0
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
#endif
|
||||
|
||||
void initialize_i2c();
|
||||
|
||||
static inline esp_err_t i2c_write_blocking(uint8_t address, const uint8_t* buffer, size_t len)
|
||||
{
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true);
|
||||
i2c_master_write(cmd, buffer, len, true);
|
||||
i2c_master_stop(cmd);
|
||||
|
||||
esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(2));
|
||||
i2c_cmd_link_delete(cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline esp_err_t i2c_read_blocking(uint8_t address, uint8_t* buffer, size_t len)
|
||||
{
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_READ, true);
|
||||
|
||||
if (len > 1)
|
||||
{
|
||||
i2c_master_read(cmd, buffer, len - 1, I2C_MASTER_ACK);
|
||||
}
|
||||
|
||||
i2c_master_read_byte(cmd, buffer + len - 1, I2C_MASTER_NACK);
|
||||
i2c_master_stop(cmd);
|
||||
|
||||
esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(2));
|
||||
i2c_cmd_link_delete(cmd);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _I2C_DRIVER_H_
|
||||
50
Firmware/ESP32/main/Kconfig
Normal file
50
Firmware/ESP32/main/Kconfig
Normal file
@@ -0,0 +1,50 @@
|
||||
menu "OGXMini Options"
|
||||
|
||||
config RESET_PIN
|
||||
int "Set reset pin"
|
||||
default 15
|
||||
|
||||
config MULTI_SLAVE_MODE
|
||||
bool "Enable multiple slave devices"
|
||||
default n
|
||||
|
||||
config ENABLE_LED_1
|
||||
bool "Enable LED 1"
|
||||
default y
|
||||
|
||||
config LED_PIN_1
|
||||
int "Set LED 1 pin"
|
||||
default 15
|
||||
depends on ENABLE_LED_1
|
||||
|
||||
config ENABLE_LED_2
|
||||
bool "Enable LED 2"
|
||||
default n
|
||||
depends on ENABLE_LED_1
|
||||
|
||||
config LED_PIN_2
|
||||
int "Set LED 2 pin"
|
||||
default 255
|
||||
depends on ENABLE_LED_2
|
||||
|
||||
config ENABLE_LED_3
|
||||
bool "Enable LED 3"
|
||||
default n
|
||||
depends on ENABLE_LED_2
|
||||
|
||||
config LED_PIN_3
|
||||
int "Set LED 3 pin"
|
||||
default 255
|
||||
depends on ENABLE_LED_3
|
||||
|
||||
config ENABLE_LED_4
|
||||
bool "Enable LED 4"
|
||||
default n
|
||||
depends on ENABLE_LED_3
|
||||
|
||||
config LED_PIN_4
|
||||
int "Set LED 4 pin"
|
||||
default 255
|
||||
depends on ENABLE_LED_4
|
||||
|
||||
endmenu
|
||||
134
Firmware/ESP32/main/Reports.h
Normal file
134
Firmware/ESP32/main/Reports.h
Normal file
@@ -0,0 +1,134 @@
|
||||
#ifndef _REPORTS_H_
|
||||
#define _REPORTS_H_
|
||||
|
||||
#include <cstdint>
|
||||
// #include <array>
|
||||
#include <cstring>
|
||||
// #include "freertos/FreeRTOS.h"
|
||||
// #include "freertos/semphr.h"
|
||||
|
||||
enum class ReportID : uint8_t { UNKNOWN = 0, SET_PAD, GET_PAD };
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct ReportIn
|
||||
{
|
||||
uint8_t report_len;
|
||||
uint8_t report_id;
|
||||
uint8_t index;
|
||||
uint8_t dpad;
|
||||
uint16_t buttons;
|
||||
uint8_t trigger_l;
|
||||
uint8_t trigger_r;
|
||||
int16_t joystick_lx;
|
||||
int16_t joystick_ly;
|
||||
int16_t joystick_rx;
|
||||
int16_t joystick_ry;
|
||||
|
||||
ReportIn()
|
||||
{
|
||||
std::memset(this, 0, sizeof(ReportIn));
|
||||
report_len = sizeof(ReportIn);
|
||||
report_id = static_cast<uint8_t>(ReportID::SET_PAD);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(ReportIn) == 16, "ReportIn is misaligned");
|
||||
|
||||
struct ReportOut
|
||||
{
|
||||
uint8_t report_len;
|
||||
uint8_t report_id;
|
||||
uint8_t index;
|
||||
uint8_t rumble_l;
|
||||
uint8_t rumble_r;
|
||||
|
||||
ReportOut()
|
||||
{
|
||||
std::memset(this, 0, sizeof(ReportOut));
|
||||
report_len = sizeof(ReportOut);
|
||||
report_id = static_cast<uint8_t>(ReportID::GET_PAD);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(ReportOut) == 5, "ReportOut is misaligned");
|
||||
#pragma pack(pop)
|
||||
|
||||
// class ReportQueue
|
||||
// {
|
||||
// public:
|
||||
// ReportQueue()
|
||||
// {
|
||||
// report_in_mutex_ = xSemaphoreCreateMutex();
|
||||
// report_out_mutex_ = xSemaphoreCreateMutex();
|
||||
// }
|
||||
|
||||
// ~ReportQueue()
|
||||
// {
|
||||
// vSemaphoreDelete(report_in_mutex_);
|
||||
// vSemaphoreDelete(report_out_mutex_);
|
||||
// }
|
||||
|
||||
// void set_report_in(const ReportIn* report)
|
||||
// {
|
||||
// if (xSemaphoreTake(report_in_mutex_, portMAX_DELAY))
|
||||
// {
|
||||
// std::memcpy(&report_in_, report, sizeof(ReportIn));
|
||||
// new_report_in_ = true;
|
||||
// xSemaphoreGive(report_in_mutex_);
|
||||
// }
|
||||
// }
|
||||
|
||||
// void set_report_out(const ReportOut* report)
|
||||
// {
|
||||
// if (xSemaphoreTake(report_out_mutex_, portMAX_DELAY))
|
||||
// {
|
||||
// std::memcpy(&report_out_, report, sizeof(ReportOut));
|
||||
// new_report_out_ = true;
|
||||
// xSemaphoreGive(report_out_mutex_);
|
||||
// }
|
||||
// }
|
||||
|
||||
// //Return false if no new data
|
||||
// bool get_report_in(ReportIn* report)
|
||||
// {
|
||||
// if (xSemaphoreTake(report_in_mutex_, portMAX_DELAY))
|
||||
// {
|
||||
// if (new_report_in_)
|
||||
// {
|
||||
// std::memcpy(report, &report_in_, sizeof(ReportIn));
|
||||
// new_report_in_ = false;
|
||||
// xSemaphoreGive(report_in_mutex_);
|
||||
// return true;
|
||||
// }
|
||||
// xSemaphoreGive(report_in_mutex_);
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// //Return false if no new data
|
||||
// bool get_report_out(ReportOut* report)
|
||||
// {
|
||||
// if (xSemaphoreTake(report_out_mutex_, portMAX_DELAY))
|
||||
// {
|
||||
// if (new_report_out_)
|
||||
// {
|
||||
// std::memcpy(report, &report_out_, sizeof(ReportOut));
|
||||
// new_report_out_ = false;
|
||||
// xSemaphoreGive(report_out_mutex_);
|
||||
// return true;
|
||||
// }
|
||||
// xSemaphoreGive(report_out_mutex_);
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// private:
|
||||
// SemaphoreHandle_t report_in_mutex_;
|
||||
// SemaphoreHandle_t report_out_mutex_;
|
||||
|
||||
// ReportIn report_in_;
|
||||
// ReportOut report_out_;
|
||||
|
||||
// bool new_report_in_{false};
|
||||
// bool new_report_out_{false};
|
||||
// };
|
||||
|
||||
#endif // _REPORTS_H_
|
||||
0
Firmware/ESP32/main/ReportsQueue.h
Normal file
0
Firmware/ESP32/main/ReportsQueue.h
Normal file
14
Firmware/ESP32/main/c_wrapper.cpp
Normal file
14
Firmware/ESP32/main/c_wrapper.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "c_wrapper.h"
|
||||
#include "I2CDriver/I2CDriver.h"
|
||||
#include "Bluepad32/Bluepad32.h"
|
||||
|
||||
void run_bluepad32()
|
||||
{
|
||||
bluepad32::run_task();
|
||||
}
|
||||
|
||||
void run_i2c()
|
||||
{
|
||||
I2CDriver i2c_driver;
|
||||
i2c_driver.run_task();
|
||||
}
|
||||
17
Firmware/ESP32/main/c_wrapper.h
Normal file
17
Firmware/ESP32/main/c_wrapper.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _C_WRAPPER_H_
|
||||
#define _C_WRAPPER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void run_bluepad32();
|
||||
void run_i2c();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _C_WRAPPER_H_
|
||||
30
Firmware/ESP32/main/main.c
Normal file
30
Firmware/ESP32/main/main.c
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "c_wrapper.h"
|
||||
|
||||
#define STACK_MULTIPLIER (2048 * CONFIG_BLUEPAD32_MAX_DEVICES)
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
xTaskCreatePinnedToCore(
|
||||
run_bluepad32,
|
||||
"bp32",
|
||||
STACK_MULTIPLIER * 4,
|
||||
NULL,
|
||||
configMAX_PRIORITIES-6,
|
||||
NULL,
|
||||
0 );
|
||||
|
||||
xTaskCreatePinnedToCore(
|
||||
run_i2c,
|
||||
"i2c",
|
||||
STACK_MULTIPLIER * 2,
|
||||
NULL,
|
||||
configMAX_PRIORITIES-8,
|
||||
NULL,
|
||||
1 );
|
||||
}
|
||||
2418
Firmware/ESP32/sdkconfig
Normal file
2418
Firmware/ESP32/sdkconfig
Normal file
File diff suppressed because it is too large
Load Diff
42
Firmware/ESP32/sdkconfig.defaults
Normal file
42
Firmware/ESP32/sdkconfig.defaults
Normal file
@@ -0,0 +1,42 @@
|
||||
#
|
||||
# ESP32 options
|
||||
#
|
||||
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
|
||||
CONFIG_ESP32_XTAL_FREQ_AUTO=y
|
||||
CONFIG_XTAL_FREQ_AUTO=y
|
||||
|
||||
# Bluetooh related
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_CONTROLLER_ONLY=y
|
||||
# Enable Dualmode (BLE and BR/EDR)
|
||||
CONFIG_BTDM_CTRL_MODE_BTDM=y
|
||||
#CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y
|
||||
# Should be, at least, one more than CONFIG_BLUEPAD32_MAX_DEVICES
|
||||
# See: https://gitlab.com/ricardoquesada/bluepad32/-/issues/11
|
||||
CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN=5
|
||||
CONFIG_BTDM_CTRL_BLE_MAX_CONN=5
|
||||
CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN=0
|
||||
CONFIG_BTDM_CTRL_AUTO_LATENCY=y
|
||||
CONFIG_BTDM_CTRL_MODEM_SLEEP=n
|
||||
|
||||
CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=y
|
||||
|
||||
#
|
||||
# FreeRTOS
|
||||
#
|
||||
# Enable FreeRTOS stats formatting functions, needed for 'tasks' console command
|
||||
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
|
||||
|
||||
#
|
||||
# Bluepad32 options
|
||||
#
|
||||
CONFIG_BLUEPAD32_MAX_DEVICES=4
|
||||
CONFIG_BLUEPAD32_PLATFORM_CUSTOM=y
|
||||
|
||||
#
|
||||
# Add here other options
|
||||
#
|
||||
20
Firmware/RP2040/.vscode/c_cpp_properties.json
vendored
Normal file
20
Firmware/RP2040/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Pico",
|
||||
"includePath": [
|
||||
"${PICO_SDK_PATH}/**"
|
||||
],
|
||||
"forcedInclude": [
|
||||
"${PICO_SDK_PATH}/src/common/pico_base/include/pico.h",
|
||||
"${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h"
|
||||
],
|
||||
"defines": [],
|
||||
"compileCommands": "${workspaceFolder}/build/compile_commands.json",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++14",
|
||||
"intelliSenseMode": "linux-gcc-arm"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
120
Firmware/RP2040/.vscode/settings.json
vendored
Normal file
120
Firmware/RP2040/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
|
||||
"files.associations": {
|
||||
"array": "cpp",
|
||||
"any": "cpp",
|
||||
"atomic": "cpp",
|
||||
"barrier": "cpp",
|
||||
"bit": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"bitset": "cpp",
|
||||
"cctype": "cpp",
|
||||
"cfenv": "cpp",
|
||||
"charconv": "cpp",
|
||||
"chrono": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"codecvt": "cpp",
|
||||
"compare": "cpp",
|
||||
"complex": "cpp",
|
||||
"concepts": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"coroutine": "cpp",
|
||||
"csetjmp": "cpp",
|
||||
"csignal": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cuchar": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"forward_list": "cpp",
|
||||
"list": "cpp",
|
||||
"map": "cpp",
|
||||
"set": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"expected": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"netfwd": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"random": "cpp",
|
||||
"ratio": "cpp",
|
||||
"regex": "cpp",
|
||||
"source_location": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"rope": "cpp",
|
||||
"slist": "cpp",
|
||||
"format": "cpp",
|
||||
"fstream": "cpp",
|
||||
"future": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"latch": "cpp",
|
||||
"limits": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"numbers": "cpp",
|
||||
"ostream": "cpp",
|
||||
"ranges": "cpp",
|
||||
"scoped_allocator": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"shared_mutex": "cpp",
|
||||
"span": "cpp",
|
||||
"spanstream": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stacktrace": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"stdfloat": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"syncstream": "cpp",
|
||||
"thread": "cpp",
|
||||
"typeindex": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"valarray": "cpp",
|
||||
"variant": "cpp",
|
||||
"xlocmon": "cpp",
|
||||
"xtr1common": "cpp",
|
||||
"xutility": "cpp",
|
||||
"ios": "cpp",
|
||||
"locale": "cpp",
|
||||
"queue": "cpp",
|
||||
"xfacet": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xiosbase": "cpp",
|
||||
"xlocale": "cpp",
|
||||
"xlocbuf": "cpp",
|
||||
"xlocinfo": "cpp",
|
||||
"xlocmes": "cpp",
|
||||
"xlocnum": "cpp",
|
||||
"xloctime": "cpp",
|
||||
"xmemory": "cpp",
|
||||
"xstring": "cpp",
|
||||
"filesystem": "cpp",
|
||||
"flash.h": "c",
|
||||
"uart_bridge.h": "c",
|
||||
"usbh.h": "c"
|
||||
}
|
||||
}
|
||||
297
Firmware/RP2040/CMakeLists.txt
Normal file
297
Firmware/RP2040/CMakeLists.txt
Normal file
@@ -0,0 +1,297 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
set(FW_NAME "OGX-Mini")
|
||||
set(FW_VERSION "v1.0.0-alpha")
|
||||
|
||||
set(CMAKE_C_COMPILER ${C_COMPILER})
|
||||
set(CMAKE_CXX_COMPILER ${CXX_COMPILER})
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
set(SRC ${CMAKE_CURRENT_LIST_DIR}/src)
|
||||
set(EXTERNAL_DIR ${CMAKE_CURRENT_LIST_DIR}/../external)
|
||||
|
||||
include(${EXTERNAL_DIR}/patch_libs.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/get_pico_sdk.cmake)
|
||||
|
||||
apply_lib_patches(${EXTERNAL_DIR})
|
||||
get_pico_sdk(${EXTERNAL_DIR})
|
||||
|
||||
include(${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||
message("PICO_SDK_VERSION_STRING: ${PICO_SDK_VERSION_STRING}")
|
||||
if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
|
||||
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
|
||||
endif()
|
||||
|
||||
project(${FW_NAME} C CXX ASM)
|
||||
|
||||
set(PICO_PIO_USB_PATH ${EXTERNAL_DIR}/Pico-PIO-USB)
|
||||
set(PICO_TINYUSB_PATH ${EXTERNAL_DIR}/tinyusb)
|
||||
set(BLUEPAD32_ROOT ${EXTERNAL_DIR}/bluepad32)
|
||||
set(BTSTACK_ROOT ${BLUEPAD32_ROOT}/external/btstack)
|
||||
set(PICO_BTSTACK_PATH ${BTSTACK_ROOT})
|
||||
|
||||
set(SOURCES_BOARD
|
||||
${SRC}/main.cpp
|
||||
|
||||
${SRC}/OGXMini/OGXMini_Standard.cpp
|
||||
${SRC}/OGXMini/OGXMini_4Ch.cpp
|
||||
${SRC}/OGXMini/OGXMini_PicoW.cpp
|
||||
${SRC}/OGXMini/OGXMini_ESP32.cpp
|
||||
|
||||
${SRC}/Board/board_api.cpp
|
||||
|
||||
${SRC}/UserSettings/UserSettings.cpp
|
||||
${SRC}/UserSettings/UserProfile.cpp
|
||||
|
||||
${SRC}/USBDevice/tud_callbacks.cpp
|
||||
${SRC}/USBDevice/DeviceManager.cpp
|
||||
${SRC}/USBDevice/DeviceDriver/DeviceDriver.cpp
|
||||
${SRC}/USBDevice/DeviceDriver/PSClassic/PSClassic.cpp
|
||||
${SRC}/USBDevice/DeviceDriver/PS3/PS3.cpp
|
||||
${SRC}/USBDevice/DeviceDriver/Switch/Switch.cpp
|
||||
${SRC}/USBDevice/DeviceDriver/XInput/XInput.cpp
|
||||
${SRC}/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.cpp
|
||||
${SRC}/USBDevice/DeviceDriver/XboxOG/XboxOG_SB.cpp
|
||||
${SRC}/USBDevice/DeviceDriver/XboxOG/XboxOG_XR.cpp
|
||||
${SRC}/USBDevice/DeviceDriver/DInput/DInput.cpp
|
||||
${SRC}/USBDevice/DeviceDriver/WebApp/WebApp.cpp
|
||||
${SRC}/USBDevice/DeviceDriver/XInput/tud_xinput/tud_xinput.cpp
|
||||
${SRC}/USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.cpp
|
||||
)
|
||||
set(LIBS_BOARD
|
||||
# Base
|
||||
pico_stdlib
|
||||
pico_multicore
|
||||
pico_rand
|
||||
hardware_timer
|
||||
hardware_clocks
|
||||
hardware_flash
|
||||
tinyusb_device
|
||||
tinyusb_board
|
||||
# UART
|
||||
hardware_uart
|
||||
hardware_irq
|
||||
)
|
||||
set(INC_DIRS_BOARD
|
||||
)
|
||||
|
||||
# Config options
|
||||
# Max gamepads
|
||||
set(MAX_GAMEPADS 1 CACHE STRING "Set number of gamepads, 1 to 4")
|
||||
if (MAX_GAMEPADS GREATER 4 OR MAX_GAMEPADS LESS 1)
|
||||
message(FATAL_ERROR "MAX_GAMEPADS must be between 1 and 4")
|
||||
endif()
|
||||
add_definitions(-DMAX_GAMEPADS=${MAX_GAMEPADS})
|
||||
|
||||
# Board type
|
||||
set(OGXM_BOARD "PI_PICO" CACHE STRING "Set board type, options can be found in src/board_config.h")
|
||||
|
||||
set(FLASH_SIZE_MB 2)
|
||||
set(PICO_BOARD none)
|
||||
|
||||
if (OGXM_BOARD STREQUAL "PI_PICO")
|
||||
set(EN_USB_HOST TRUE)
|
||||
|
||||
elseif(OGXM_BOARD STREQUAL "ADA_FEATHER")
|
||||
set(EN_USB_HOST TRUE)
|
||||
set(EN_RGB TRUE)
|
||||
set(FLASH_SIZE_MB 8)
|
||||
|
||||
elseif(OGXM_BOARD STREQUAL "RP_ZERO")
|
||||
set(EN_USB_HOST TRUE)
|
||||
set(EN_RGB TRUE)
|
||||
|
||||
elseif(OGXM_BOARD STREQUAL "INTERNAL_4CH")
|
||||
set(EN_USB_HOST TRUE)
|
||||
set(EN_4CH TRUE)
|
||||
|
||||
elseif(OGXM_BOARD STREQUAL "EXTERNAL_4CH")
|
||||
set(EN_USB_HOST TRUE)
|
||||
set(EN_4CH TRUE)
|
||||
set(EN_RGB TRUE)
|
||||
|
||||
elseif(OGXM_BOARD STREQUAL "PI_PICOW")
|
||||
set(EN_BLUETOOTH TRUE)
|
||||
set(PICO_BOARD pico_w)
|
||||
|
||||
elseif(OGXM_BOARD STREQUAL "W_ESP32")
|
||||
set(EN_ESP32 TRUE)
|
||||
set(EN_UART_BRIDGE TRUE)
|
||||
|
||||
else()
|
||||
message(FATAL_ERROR "Invalid OGXM_BOARD value. See options in src/board_config.h")
|
||||
|
||||
endif()
|
||||
|
||||
add_definitions(-DOGXM_BOARD=${OGXM_BOARD})
|
||||
|
||||
if(EN_USB_HOST)
|
||||
message(STATUS "USB host enabled.")
|
||||
add_compile_definitions(CONFIG_EN_USB_HOST=1)
|
||||
list(APPEND SOURCES_BOARD
|
||||
${SRC}/USBHost/tuh_callbacks.cpp
|
||||
|
||||
${SRC}/USBHost/HostDriver/DInput/DInput.cpp
|
||||
${SRC}/USBHost/HostDriver/PSClassic/PSClassic.cpp
|
||||
${SRC}/USBHost/HostDriver/SwitchWired/SwitchWired.cpp
|
||||
${SRC}/USBHost/HostDriver/SwitchPro/SwitchPro.cpp
|
||||
${SRC}/USBHost/HostDriver/PS5/PS5.cpp
|
||||
${SRC}/USBHost/HostDriver/PS4/PS4.cpp
|
||||
${SRC}/USBHost/HostDriver/PS3/PS3.cpp
|
||||
${SRC}/USBHost/HostDriver/XInput/XboxOG.cpp
|
||||
${SRC}/USBHost/HostDriver/XInput/XboxOne.cpp
|
||||
${SRC}/USBHost/HostDriver/XInput/Xbox360.cpp
|
||||
${SRC}/USBHost/HostDriver/XInput/Xbox360W.cpp
|
||||
${SRC}/USBHost/HostDriver/N64/N64.cpp
|
||||
${SRC}/USBHost/HostDriver/XInput/tuh_xinput/tuh_xinput.cpp
|
||||
${SRC}/USBHost/HostDriver/HIDGeneric/HIDGeneric.cpp
|
||||
${SRC}/USBHost/HostDriver/tuh_uni/tuh_uni.cpp
|
||||
|
||||
${SRC}/USBHost/HIDParser/HIDJoystick.cpp
|
||||
${SRC}/USBHost/HIDParser/HIDReportDescriptor.cpp
|
||||
${SRC}/USBHost/HIDParser/HIDReportDescriptorElements.cpp
|
||||
${SRC}/USBHost/HIDParser/HIDReportDescriptorUsages.cpp
|
||||
${SRC}/USBHost/HIDParser/HIDUtils.cpp
|
||||
)
|
||||
list(APPEND LIBS_BOARD
|
||||
tinyusb_host
|
||||
tinyusb_pico_pio_usb
|
||||
)
|
||||
endif()
|
||||
|
||||
if(EN_BLUETOOTH)
|
||||
add_compile_definitions(CONFIG_EN_BLUETOOTH=1)
|
||||
message(STATUS "Bluetooth enabled.")
|
||||
list(APPEND SOURCES_BOARD
|
||||
${SRC}/Bluepad32/Bluepad32.cpp
|
||||
)
|
||||
list(APPEND INC_DIRS_BOARD
|
||||
${SRC}
|
||||
${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/include
|
||||
${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/include
|
||||
)
|
||||
list(APPEND LIBS_BOARD
|
||||
pico_cyw43_arch_none
|
||||
pico_btstack_classic
|
||||
pico_btstack_cyw43
|
||||
bluepad32
|
||||
)
|
||||
endif()
|
||||
|
||||
if(EN_RGB)
|
||||
add_compile_definitions(CONFIG_EN_RGB=1)
|
||||
message(STATUS "RGB enabled.")
|
||||
list(APPEND SOURCES_BOARD
|
||||
${SRC}/Board/Pico_WS2812/WS2812.cpp
|
||||
)
|
||||
list(APPEND LIBS_BOARD
|
||||
hardware_pio
|
||||
)
|
||||
endif()
|
||||
|
||||
if(EN_4CH)
|
||||
add_compile_definitions(CONFIG_EN_4CH=1)
|
||||
message(STATUS "4CH enabled.")
|
||||
list(APPEND SOURCES_BOARD
|
||||
${SRC}/I2CDriver/i2c_driver_4ch.cpp
|
||||
)
|
||||
list(APPEND LIBS_BOARD
|
||||
hardware_i2c
|
||||
pico_i2c_slave
|
||||
)
|
||||
endif()
|
||||
|
||||
if(EN_ESP32)
|
||||
add_compile_definitions(CONFIG_EN_ESP32=1)
|
||||
message(STATUS "ESP32 enabled.")
|
||||
list(APPEND SOURCES_BOARD
|
||||
${SRC}/I2CDriver/i2c_driver_esp32.cpp
|
||||
)
|
||||
list(APPEND LIBS_BOARD
|
||||
hardware_i2c
|
||||
pico_i2c_slave
|
||||
)
|
||||
endif()
|
||||
|
||||
if(EN_UART_BRIDGE)
|
||||
add_compile_definitions(CONFIG_EN_UART_BRIDGE=1)
|
||||
message(STATUS "UART bridge enabled.")
|
||||
list(APPEND SOURCES_BOARD
|
||||
${SRC}/USBDevice/DeviceDriver/UARTBridge/UARTBridge.cpp
|
||||
${SRC}/USBDevice/DeviceDriver/UARTBridge/uart_bridge/uart_bridge.c
|
||||
)
|
||||
endif()
|
||||
|
||||
add_compile_definitions(PICO_FLASH_SIZE_BYTES=${FLASH_SIZE_MB}*1024*1024)
|
||||
add_compile_definitions(FIRMWARE_VERSION="${FW_VERSION}")
|
||||
|
||||
# Check for DVD dongle firmware
|
||||
if(EXISTS ${SRC}/USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid_xremote_rom.h)
|
||||
message(STATUS "XRemote ROM available.")
|
||||
add_definitions(-DXREMOTE_ROM_AVAILABLE)
|
||||
endif()
|
||||
|
||||
if(NOT EN_BLUETOOTH)
|
||||
add_compile_definitions(PICO_BOOT_STAGE2_CHOOSE_GENERIC_03H=1 PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64)
|
||||
endif()
|
||||
|
||||
pico_sdk_init()
|
||||
|
||||
add_executable(${FW_NAME} ${SOURCES_BOARD})
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
pico_enable_stdio_uart(${FW_NAME} 1)
|
||||
add_compile_definitions(LOG=2)
|
||||
target_compile_options(${FW_NAME} PRIVATE
|
||||
-Wall # Enable most warnings
|
||||
-Wextra # Enable extra warnings
|
||||
-Wconversion # Warn on type conversion issues
|
||||
-Wsign-conversion # Warn on sign conversion issues
|
||||
# -Werror # Treat warnings as errors
|
||||
)
|
||||
|
||||
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
add_compile_options(
|
||||
-O2 # Optimize for speed
|
||||
-mcpu=cortex-m0plus # Target ARM Cortex-M0+
|
||||
-mthumb # Use Thumb instruction set
|
||||
-ffunction-sections # Place each function in its own section
|
||||
-fdata-sections # Place each data item in its own section
|
||||
-fomit-frame-pointer # Omit frame pointers
|
||||
-finline-functions # Enable inlining
|
||||
)
|
||||
|
||||
add_link_options(
|
||||
-Wl,--gc-sections # Remove unused sections
|
||||
-flto # Link-Time Optimization
|
||||
)
|
||||
endif()
|
||||
|
||||
pico_set_program_name(${FW_NAME} ${FW_NAME})
|
||||
pico_set_program_version(${FW_NAME} ${FW_VERSION})
|
||||
|
||||
target_include_directories(${FW_NAME} PRIVATE ${SRC})
|
||||
|
||||
if(EN_RGB)
|
||||
pico_generate_pio_header(${FW_NAME} ${SRC}/Board/Pico_WS2812/WS2812.pio)
|
||||
endif()
|
||||
|
||||
include_directories(${INC_DIRS_BOARD})
|
||||
|
||||
if(EN_USB_HOST)
|
||||
add_subdirectory(${PICO_PIO_USB_PATH} ${CMAKE_BINARY_DIR}/Pico-PIO-USB)
|
||||
endif()
|
||||
|
||||
if(EN_BLUETOOTH)
|
||||
add_subdirectory(${BLUEPAD32_ROOT}/src/components/bluepad32 libbluepad32)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${FW_NAME} PRIVATE ${LIBS_BOARD})
|
||||
|
||||
set(EXE_FILENAME "${FW_NAME}-${FW_VERSION}-${OGXM_BOARD}")
|
||||
set_target_properties(${FW_NAME} PROPERTIES OUTPUT_NAME ${EXE_FILENAME})
|
||||
|
||||
pico_add_extra_outputs(${FW_NAME})
|
||||
17
Firmware/RP2040/cmake/get_pico_sdk.cmake
Normal file
17
Firmware/RP2040/cmake/get_pico_sdk.cmake
Normal file
@@ -0,0 +1,17 @@
|
||||
function(get_pico_sdk EXTERNAL_DIR)
|
||||
if(NOT DEFINED ENV{PICO_SDK_PATH})
|
||||
message("PICO_SDK_PATH not set, downloading Pico SDK...")
|
||||
set(PICO_SDK_PATH ${EXTERNAL_DIR}/pico-sdk PARENT_SCOPE)
|
||||
if(NOT EXISTS ${PICO_SDK_PATH})
|
||||
execute_process(
|
||||
COMMAND git clone --recursive https://github.com/raspberrypi/pico-sdk.git
|
||||
WORKING_DIRECTORY ${EXTERNAL_DIR}
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
message("Using PICO_SDK_PATH from environment: $ENV{PICO_SDK_PATH}")
|
||||
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
set(PICOTOOL_FETCH_FROM_GIT_PATH ${EXTERNAL_DIR}/picotool PARENT_SCOPE)
|
||||
endfunction()
|
||||
325
Firmware/RP2040/src/Bluepad32/Bluepad32.cpp
Normal file
325
Firmware/RP2040/src/Bluepad32/Bluepad32.cpp
Normal file
@@ -0,0 +1,325 @@
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <pico/mutex.h>
|
||||
#include <pico/cyw43_arch.h>
|
||||
|
||||
#include "btstack_run_loop.h"
|
||||
#include "uni.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "Bluepad32/Bluepad32.h"
|
||||
#include "Board/board_api.h"
|
||||
|
||||
#ifndef CONFIG_BLUEPAD32_PLATFORM_CUSTOM
|
||||
#error "Pico W must use BLUEPAD32_PLATFORM_CUSTOM"
|
||||
#endif
|
||||
|
||||
static_assert((CONFIG_BLUEPAD32_MAX_DEVICES == MAX_GAMEPADS), "Mismatch between BP32 and Gamepad max devices");
|
||||
|
||||
namespace bluepad32 {
|
||||
|
||||
static constexpr uint32_t FEEDBACK_TIME_MS = 200;
|
||||
static constexpr uint32_t LED_CHECK_TIME_MS = 500;
|
||||
|
||||
struct Device
|
||||
{
|
||||
bool connected{false};
|
||||
Gamepad* gamepad{nullptr};
|
||||
};
|
||||
|
||||
std::array<Device, MAX_GAMEPADS> devices_;
|
||||
|
||||
//This solves a null function pointer issue with bluepad32, device->report_parser.play_dual_rumble() becomes null before the disconnect callback
|
||||
void set_rumble(uni_hid_device_t* bp_device, uint16_t length, uint8_t rumble_l, uint8_t rumble_r)
|
||||
{
|
||||
if (!bp_device || !bp_device->report_parser.play_dual_rumble)
|
||||
{
|
||||
return;
|
||||
}
|
||||
switch (bp_device->controller_type)
|
||||
{
|
||||
case CONTROLLER_TYPE_XBoxOneController:
|
||||
uni_hid_parser_xboxone_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
|
||||
break;
|
||||
case CONTROLLER_TYPE_AndroidController:
|
||||
if (bp_device->vendor_id == UNI_HID_PARSER_STADIA_VID && bp_device->product_id == UNI_HID_PARSER_STADIA_PID)
|
||||
{
|
||||
uni_hid_parser_stadia_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
|
||||
}
|
||||
break;
|
||||
case CONTROLLER_TYPE_PSMoveController:
|
||||
uni_hid_parser_psmove_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
|
||||
break;
|
||||
case CONTROLLER_TYPE_PS3Controller:
|
||||
uni_hid_parser_ds3_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
|
||||
break;
|
||||
case CONTROLLER_TYPE_PS4Controller:
|
||||
uni_hid_parser_ds4_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
|
||||
break;
|
||||
case CONTROLLER_TYPE_PS5Controller:
|
||||
uni_hid_parser_ds5_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
|
||||
break;
|
||||
case CONTROLLER_TYPE_WiiController:
|
||||
uni_hid_parser_wii_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
|
||||
break;
|
||||
case CONTROLLER_TYPE_SwitchProController:
|
||||
case CONTROLLER_TYPE_SwitchJoyConRight:
|
||||
case CONTROLLER_TYPE_SwitchJoyConLeft:
|
||||
uni_hid_parser_switch_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void send_feedback_cb(btstack_timer_source *ts)
|
||||
{
|
||||
uni_hid_device_t* bp_device = nullptr;
|
||||
|
||||
for (uint8_t i = 0; i < devices_.size(); ++i)
|
||||
{
|
||||
if (!devices_[i].connected ||
|
||||
!(bp_device = uni_hid_device_get_instance_for_idx(i)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t rumble_l = devices_[i].gamepad->get_rumble_l().uint8();
|
||||
uint8_t rumble_r = devices_[i].gamepad->get_rumble_r().uint8();
|
||||
if (rumble_l > 0 || rumble_r > 0)
|
||||
{
|
||||
// bp_device->report_parser.play_dual_rumble(bp_device, 0, static_cast<uint16_t>(FEEDBACK_TIME_MS), rumble_l, rumble_r);
|
||||
set_rumble(bp_device, static_cast<uint16_t>(FEEDBACK_TIME_MS), rumble_l, rumble_r);
|
||||
}
|
||||
}
|
||||
|
||||
btstack_run_loop_set_timer(ts, FEEDBACK_TIME_MS);
|
||||
btstack_run_loop_add_timer(ts);
|
||||
}
|
||||
|
||||
static void check_led_cb(btstack_timer_source *ts)
|
||||
{
|
||||
static bool led_state = false;
|
||||
|
||||
led_state = !led_state;
|
||||
|
||||
board_api::set_led(any_connected() ? true : led_state);
|
||||
|
||||
btstack_run_loop_set_timer(ts, LED_CHECK_TIME_MS);
|
||||
btstack_run_loop_add_timer(ts);
|
||||
}
|
||||
|
||||
//BT Driver
|
||||
|
||||
static void init(int argc, const char** arg_V)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void init_complete_cb(void)
|
||||
{
|
||||
uni_bt_enable_new_connections_unsafe(true);
|
||||
|
||||
// Based on runtime condition, you can delete or list the stored BT keys.
|
||||
if (1)
|
||||
uni_bt_del_keys_unsafe();
|
||||
else
|
||||
uni_bt_list_keys_unsafe();
|
||||
|
||||
uni_property_dump_all();
|
||||
}
|
||||
|
||||
static uni_error_t device_discovered_cb(bd_addr_t addr, const char* name, uint16_t cod, uint8_t rssi)
|
||||
{
|
||||
if (!((cod & UNI_BT_COD_MINOR_MASK) & UNI_BT_COD_MINOR_GAMEPAD))
|
||||
{
|
||||
return UNI_ERROR_IGNORE_DEVICE;
|
||||
}
|
||||
return UNI_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static void device_connected_cb(uni_hid_device_t* device)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void device_disconnected_cb(uni_hid_device_t* device)
|
||||
{
|
||||
int idx = uni_hid_device_get_idx_for_instance(device);
|
||||
if (idx >= MAX_GAMEPADS || idx < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
devices_[idx].connected = false;
|
||||
devices_[idx].gamepad->reset_pad();
|
||||
}
|
||||
|
||||
static uni_error_t device_ready_cb(uni_hid_device_t* device)
|
||||
{
|
||||
int idx = uni_hid_device_get_idx_for_instance(device);
|
||||
if (idx >= MAX_GAMEPADS || idx < 0)
|
||||
{
|
||||
return UNI_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
devices_[idx].connected = true;
|
||||
return UNI_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static void oob_event_cb(uni_platform_oob_event_t event, void* data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static void controller_data_cb(uni_hid_device_t* device, uni_controller_t* controller)
|
||||
{
|
||||
static uni_gamepad_t prev_uni_gp[MAX_GAMEPADS] = {};
|
||||
|
||||
if (controller->klass != UNI_CONTROLLER_CLASS_GAMEPAD)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uni_gamepad_t *uni_gp = &controller->gamepad;
|
||||
int idx = uni_hid_device_get_idx_for_instance(device);
|
||||
|
||||
if (idx >= MAX_GAMEPADS || idx < 0 || std::memcmp(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t)) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Gamepad* gamepad = devices_[idx].gamepad;
|
||||
gamepad->reset_pad();
|
||||
|
||||
switch (uni_gp->dpad)
|
||||
{
|
||||
case DPAD_UP:
|
||||
gamepad->set_dpad_up();
|
||||
break;
|
||||
case DPAD_DOWN:
|
||||
gamepad->set_dpad_down();
|
||||
break;
|
||||
case DPAD_LEFT:
|
||||
gamepad->set_dpad_left();
|
||||
break;
|
||||
case DPAD_RIGHT:
|
||||
gamepad->set_dpad_right();
|
||||
break;
|
||||
case DPAD_UP | DPAD_RIGHT:
|
||||
gamepad->set_dpad_up_right();
|
||||
break;
|
||||
case DPAD_DOWN | DPAD_RIGHT:
|
||||
gamepad->set_dpad_down_right();
|
||||
break;
|
||||
case DPAD_DOWN | DPAD_LEFT:
|
||||
gamepad->set_dpad_down_left();
|
||||
break;
|
||||
case DPAD_UP | DPAD_LEFT:
|
||||
gamepad->set_dpad_up_left();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (uni_gp->buttons & BUTTON_A) gamepad->set_button_a();
|
||||
if (uni_gp->buttons & BUTTON_B) gamepad->set_button_b();
|
||||
if (uni_gp->buttons & BUTTON_X) gamepad->set_button_x();
|
||||
if (uni_gp->buttons & BUTTON_Y) gamepad->set_button_y();
|
||||
if (uni_gp->buttons & BUTTON_SHOULDER_L) gamepad->set_button_lb();
|
||||
if (uni_gp->buttons & BUTTON_SHOULDER_R) gamepad->set_button_rb();
|
||||
if (uni_gp->buttons & BUTTON_THUMB_L) gamepad->set_button_l3();
|
||||
if (uni_gp->buttons & BUTTON_THUMB_R) gamepad->set_button_r3();
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_BACK) gamepad->set_button_back();
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_START) gamepad->set_button_start();
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_SYSTEM) gamepad->set_button_sys();
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_CAPTURE) gamepad->set_button_misc();
|
||||
|
||||
gamepad->set_trigger_l_uint10(uni_gp->brake);
|
||||
gamepad->set_trigger_r_uint10(uni_gp->throttle);
|
||||
|
||||
gamepad->set_joystick_lx_int10(uni_gp->axis_x);
|
||||
gamepad->set_joystick_ly_int10(uni_gp->axis_y);
|
||||
gamepad->set_joystick_rx_int10(uni_gp->axis_rx);
|
||||
gamepad->set_joystick_ry_int10(uni_gp->axis_ry);
|
||||
|
||||
std::memcpy(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t));
|
||||
}
|
||||
|
||||
const uni_property_t* get_property_cb(uni_property_idx_t idx)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uni_platform* get_driver()
|
||||
{
|
||||
static uni_platform driver =
|
||||
{
|
||||
.name = "OGXMiniW",
|
||||
.init = init,
|
||||
.on_init_complete = init_complete_cb,
|
||||
.on_device_discovered = device_discovered_cb,
|
||||
.on_device_connected = device_connected_cb,
|
||||
.on_device_disconnected = device_disconnected_cb,
|
||||
.on_device_ready = device_ready_cb,
|
||||
.on_controller_data = controller_data_cb,
|
||||
.get_property = get_property_cb,
|
||||
.on_oob_event = oob_event_cb,
|
||||
};
|
||||
return &driver;
|
||||
}
|
||||
|
||||
//Public API
|
||||
|
||||
void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepads)
|
||||
{
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
|
||||
{
|
||||
devices_[i].gamepad = &gamepads[i];
|
||||
}
|
||||
}
|
||||
|
||||
void run_loop()
|
||||
{
|
||||
uni_platform_set_custom(get_driver());
|
||||
uni_init(0, nullptr);
|
||||
|
||||
btstack_timer_source_t feedback_timer;
|
||||
feedback_timer.process = send_feedback_cb;
|
||||
feedback_timer.context = nullptr;
|
||||
btstack_run_loop_set_timer(&feedback_timer, FEEDBACK_TIME_MS);
|
||||
btstack_run_loop_add_timer(&feedback_timer);
|
||||
|
||||
btstack_timer_source_t led_timer;
|
||||
led_timer.process = check_led_cb;
|
||||
led_timer.context = nullptr;
|
||||
btstack_run_loop_set_timer(&led_timer, LED_CHECK_TIME_MS);
|
||||
btstack_run_loop_add_timer(&led_timer);
|
||||
|
||||
btstack_run_loop_execute();
|
||||
}
|
||||
|
||||
std::array<bool, MAX_GAMEPADS> get_connected_map()
|
||||
{
|
||||
std::array<bool, MAX_GAMEPADS> mounted_map;
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
|
||||
{
|
||||
mounted_map[i] = devices_[i].connected;
|
||||
}
|
||||
return mounted_map;
|
||||
}
|
||||
|
||||
bool any_connected()
|
||||
{
|
||||
for (auto& device : devices_)
|
||||
{
|
||||
if (device.connected)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace bluepad32
|
||||
23
Firmware/RP2040/src/Bluepad32/Bluepad32.h
Normal file
23
Firmware/RP2040/src/Bluepad32/Bluepad32.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef _BLUEPAD_32_H_
|
||||
#define _BLUEPAD_32_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "Gamepad.h"
|
||||
#include "board_config.h"
|
||||
#include "UserSettings/UserProfile.h"
|
||||
|
||||
/* NOTE: Everything bluepad32/uni needs to be wrapped
|
||||
and kept away from tinyusb due to naming conflicts */
|
||||
|
||||
namespace bluepad32 {
|
||||
|
||||
void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepads);
|
||||
void run_loop();
|
||||
std::array<bool, MAX_GAMEPADS> get_connected_map();
|
||||
bool any_connected();
|
||||
|
||||
} //namespace bluepad32
|
||||
|
||||
#endif // _BLUEPAD_32_H_
|
||||
122
Firmware/RP2040/src/Board/Pico_WS2812/WS2812.cpp
Normal file
122
Firmware/RP2040/src/Board/Pico_WS2812/WS2812.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "Board/Pico_WS2812/WS2812.hpp"
|
||||
#include "WS2812.pio.h"
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
WS2812::WS2812(uint pin, uint length, PIO pio, uint sm) {
|
||||
initialize(pin, length, pio, sm, NONE, GREEN, RED, BLUE);
|
||||
}
|
||||
|
||||
WS2812::WS2812(uint pin, uint length, PIO pio, uint sm, DataFormat format) {
|
||||
switch (format) {
|
||||
case FORMAT_RGB:
|
||||
initialize(pin, length, pio, sm, NONE, RED, GREEN, BLUE);
|
||||
break;
|
||||
case FORMAT_GRB:
|
||||
initialize(pin, length, pio, sm, NONE, GREEN, RED, BLUE);
|
||||
break;
|
||||
case FORMAT_WRGB:
|
||||
initialize(pin, length, pio, sm, WHITE, RED, GREEN, BLUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WS2812::WS2812(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3) {
|
||||
initialize(pin, length, pio, sm, b1, b1, b2, b3);
|
||||
}
|
||||
|
||||
WS2812::WS2812(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3, DataByte b4) {
|
||||
initialize(pin, length, pio, sm, b1, b2, b3, b4);
|
||||
}
|
||||
|
||||
WS2812::~WS2812() {
|
||||
|
||||
}
|
||||
|
||||
void WS2812::initialize(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3, DataByte b4) {
|
||||
this->pin = pin;
|
||||
this->length = length;
|
||||
this->pio = pio;
|
||||
this->sm = sm;
|
||||
this->data.fill(0);
|
||||
this->bytes[0] = b1;
|
||||
this->bytes[1] = b2;
|
||||
this->bytes[2] = b3;
|
||||
this->bytes[3] = b4;
|
||||
uint offset = pio_add_program(pio, &ws2812_program);
|
||||
uint bits = (b1 == NONE ? 24 : 32);
|
||||
#ifdef DEBUG
|
||||
printf("WS2812 / Initializing SM %u with offset %X at pin %u and %u data bits...\n", sm, offset, pin, bits);
|
||||
#endif
|
||||
ws2812_program_init(pio, sm, offset, pin, 800000, bits);
|
||||
}
|
||||
|
||||
uint32_t WS2812::convertData(uint32_t rgbw) {
|
||||
uint32_t result = 0;
|
||||
for (uint b = 0; b < 4; b++) {
|
||||
switch (bytes[b]) {
|
||||
case RED:
|
||||
result |= (rgbw & 0xFF);
|
||||
break;
|
||||
case GREEN:
|
||||
result |= (rgbw & 0xFF00) >> 8;
|
||||
break;
|
||||
case BLUE:
|
||||
result |= (rgbw & 0xFF0000) >> 16;
|
||||
break;
|
||||
case WHITE:
|
||||
result |= (rgbw & 0xFF000000) >> 24;
|
||||
break;
|
||||
}
|
||||
result <<= 8;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void WS2812::setPixelColor(uint index, uint32_t color) {
|
||||
if (index < length) {
|
||||
data[index] = convertData(color);
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812::setPixelColor(uint index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
setPixelColor(index, RGB(red, green, blue));
|
||||
}
|
||||
|
||||
void WS2812::setPixelColor(uint index, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) {
|
||||
setPixelColor(index, RGBW(red, green, blue, white));
|
||||
}
|
||||
|
||||
void WS2812::fill(uint32_t color) {
|
||||
fill(color, 0, length);
|
||||
}
|
||||
|
||||
void WS2812::fill(uint32_t color, uint first) {
|
||||
fill(color, first, length-first);
|
||||
}
|
||||
|
||||
void WS2812::fill(uint32_t color, uint first, uint count) {
|
||||
uint last = (first + count);
|
||||
if (last > length) {
|
||||
last = length;
|
||||
}
|
||||
color = convertData(color);
|
||||
for (uint i = first; i < last; i++) {
|
||||
data[i] = color;
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812::show() {
|
||||
#ifdef DEBUG
|
||||
for (uint i = 0; i < length; i++) {
|
||||
printf("WS2812 / Put data: %08X\n", data[i]);
|
||||
}
|
||||
#endif
|
||||
for (uint i = 0; i < length; i++) {
|
||||
pio_sm_put_blocking(pio, sm, data[i]);
|
||||
}
|
||||
}
|
||||
58
Firmware/RP2040/src/Board/Pico_WS2812/WS2812.hpp
Normal file
58
Firmware/RP2040/src/Board/Pico_WS2812/WS2812.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef WS2812_H
|
||||
#define WS2812_H
|
||||
|
||||
#include <array>
|
||||
#include <pico/types.h>
|
||||
#include <hardware/pio.h>
|
||||
|
||||
class WS2812 {
|
||||
public:
|
||||
enum DataByte {
|
||||
NONE=0,
|
||||
RED=1,
|
||||
GREEN=2,
|
||||
BLUE=3,
|
||||
WHITE=4
|
||||
};
|
||||
enum DataFormat {
|
||||
FORMAT_RGB=0,
|
||||
FORMAT_GRB=1,
|
||||
FORMAT_WRGB=2
|
||||
};
|
||||
|
||||
WS2812(uint pin, uint length, PIO pio, uint sm);
|
||||
WS2812(uint pin, uint length, PIO pio, uint sm, DataFormat format);
|
||||
WS2812(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3);
|
||||
WS2812(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3, DataByte b4);
|
||||
~WS2812();
|
||||
|
||||
static uint32_t RGB(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
return (uint32_t)(blue) << 16 | (uint32_t)(green) << 8 | (uint32_t)(red);
|
||||
};
|
||||
|
||||
static uint32_t RGBW(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) {
|
||||
return (uint32_t)(white) << 24 | (uint32_t)(blue) << 16 | (uint32_t)(green) << 8 | (uint32_t)(red);
|
||||
}
|
||||
|
||||
void setPixelColor(uint index, uint32_t color);
|
||||
void setPixelColor(uint index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void setPixelColor(uint index, uint8_t red, uint8_t green, uint8_t blue, uint8_t white);
|
||||
void fill(uint32_t color);
|
||||
void fill(uint32_t color, uint first);
|
||||
void fill(uint32_t color, uint first, uint count);
|
||||
void show();
|
||||
|
||||
private:
|
||||
uint pin;
|
||||
uint length;
|
||||
PIO pio;
|
||||
uint sm;
|
||||
DataByte bytes[4];
|
||||
std::array<uint32_t, 20> data;
|
||||
|
||||
void initialize(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3, DataByte b4);
|
||||
uint32_t convertData(uint32_t rgbw);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
44
Firmware/RP2040/src/Board/Pico_WS2812/WS2812.pio
Normal file
44
Firmware/RP2040/src/Board/Pico_WS2812/WS2812.pio
Normal file
@@ -0,0 +1,44 @@
|
||||
.program ws2812
|
||||
.side_set 1
|
||||
|
||||
.define public T1 2
|
||||
.define public T2 5
|
||||
.define public T3 3
|
||||
|
||||
.lang_opt python sideset_init = pico.PIO.OUT_HIGH
|
||||
.lang_opt python out_init = pico.PIO.OUT_HIGH
|
||||
.lang_opt python out_shiftdir = 1
|
||||
|
||||
.wrap_target
|
||||
|
||||
bitloop:
|
||||
out x, 1 side 0 [T3 - 1]
|
||||
jmp !x send_zero side 1 [T1 - 1]
|
||||
send_one:
|
||||
jmp bitloop side 1 [T2 - 1]
|
||||
send_zero:
|
||||
nop side 0 [T2 - 1]
|
||||
|
||||
.wrap
|
||||
|
||||
% c-sdk {
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, uint bits) {
|
||||
|
||||
pio_gpio_init(pio, pin);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
|
||||
|
||||
pio_sm_config c = ws2812_program_get_default_config(offset);
|
||||
sm_config_set_sideset_pins(&c, pin);
|
||||
sm_config_set_out_shift(&c, false, true, bits);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
|
||||
int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
|
||||
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
%}
|
||||
189
Firmware/RP2040/src/Board/board_api.cpp
Normal file
189
Firmware/RP2040/src/Board/board_api.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
#include <pico/stdlib.h>
|
||||
#include <pico/mutex.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/watchdog.h>
|
||||
|
||||
#include "Board/board_api.h"
|
||||
#include "board_config.h"
|
||||
|
||||
#if defined(CONFIG_EN_BLUETOOTH)
|
||||
#include <pico/cyw43_arch.h>
|
||||
#endif // defined(CONFIG_EN_BLUETOOTH)
|
||||
|
||||
#if defined(CONFIG_EN_RGB)
|
||||
#include "WS2812.pio.h"
|
||||
#include "Board/Pico_WS2812/WS2812.hpp"
|
||||
#endif // defined(CONFIG_EN_RGB)
|
||||
|
||||
namespace board_api {
|
||||
|
||||
bool inited_ = false;
|
||||
mutex_t gpio_mutex_;
|
||||
|
||||
#if defined(CONFIG_EN_RGB)
|
||||
WS2812 ws2812_ = WS2812(RGB_PXL_PIN, 1, pio1, 0, WS2812::FORMAT_GRB);
|
||||
#endif // defined(CONFIG_EN_RGB)
|
||||
|
||||
void init_gpio()
|
||||
{
|
||||
if (inited_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!mutex_is_initialized(&gpio_mutex_))
|
||||
{
|
||||
mutex_init(&gpio_mutex_);
|
||||
}
|
||||
mutex_enter_blocking(&gpio_mutex_);
|
||||
|
||||
#ifdef VCC_EN_PIN
|
||||
gpio_init(VCC_EN_PIN);
|
||||
gpio_set_dir(VCC_EN_PIN, GPIO_OUT);
|
||||
gpio_put(VCC_EN_PIN, 1);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_EN_RGB)
|
||||
#ifdef RGB_PWR_PIN
|
||||
gpio_init(RGB_PWR_PIN);
|
||||
gpio_set_dir(RGB_PWR_PIN, GPIO_OUT);
|
||||
gpio_put(RGB_PWR_PIN, 1);
|
||||
#endif
|
||||
|
||||
set_led(false);
|
||||
|
||||
#elif defined(CONFIG_EN_BLUETOOTH)
|
||||
//
|
||||
|
||||
#elif defined(LED_INDICATOR_PIN)
|
||||
gpio_init(LED_INDICATOR_PIN);
|
||||
gpio_set_dir(LED_INDICATOR_PIN, GPIO_OUT);
|
||||
gpio_put(LED_INDICATOR_PIN, 0);
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_EN_ESP32)
|
||||
gpio_init(ESP_PROG_PIN);
|
||||
gpio_set_dir(ESP_PROG_PIN, GPIO_OUT);
|
||||
gpio_put(ESP_PROG_PIN, 1);
|
||||
|
||||
gpio_init(ESP_RST_PIN);
|
||||
gpio_set_dir(ESP_RST_PIN, GPIO_OUT);
|
||||
gpio_put(ESP_RST_PIN, 1);
|
||||
|
||||
#endif //defined(CONFIG_EN_ESP32)
|
||||
|
||||
#if defined(CONFIG_EN_UART_BRIDGE)
|
||||
gpio_init(MODE_SEL_PIN);
|
||||
gpio_set_dir(MODE_SEL_PIN, GPIO_IN);
|
||||
gpio_pull_up(MODE_SEL_PIN);
|
||||
|
||||
#endif // defined(CONFIG_EN_UART_BRIDGE)
|
||||
|
||||
inited_ = true;
|
||||
mutex_exit(&gpio_mutex_);
|
||||
}
|
||||
|
||||
void set_led(bool state)
|
||||
{
|
||||
if (!inited_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mutex_enter_blocking(&gpio_mutex_);
|
||||
|
||||
#if defined(CONFIG_EN_RGB)
|
||||
ws2812_.setPixelColor(0, state ? WS2812::RGB(0x00, 0xFF, 0x00) : WS2812::RGB(0xFF, 0, 0));
|
||||
ws2812_.show();
|
||||
|
||||
#elif defined(CONFIG_EN_BLUETOOTH)
|
||||
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, state ? 1 : 0);
|
||||
|
||||
#elif defined(LED_INDICATOR_PIN)
|
||||
gpio_put(LED_INDICATOR_PIN, state ? 1 : 0);
|
||||
|
||||
#endif //defined(CONFIG_EN_RGB)
|
||||
|
||||
mutex_exit(&gpio_mutex_);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_EN_UART_BRIDGE)
|
||||
bool uart_bridge_mode()
|
||||
{
|
||||
if (!inited_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mode = false;
|
||||
mutex_enter_blocking(&gpio_mutex_);
|
||||
|
||||
gpio_pull_up(MODE_SEL_PIN);
|
||||
if (gpio_get(MODE_SEL_PIN) == 0)
|
||||
{
|
||||
mode = true;
|
||||
}
|
||||
|
||||
mutex_exit(&gpio_mutex_);
|
||||
return mode;
|
||||
}
|
||||
#endif // defined(CONFIG_EN_UART_BRIDGE)
|
||||
|
||||
#if defined(CONFIG_EN_ESP32)
|
||||
void reset_esp32_unsafe()
|
||||
{
|
||||
gpio_put(ESP_RST_PIN, 0);
|
||||
sleep_ms(500);
|
||||
gpio_put(ESP_RST_PIN, 1);
|
||||
sleep_ms(250);
|
||||
}
|
||||
|
||||
void reset_esp32()
|
||||
{
|
||||
if (!inited_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mutex_enter_blocking(&gpio_mutex_);
|
||||
reset_esp32_unsafe();
|
||||
mutex_exit(&gpio_mutex_);
|
||||
}
|
||||
|
||||
void enter_esp32_prog_mode()
|
||||
{
|
||||
if (!inited_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mutex_enter_blocking(&gpio_mutex_);
|
||||
|
||||
gpio_put(ESP_PROG_PIN, 1);
|
||||
sleep_ms(250);
|
||||
gpio_put(ESP_PROG_PIN, 0);
|
||||
sleep_ms(250);
|
||||
|
||||
reset_esp32_unsafe();
|
||||
|
||||
gpio_put(ESP_PROG_PIN, 1);
|
||||
|
||||
mutex_exit(&gpio_mutex_);
|
||||
}
|
||||
#endif // defined(CONFIG_EN_ESP32)
|
||||
|
||||
void reboot()
|
||||
{
|
||||
#define AIRCR_REG (*((volatile uint32_t *)(0xE000ED0C)))
|
||||
#define AIRCR_SYSRESETREQ (1 << 2)
|
||||
#define AIRCR_VECTKEY (0x5FA << 16)
|
||||
|
||||
AIRCR_REG = AIRCR_VECTKEY | AIRCR_SYSRESETREQ;
|
||||
while(1);
|
||||
|
||||
// watchdog_enable(500, true); // 10-second timeout
|
||||
// while (true)
|
||||
// {
|
||||
// // Your main loop logic
|
||||
// watchdog_update(); // Reset the watchdog timer
|
||||
// }
|
||||
}
|
||||
|
||||
} // namespace board_api
|
||||
17
Firmware/RP2040/src/Board/board_api.h
Normal file
17
Firmware/RP2040/src/Board/board_api.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _BOARD_API_H_
|
||||
#define _BOARD_API_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace board_api
|
||||
{
|
||||
void init_gpio();
|
||||
void set_led(bool state);
|
||||
void reboot();
|
||||
|
||||
bool uart_bridge_mode();
|
||||
void reset_esp32();
|
||||
void enter_esp32_prog_mode();
|
||||
}
|
||||
|
||||
#endif // _BOARD_API_H_
|
||||
81
Firmware/RP2040/src/Descriptors/CDCDev.h
Normal file
81
Firmware/RP2040/src/Descriptors/CDCDev.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#ifndef _CDC_DEV_DESCRIPTORS_H_
|
||||
#define _CDC_DEV_DESCRIPTORS_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
namespace CDCDesc
|
||||
{
|
||||
#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
|
||||
|
||||
static const tusb_desc_device_t DEVICE_DESCRIPTORS =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = 0xCafe,
|
||||
.idProduct = USB_PID,
|
||||
.bcdDevice = 0x0100,
|
||||
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
enum Itf
|
||||
{
|
||||
NUM_CDC = 0,
|
||||
NUM_CDC_DATA,
|
||||
NUM_TOTAL
|
||||
};
|
||||
|
||||
#define EPNUM_CDC_NOTIF 0x81
|
||||
#define EPNUM_CDC_OUT 0x02
|
||||
#define EPNUM_CDC_IN 0x82
|
||||
|
||||
static constexpr int CONFIG_LEN = (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN);
|
||||
|
||||
static const uint8_t CONFIGURATION_DESCRIPTORS[] =
|
||||
{
|
||||
TUD_CONFIG_DESCRIPTOR(1, Itf::NUM_TOTAL, 0, CONFIG_LEN, 0x00, 500),
|
||||
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,
|
||||
};
|
||||
|
||||
static const char *STRING_DESCRIPTORS[] =
|
||||
{
|
||||
(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];
|
||||
|
||||
}; // namespace CDCDesc
|
||||
|
||||
#endif // _CDC_DEV_DESCRIPTORS_H_
|
||||
297
Firmware/RP2040/src/Descriptors/DInput.h
Normal file
297
Firmware/RP2040/src/Descriptors/DInput.h
Normal file
@@ -0,0 +1,297 @@
|
||||
#ifndef _DINPUT_DESCRIPTORS_H_
|
||||
#define _DINPUT_DESCRIPTORS_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#include "board_config.h"
|
||||
|
||||
namespace DInput
|
||||
{
|
||||
static constexpr uint8_t DPAD_MASK = 0x0F;
|
||||
static constexpr uint8_t AXIS_MIN = 0x00;
|
||||
static constexpr uint8_t AXIS_MID = 0x80;
|
||||
static constexpr uint8_t AXIS_MAX = 0xFF;
|
||||
|
||||
namespace Buttons0
|
||||
{
|
||||
static constexpr uint8_t SQUARE = 0x01;
|
||||
static constexpr uint8_t CROSS = 0x02;
|
||||
static constexpr uint8_t CIRCLE = 0x04;
|
||||
static constexpr uint8_t TRIANGLE = 0x08;
|
||||
static constexpr uint8_t L1 = 0x10;
|
||||
static constexpr uint8_t R1 = 0x20;
|
||||
static constexpr uint8_t L2 = 0x40;
|
||||
static constexpr uint8_t R2 = 0x80;
|
||||
};
|
||||
|
||||
namespace Buttons1
|
||||
{
|
||||
static constexpr uint8_t SELECT = 0x01;
|
||||
static constexpr uint8_t START = 0x02;
|
||||
static constexpr uint8_t L3 = 0x04;
|
||||
static constexpr uint8_t R3 = 0x08;
|
||||
static constexpr uint8_t PS = 0x10;
|
||||
static constexpr uint8_t TP = 0x20;
|
||||
};
|
||||
|
||||
namespace DPad
|
||||
{
|
||||
static constexpr uint8_t UP = 0x00;
|
||||
static constexpr uint8_t UP_RIGHT = 0x01;
|
||||
static constexpr uint8_t RIGHT = 0x02;
|
||||
static constexpr uint8_t DOWN_RIGHT = 0x03;
|
||||
static constexpr uint8_t DOWN = 0x04;
|
||||
static constexpr uint8_t DOWN_LEFT = 0x05;
|
||||
static constexpr uint8_t LEFT = 0x06;
|
||||
static constexpr uint8_t UP_LEFT = 0x07;
|
||||
static constexpr uint8_t CENTER = 0x08;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct InReport
|
||||
{
|
||||
uint8_t buttons[2];
|
||||
uint8_t dpad;
|
||||
|
||||
uint8_t joystick_lx;
|
||||
uint8_t joystick_ly;
|
||||
uint8_t joystick_rx;
|
||||
uint8_t joystick_ry;
|
||||
|
||||
uint8_t right_axis;
|
||||
uint8_t left_axis;
|
||||
uint8_t up_axis;
|
||||
uint8_t down_axis;
|
||||
|
||||
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;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(InReport) == 19, "DInput::InReport is misaligned");
|
||||
|
||||
static const uint8_t STRING_DESC_LANGUAGE[] = { 0x09, 0x04 };
|
||||
static const uint8_t STRING_MANUFACTURER[] = "SHANWAN";
|
||||
static const uint8_t STRING_PRODUCT[] = "2In1 USB Joystick";
|
||||
static const uint8_t STRING_VERSION[] = "1.0";
|
||||
|
||||
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
|
||||
{
|
||||
STRING_DESC_LANGUAGE,
|
||||
STRING_MANUFACTURER,
|
||||
STRING_PRODUCT,
|
||||
STRING_VERSION
|
||||
};
|
||||
|
||||
static const uint8_t DEVICE_DESCRIPTORS[] =
|
||||
{
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x10, 0x01, // bcdUSB 1.10
|
||||
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x40, // bMaxPacketSize0 64
|
||||
0x63, 0x25, // idVendor 0x2563
|
||||
0x75, 0x05, // idProduct 0x0575
|
||||
0x00, 0x02, // bcdDevice 4.00
|
||||
0x01, // iManufacturer (String Index)
|
||||
0x02, // iProduct (String Index)
|
||||
0x00, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
static const uint8_t REPORT_DESCRIPTORS[] =
|
||||
{
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x05, // Usage (Game Pad)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x45, 0x01, // Physical Maximum (1)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x0D, // Report Count (13)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x0D, // Usage Maximum (0x0D)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x03, // Report Count (3)
|
||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x25, 0x07, // Logical Maximum (7)
|
||||
0x46, 0x3B, 0x01, // Physical Maximum (315)
|
||||
0x75, 0x04, // Report Size (4)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
|
||||
0x09, 0x39, // Usage (Hat switch)
|
||||
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
|
||||
0x65, 0x00, // Unit (None)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x46, 0xFF, 0x00, // Physical Maximum (255)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x09, 0x32, // Usage (Z)
|
||||
0x09, 0x35, // Usage (Rz)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
|
||||
0x09, 0x20, // Usage (0x20)
|
||||
0x09, 0x21, // Usage (0x21)
|
||||
0x09, 0x22, // Usage (0x22)
|
||||
0x09, 0x23, // Usage (0x23)
|
||||
0x09, 0x24, // Usage (0x24)
|
||||
0x09, 0x25, // Usage (0x25)
|
||||
0x09, 0x26, // Usage (0x26)
|
||||
0x09, 0x27, // Usage (0x27)
|
||||
0x09, 0x28, // Usage (0x28)
|
||||
0x09, 0x29, // Usage (0x29)
|
||||
0x09, 0x2A, // Usage (0x2A)
|
||||
0x09, 0x2B, // Usage (0x2B)
|
||||
0x95, 0x0C, // Report Count (12)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x0A, 0x21, 0x26, // Usage (0x2621)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x0A, 0x21, 0x26, // Usage (0x2621)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x26, 0xFF, 0x03, // Logical Maximum (1023)
|
||||
0x46, 0xFF, 0x03, // Physical Maximum (1023)
|
||||
0x09, 0x2C, // Usage (0x2C)
|
||||
0x09, 0x2D, // Usage (0x2D)
|
||||
0x09, 0x2E, // Usage (0x2E)
|
||||
0x09, 0x2F, // Usage (0x2F)
|
||||
0x75, 0x10, // Report Size (16)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
};
|
||||
|
||||
// uint8_t const CONFIGURATION_DESCRIPTORS[] =
|
||||
// {
|
||||
// 0x09, // bLength
|
||||
// 0x02, // bDescriptorType (Configuration)
|
||||
// 0x29, 0x00, // wTotalLength 41
|
||||
// 0x01, // bNumInterfaces 1
|
||||
// 0x01, // bConfigurationValue
|
||||
// 0x00, // iConfiguration (String Index)
|
||||
// 0x80, // bmAttributes
|
||||
// 0xFA, // bMaxPower 500mA
|
||||
|
||||
// 0x09, // bLength
|
||||
// 0x04, // bDescriptorType (Interface)
|
||||
// 0x00, // bInterfaceNumber 0
|
||||
// 0x00, // bAlternateSetting
|
||||
// 0x02, // bNumEndpoints 2
|
||||
// 0x03, // bInterfaceClass
|
||||
// 0x00, // bInterfaceSubClass
|
||||
// 0x00, // bInterfaceProtocol
|
||||
// 0x00, // iInterface (String Index)
|
||||
|
||||
// 0x09, // bLength
|
||||
// 0x21, // bDescriptorType (HID)
|
||||
// 0x10, 0x01, // bcdHID 1.10
|
||||
// 0x00, // bCountryCode
|
||||
// 0x01, // bNumDescriptors
|
||||
// 0x22, // bDescriptorType[0] (HID)
|
||||
// 0x89, 0x00, // wDescriptorLength[0] 137
|
||||
|
||||
// 0x07, // bLength
|
||||
// 0x05, // bDescriptorType (Endpoint)
|
||||
// 0x02, // bEndpointAddress (OUT/H2D)
|
||||
// 0x03, // bmAttributes (Interrupt)
|
||||
// 0x20, 0x00, // wMaxPacketSize 32
|
||||
// 0x0A, // bInterval 10 (unit depends on device speed)
|
||||
|
||||
// 0x07, // bLength
|
||||
// 0x05, // bDescriptorType (Endpoint)
|
||||
// 0x81, // bEndpointAddress (IN/D2H)
|
||||
// 0x03, // bmAttributes (Interrupt)
|
||||
// 0x20, 0x00, // wMaxPacketSize 32
|
||||
// 0x0A, // bInterval 10 (unit depends on device speed)
|
||||
// };
|
||||
|
||||
enum Itf
|
||||
{
|
||||
NUM_HID1 = 0,
|
||||
#if MAX_GAMEPADS > 1
|
||||
NUM_HID2,
|
||||
#endif
|
||||
#if MAX_GAMEPADS > 2
|
||||
NUM_HID3,
|
||||
#endif
|
||||
#if MAX_GAMEPADS > 3
|
||||
NUM_HID4,
|
||||
#endif
|
||||
NUM_TOTAL
|
||||
};
|
||||
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + (TUD_HID_DESC_LEN * MAX_GAMEPADS))
|
||||
|
||||
// #define EPNUM_HID1 0x81
|
||||
// #define EPNUM_HID2 0x82
|
||||
// #define EPNUM_HID3 0x83
|
||||
// #define EPNUM_HID4 0x84
|
||||
|
||||
uint8_t const CONFIGURATION_DESCRIPTORS[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR( 1,
|
||||
Itf::NUM_TOTAL,
|
||||
0,
|
||||
CONFIG_TOTAL_LEN,
|
||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
|
||||
500),
|
||||
|
||||
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR( Itf::NUM_HID1,
|
||||
0,
|
||||
HID_ITF_PROTOCOL_NONE,
|
||||
sizeof(REPORT_DESCRIPTORS),
|
||||
(0x80 | (Itf::NUM_HID1 + 1)),
|
||||
CFG_TUD_HID_EP_BUFSIZE,
|
||||
1),
|
||||
#if MAX_GAMEPADS > 1
|
||||
TUD_HID_DESCRIPTOR( Itf::NUM_HID2,
|
||||
0,
|
||||
HID_ITF_PROTOCOL_NONE,
|
||||
sizeof(REPORT_DESCRIPTORS),
|
||||
(0x80 | (Itf::NUM_HID2 + 1)),
|
||||
CFG_TUD_HID_EP_BUFSIZE,
|
||||
1),
|
||||
#endif
|
||||
#if MAX_GAMEPADS > 2
|
||||
TUD_HID_DESCRIPTOR( Itf::NUM_HID3,
|
||||
0,
|
||||
HID_ITF_PROTOCOL_NONE,
|
||||
sizeof(REPORT_DESCRIPTORS),
|
||||
(0x80 | (Itf::NUM_HID3 + 1)),
|
||||
CFG_TUD_HID_EP_BUFSIZE,
|
||||
1),
|
||||
#endif
|
||||
#if MAX_GAMEPADS > 3
|
||||
TUD_HID_DESCRIPTOR( Itf::NUM_HID4,
|
||||
0,
|
||||
HID_ITF_PROTOCOL_NONE,
|
||||
sizeof(REPORT_DESCRIPTORS),
|
||||
(0x80 | (Itf::NUM_HID4 + 1)),
|
||||
CFG_TUD_HID_EP_BUFSIZE,
|
||||
1)
|
||||
#endif
|
||||
};
|
||||
|
||||
}; // namespace DInput
|
||||
|
||||
#endif // _DINPUT_DESCRIPTORS_H_
|
||||
50
Firmware/RP2040/src/Descriptors/N64.h
Normal file
50
Firmware/RP2040/src/Descriptors/N64.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef _N64_DESCRIPTORS_H_
|
||||
#define _N64_DESCRIPTORS_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace N64
|
||||
{
|
||||
static constexpr uint16_t DPAD_MASK = 0x0F;
|
||||
static constexpr uint8_t JOY_MIN = 0x00;
|
||||
static constexpr uint8_t JOY_MID = 0x80;
|
||||
static constexpr uint8_t JOY_MAX = 0xFF;
|
||||
|
||||
namespace Buttons
|
||||
{
|
||||
static constexpr uint16_t DPAD_UP = 0x00;
|
||||
static constexpr uint16_t DPAD_UP_RIGHT = 0x01;
|
||||
static constexpr uint16_t DPAD_RIGHT = 0x02;
|
||||
static constexpr uint16_t DPAD_RIGHT_DOWN = 0x03;
|
||||
static constexpr uint16_t DPAD_DOWN = 0x04;
|
||||
static constexpr uint16_t DPAD_DOWN_LEFT = 0x05;
|
||||
static constexpr uint16_t DPAD_LEFT = 0x06;
|
||||
static constexpr uint16_t DPAD_LEFT_UP = 0x07;
|
||||
static constexpr uint16_t DPAD_NONE = 0x08;
|
||||
|
||||
static constexpr uint16_t C_UP = (1 << 4);
|
||||
static constexpr uint16_t C_RIGHT = (1 << 5);
|
||||
static constexpr uint16_t C_DOWN = (1 << 6);
|
||||
static constexpr uint16_t C_LEFT = (1 << 7);
|
||||
static constexpr uint16_t L = (1 << 8);
|
||||
static constexpr uint16_t R = (1 << 9);
|
||||
static constexpr uint16_t A = (1 << 10);
|
||||
static constexpr uint16_t Z = (1 << 11);
|
||||
static constexpr uint16_t B = (1 << 12);
|
||||
static constexpr uint16_t START = (1 << 13);
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct InReport
|
||||
{
|
||||
uint8_t joystick_x;
|
||||
uint8_t joystick_y;
|
||||
uint8_t padding[3];
|
||||
uint16_t buttons;
|
||||
};
|
||||
static_assert(sizeof(InReport) == 7, "N64 InReport size is not correct");
|
||||
#pragma pack(pop)
|
||||
|
||||
}; // namespace N64
|
||||
|
||||
#endif // _N64_DESCRIPTORS_H_
|
||||
495
Firmware/RP2040/src/Descriptors/PS3.h
Normal file
495
Firmware/RP2040/src/Descriptors/PS3.h
Normal file
@@ -0,0 +1,495 @@
|
||||
#ifndef _PS3_DESCRIPTORS_H_
|
||||
#define _PS3_DESCRIPTORS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstring>
|
||||
#include <pico/rand.h>
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
namespace PS3
|
||||
{
|
||||
static constexpr uint8_t MAGIC_BYTES[8] = { 0x21, 0x26, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
|
||||
static constexpr uint8_t JOYSTICK_MID = 0x80;
|
||||
static constexpr uint16_t SIXAXIS_MID = 0xFF01;
|
||||
|
||||
namespace ReportID
|
||||
{
|
||||
static constexpr uint8_t FEATURE_01 = 0x01;
|
||||
static constexpr uint8_t FEATURE_EF = 0xEF;
|
||||
static constexpr uint8_t GET_PAIRING_INFO = 0xF2;
|
||||
static constexpr uint8_t FEATURE_F4 = 0xF4;
|
||||
static constexpr uint8_t FEATURE_F5 = 0xF5;
|
||||
static constexpr uint8_t FEATURE_F7 = 0xF7;
|
||||
static constexpr uint8_t FEATURE_F8 = 0xF8;
|
||||
};
|
||||
|
||||
namespace PlugState
|
||||
{
|
||||
static constexpr uint8_t PLUGGED = 0x02;
|
||||
static constexpr uint8_t UNPLUGGED = 0x03;
|
||||
};
|
||||
|
||||
namespace PowerState
|
||||
{
|
||||
static constexpr uint8_t CHARGING = 0xEE;
|
||||
static constexpr uint8_t NOT_CHARGING = 0xF1;
|
||||
static constexpr uint8_t SHUTDOWN = 0x01;
|
||||
static constexpr uint8_t DISCHARGING = 0x02;
|
||||
static constexpr uint8_t LOW = 0x03;
|
||||
static constexpr uint8_t HIGH = 0x04;
|
||||
static constexpr uint8_t FULL = 0x05;
|
||||
};
|
||||
|
||||
namespace RumbleState
|
||||
{
|
||||
static constexpr uint8_t WIRED_RUMBLE = 0x10;
|
||||
static constexpr uint8_t WIRED_NO_RUMBLE = 0x12;
|
||||
static constexpr uint8_t WIRELESS_RUMBLE = 0x14;
|
||||
static constexpr uint8_t WIRELESS_NO_RUMBLE = 0x16;
|
||||
};
|
||||
|
||||
namespace Buttons0
|
||||
{
|
||||
static constexpr uint8_t SELECT = 0x01;
|
||||
static constexpr uint8_t L3 = 0x02;
|
||||
static constexpr uint8_t R3 = 0x04;
|
||||
static constexpr uint8_t START = 0x08;
|
||||
static constexpr uint8_t DPAD_UP = 0x10;
|
||||
static constexpr uint8_t DPAD_RIGHT = 0x20;
|
||||
static constexpr uint8_t DPAD_DOWN = 0x40;
|
||||
static constexpr uint8_t DPAD_LEFT = 0x80;
|
||||
};
|
||||
|
||||
namespace Buttons1
|
||||
{
|
||||
static constexpr uint8_t L2 = 0x01;
|
||||
static constexpr uint8_t R2 = 0x02;
|
||||
static constexpr uint8_t L1 = 0x04;
|
||||
static constexpr uint8_t R1 = 0x08;
|
||||
static constexpr uint8_t TRIANGLE = 0x10;
|
||||
static constexpr uint8_t CIRCLE = 0x20;
|
||||
static constexpr uint8_t CROSS = 0x40;
|
||||
static constexpr uint8_t SQUARE = 0x80;
|
||||
};
|
||||
|
||||
namespace Buttons2
|
||||
{
|
||||
static constexpr uint8_t PS = 0x01;
|
||||
static constexpr uint8_t TP = 0x02;
|
||||
};
|
||||
|
||||
const uint8_t DEFAULT_OUT_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
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct InReport
|
||||
{
|
||||
uint8_t report_id;
|
||||
uint8_t reserved0;
|
||||
|
||||
uint8_t buttons[3];
|
||||
|
||||
uint8_t reserved1;
|
||||
|
||||
uint8_t joystick_lx;
|
||||
uint8_t joystick_ly;
|
||||
uint8_t joystick_rx;
|
||||
uint8_t joystick_ry;
|
||||
|
||||
uint8_t reserved2[2];
|
||||
|
||||
uint8_t move_power_status;
|
||||
|
||||
uint8_t reserved3;
|
||||
|
||||
uint8_t up_axis;
|
||||
uint8_t right_axis;
|
||||
uint8_t down_axis;
|
||||
uint8_t left_axis;
|
||||
|
||||
uint8_t l2_axis;
|
||||
uint8_t r2_axis;
|
||||
uint8_t l1_axis;
|
||||
uint8_t r1_axis;
|
||||
|
||||
uint8_t triangle_axis;
|
||||
uint8_t circle_axis;
|
||||
uint8_t cross_axis;
|
||||
uint8_t square_axis;
|
||||
|
||||
uint8_t reserved4[3];
|
||||
|
||||
uint8_t plugged;
|
||||
uint8_t power_status;
|
||||
uint8_t rumble_status;
|
||||
|
||||
uint8_t reserved5[9];
|
||||
|
||||
uint16_t acceler_x;
|
||||
uint16_t acceler_y;
|
||||
uint16_t acceler_z;
|
||||
|
||||
uint16_t velocity_z;
|
||||
|
||||
InReport()
|
||||
{
|
||||
std::memset(this, 0, sizeof(InReport));
|
||||
report_id = 0x01;
|
||||
joystick_lx = JOYSTICK_MID;
|
||||
joystick_ly = JOYSTICK_MID;
|
||||
joystick_rx = JOYSTICK_MID;
|
||||
joystick_ry = JOYSTICK_MID;
|
||||
plugged = PlugState::PLUGGED;
|
||||
power_status = PowerState::FULL;
|
||||
rumble_status = RumbleState::WIRED_RUMBLE;
|
||||
acceler_x = acceler_y = acceler_z = velocity_z = SIXAXIS_MID;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(InReport) == 49, "PS3::InReport size mismatch");
|
||||
|
||||
struct LEDs {
|
||||
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%) */
|
||||
};
|
||||
static_assert(sizeof(LEDs) == 5, "PS3::LEDs size mismatch");
|
||||
|
||||
struct Rumble {
|
||||
uint8_t reserved;
|
||||
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 */
|
||||
};
|
||||
static_assert(sizeof(Rumble) == 5, "PS3::Rumble size mismatch");
|
||||
|
||||
struct OutReport
|
||||
{
|
||||
struct Rumble rumble;
|
||||
uint8_t padding[4];
|
||||
uint8_t leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
|
||||
struct LEDs led[4]; /* LEDx at (4 - x) */
|
||||
struct LEDs reserved; /* LED5, not actually soldered */
|
||||
|
||||
OutReport()
|
||||
{
|
||||
std::memcpy(this, DEFAULT_OUT_REPORT, sizeof(OutReport));
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(OutReport) == 35, "PS3::OutReport size mismatch");
|
||||
static_assert(sizeof(OutReport) == sizeof(DEFAULT_OUT_REPORT));
|
||||
|
||||
struct BTInfo
|
||||
{
|
||||
uint8_t reserved0[2];
|
||||
uint8_t device_address[7]; // leading zero followed by address
|
||||
uint8_t host_address[7]; // leading zero followed by address
|
||||
uint8_t reserved1;
|
||||
|
||||
BTInfo()
|
||||
{
|
||||
std::memset(this, 0, sizeof(BTInfo));
|
||||
std::memset(reserved0, 0xFF, sizeof(reserved0));
|
||||
uint8_t addr[] = { 0x00, 0x20, 0x40, 0xCE, 0x00, 0x00, 0x00 };
|
||||
std::memcpy(device_address, addr, sizeof(addr));
|
||||
for (uint8_t addr = 0; addr < 3; addr++)
|
||||
{
|
||||
device_address[4 + addr] = static_cast<uint8_t>(get_rand_32() % 0xFF);
|
||||
}
|
||||
for (uint8_t addr = 0; addr < 6; addr++)
|
||||
{
|
||||
host_address[1 + addr] = static_cast<uint8_t>(get_rand_32() % 0xFF);
|
||||
}
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(BTInfo) == 17, "PS3::BTInfo size mismatch");
|
||||
#pragma pack(pop)
|
||||
|
||||
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
|
||||
static const uint8_t STRING_MANUFACTURER[] = "Sony";
|
||||
static const uint8_t STRING_PRODUCT[] = "PLAYSTATION(R)3 Controller";
|
||||
static const uint8_t STRING_VERSION[] = "1.0";
|
||||
|
||||
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
|
||||
{
|
||||
STRING_LANGUAGE,
|
||||
STRING_MANUFACTURER,
|
||||
STRING_PRODUCT,
|
||||
STRING_VERSION
|
||||
};
|
||||
|
||||
static const uint8_t DEVICE_DESCRIPTORS[] =
|
||||
{
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x00, 0x02, // bcdUSB 2.00
|
||||
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x40, // bMaxPacketSize0 64
|
||||
0x4C, 0x05, // idVendor 0x054C
|
||||
0x68, 0x02, // idProduct 0x0268
|
||||
0x00, 0x01, // bcdDevice 2.00
|
||||
0x01, // iManufacturer (String Index)
|
||||
0x02, // iProduct (String Index)
|
||||
0x00, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
static const uint8_t REPORT_DESCRIPTORS[] =
|
||||
{
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x04, // Usage (Joystick)
|
||||
0xA1, 0x01, // Collection (Physical)
|
||||
0xA1, 0x02, // Collection (Application)
|
||||
0x85, 0x01, // Report ID (1)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// NOTE: reserved byte
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x13, // Report Count (19)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x45, 0x01, // Physical Maximum (1)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x13, // Usage Maximum (0x13)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x0D, // Report Count (13)
|
||||
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// NOTE: 32 bit integer, where 0:18 are buttons and 19:31 are reserved
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xA1, 0x00, // Collection (Undefined)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x46, 0xFF, 0x00, // Physical Maximum (255)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x09, 0x32, // Usage (Z)
|
||||
0x09, 0x35, // Usage (Rz)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// NOTE: four joysticks
|
||||
0xC0, // End Collection
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x27, // Report Count (39)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x30, // Report Count (48)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x30, // Report Count (48)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
0xA1, 0x02, // Collection (Application)
|
||||
0x85, 0x02, // Report ID (2)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x30, // Report Count (48)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
0xA1, 0x02, // Collection (Application)
|
||||
0x85, 0xEE, // Report ID (238)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x30, // Report Count (48)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
0xA1, 0x02, // Collection (Application)
|
||||
0x85, 0xEF, // Report ID (239)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x30, // Report Count (48)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
0xC0, // End Collection
|
||||
// 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
// 0x09, 0x04, // Usage (Joystick)
|
||||
// 0xA1, 0x01, // Collection (Application)
|
||||
// 0xA1, 0x02, // Collection (Logical)
|
||||
// 0x85, 0x01, // Report ID (1)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x01, // Report Count (1)
|
||||
// 0x15, 0x00, // Logical Minimum (0)
|
||||
// 0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
// 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0x75, 0x01, // Report Size (1)
|
||||
// 0x95, 0x13, // Report Count (19)
|
||||
// 0x15, 0x00, // Logical Minimum (0)
|
||||
// 0x25, 0x01, // Logical Maximum (1)
|
||||
// 0x35, 0x00, // Physical Minimum (0)
|
||||
// 0x45, 0x01, // Physical Maximum (1)
|
||||
// 0x05, 0x09, // Usage Page (Button)
|
||||
// 0x19, 0x01, // Usage Minimum (0x01)
|
||||
// 0x29, 0x13, // Usage Maximum (0x13)
|
||||
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0x75, 0x01, // Report Size (1)
|
||||
// 0x95, 0x0D, // Report Count (13)
|
||||
// 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
|
||||
// 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0x15, 0x00, // Logical Minimum (0)
|
||||
// 0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
// 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
// 0x09, 0x01, // Usage (Pointer)
|
||||
// 0xA1, 0x00, // Collection (Physical)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x04, // Report Count (4)
|
||||
// 0x35, 0x00, // Physical Minimum (0)
|
||||
// 0x46, 0xFF, 0x00, // Physical Maximum (255)
|
||||
// 0x09, 0x30, // Usage (X)
|
||||
// 0x09, 0x31, // Usage (Y)
|
||||
// 0x09, 0x32, // Usage (Z)
|
||||
// 0x09, 0x35, // Usage (Rz)
|
||||
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0xC0, // End Collection
|
||||
// 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x27, // Report Count (39)
|
||||
// 0x09, 0x01, // Usage (Pointer)
|
||||
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x30, // Report Count (48)
|
||||
// 0x09, 0x01, // Usage (Pointer)
|
||||
// 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x30, // Report Count (48)
|
||||
// 0x09, 0x01, // Usage (Pointer)
|
||||
// 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
// 0xC0, // End Collection
|
||||
// 0xA1, 0x02, // Collection (Logical)
|
||||
// 0x85, 0x02, // Report ID (2)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x30, // Report Count (48)
|
||||
// 0x09, 0x01, // Usage (Pointer)
|
||||
// 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
// 0xC0, // End Collection
|
||||
// 0xA1, 0x02, // Collection (Logical)
|
||||
// 0x85, 0xEE, // Report ID (-18)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x30, // Report Count (48)
|
||||
// 0x09, 0x01, // Usage (Pointer)
|
||||
// 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
// 0xC0, // End Collection
|
||||
// 0xA1, 0x02, // Collection (Logical)
|
||||
// 0x85, 0xEF, // Report ID (-17)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x30, // Report Count (48)
|
||||
// 0x09, 0x01, // Usage (Pointer)
|
||||
// 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
// 0xC0, // End Collection
|
||||
// 0xC0, // End Collection
|
||||
};
|
||||
|
||||
// #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN)
|
||||
|
||||
static const uint8_t CONFIGURATION_DESCRIPTORS[] =
|
||||
{
|
||||
// 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)
|
||||
// 0x94, 0x00, // wDescriptorLength[0] 148
|
||||
|
||||
// 0x07, // bLength
|
||||
// 0x05, // bDescriptorType (Endpoint)
|
||||
// 0x02, // bEndpointAddress (OUT/H2D)
|
||||
// 0x03, // bmAttributes (Interrupt)
|
||||
// 0x40, 0x00, // wMaxPacketSize 64
|
||||
// 0x0A, // bInterval 10 (unit depends on device speed)
|
||||
|
||||
// 0x07, // bLength
|
||||
// 0x05, // bDescriptorType (Endpoint)
|
||||
// 0x81, // bEndpointAddress (IN/D2H)
|
||||
// 0x03, // bmAttributes (Interrupt)
|
||||
// 0x40, 0x00, // wMaxPacketSize 64
|
||||
// 0x0A, // bInterval 10 (unit depends on device speed)
|
||||
|
||||
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.17
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0x94, 0x00, // wDescriptorLength[0] 148
|
||||
|
||||
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)
|
||||
};
|
||||
} // namespace PS3
|
||||
|
||||
#endif // _PS3_DESCRIPTORS_H_
|
||||
114
Firmware/RP2040/src/Descriptors/PS4.h
Normal file
114
Firmware/RP2040/src/Descriptors/PS4.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#ifndef _PS4_DESCRIPTORS_H_
|
||||
#define _PS4_DESCRIPTORS_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace PS4
|
||||
{
|
||||
static constexpr uint8_t DPAD_MASK = 0x0F;
|
||||
static constexpr uint8_t COUNTER_MASK = 0xFC;
|
||||
static constexpr uint8_t AXIS_MAX = 0xFF;
|
||||
static constexpr uint8_t AXIS_MIN = 0x00;
|
||||
static constexpr uint8_t JOYSTICK_MID = 0x80;
|
||||
|
||||
namespace Buttons0
|
||||
{
|
||||
static constexpr uint8_t DPAD_UP = 0x00;
|
||||
static constexpr uint8_t DPAD_UP_RIGHT = 0x01;
|
||||
static constexpr uint8_t DPAD_RIGHT = 0x02;
|
||||
static constexpr uint8_t DPAD_RIGHT_DOWN = 0x03;
|
||||
static constexpr uint8_t DPAD_DOWN = 0x04;
|
||||
static constexpr uint8_t DPAD_DOWN_LEFT = 0x05;
|
||||
static constexpr uint8_t DPAD_LEFT = 0x06;
|
||||
static constexpr uint8_t DPAD_LEFT_UP = 0x07;
|
||||
static constexpr uint8_t DPAD_CENTER = 0x08;
|
||||
|
||||
static constexpr uint8_t SQUARE = 0x10;
|
||||
static constexpr uint8_t CROSS = 0x20;
|
||||
static constexpr uint8_t CIRCLE = 0x40;
|
||||
static constexpr uint8_t TRIANGLE = 0x80;
|
||||
};
|
||||
|
||||
namespace Buttons1
|
||||
{
|
||||
static constexpr uint8_t L1 = 0x01;
|
||||
static constexpr uint8_t R1 = 0x02;
|
||||
static constexpr uint8_t L2 = 0x04;
|
||||
static constexpr uint8_t R2 = 0x08;
|
||||
static constexpr uint8_t SHARE = 0x10;
|
||||
static constexpr uint8_t OPTIONS = 0x20;
|
||||
static constexpr uint8_t L3 = 0x40;
|
||||
static constexpr uint8_t R3 = 0x80;
|
||||
};
|
||||
|
||||
namespace Buttons2
|
||||
{
|
||||
static constexpr uint8_t PS = 0x01;
|
||||
static constexpr uint8_t TP = 0x02;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct InReport
|
||||
{
|
||||
uint8_t report_id;
|
||||
|
||||
uint8_t joystick_lx;
|
||||
uint8_t joystick_ly;
|
||||
uint8_t joystick_rx;
|
||||
uint8_t joystick_ry;
|
||||
|
||||
uint8_t buttons[3];
|
||||
|
||||
uint8_t trigger_l;
|
||||
uint8_t trigger_r;
|
||||
};
|
||||
static_assert(sizeof(InReport) == 10);
|
||||
|
||||
struct OutReport
|
||||
{
|
||||
uint8_t report_id;
|
||||
|
||||
uint8_t set_rumble : 1;
|
||||
uint8_t set_led : 1;
|
||||
uint8_t set_led_blink : 1;
|
||||
uint8_t set_ext_write : 1;
|
||||
uint8_t set_left_volume : 1;
|
||||
uint8_t set_right_volume : 1;
|
||||
uint8_t set_mic_volume : 1;
|
||||
uint8_t set_speaker_volume : 1;
|
||||
uint8_t set_flags2;
|
||||
|
||||
uint8_t reserved;
|
||||
|
||||
uint8_t motor_right;
|
||||
uint8_t motor_left;
|
||||
|
||||
uint8_t lightbar_red;
|
||||
uint8_t lightbar_green;
|
||||
uint8_t lightbar_blue;
|
||||
uint8_t lightbar_blink_on;
|
||||
uint8_t lightbar_blink_off;
|
||||
|
||||
uint8_t ext_data[8];
|
||||
|
||||
uint8_t volume_left;
|
||||
uint8_t volume_right;
|
||||
uint8_t volume_mic;
|
||||
uint8_t volume_speaker;
|
||||
|
||||
uint8_t other[9];
|
||||
};
|
||||
static_assert(sizeof(OutReport) == 32);
|
||||
#pragma pack(pop)
|
||||
|
||||
static const uint8_t LED_COLORS[][3] =
|
||||
{
|
||||
{ 0x00, 0x00, 0x40 }, // Blue
|
||||
{ 0x40, 0x00, 0x00 }, // Red
|
||||
{ 0x00, 0x40, 0x00 }, // Green
|
||||
{ 0x20, 0x00, 0x20 }, // Pink
|
||||
};
|
||||
|
||||
}; // namespace PS4
|
||||
|
||||
#endif // _PS4_DESCRIPTORS_H_
|
||||
137
Firmware/RP2040/src/Descriptors/PS5.h
Normal file
137
Firmware/RP2040/src/Descriptors/PS5.h
Normal file
@@ -0,0 +1,137 @@
|
||||
#ifndef _PS5_DESCRIPTORS_H_
|
||||
#define _PS5_DESCRIPTORS_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
namespace PS5
|
||||
{
|
||||
static constexpr uint8_t DPAD_MASK = 0x0F;
|
||||
static constexpr size_t IN_REPORT_CMP_SIZE = 12;
|
||||
static constexpr uint8_t JOYSTICK_MID = 0x80;
|
||||
static constexpr uint8_t OUT_REPORT_ID = 5;
|
||||
|
||||
namespace Buttons0
|
||||
{
|
||||
static constexpr uint8_t DPAD_UP = 0x00;
|
||||
static constexpr uint8_t DPAD_UP_RIGHT = 0x01;
|
||||
static constexpr uint8_t DPAD_RIGHT = 0x02;
|
||||
static constexpr uint8_t DPAD_RIGHT_DOWN = 0x03;
|
||||
static constexpr uint8_t DPAD_DOWN = 0x04;
|
||||
static constexpr uint8_t DPAD_DOWN_LEFT = 0x05;
|
||||
static constexpr uint8_t DPAD_LEFT = 0x06;
|
||||
static constexpr uint8_t DPAD_LEFT_UP = 0x07;
|
||||
static constexpr uint8_t DPAD_CENTER = 0x08;
|
||||
|
||||
static constexpr uint8_t SQUARE = 0x10;
|
||||
static constexpr uint8_t CROSS = 0x20;
|
||||
static constexpr uint8_t CIRCLE = 0x40;
|
||||
static constexpr uint8_t TRIANGLE = 0x80;
|
||||
};
|
||||
|
||||
namespace Buttons1
|
||||
{
|
||||
static constexpr uint8_t L1 = 0x01;
|
||||
static constexpr uint8_t R1 = 0x02;
|
||||
static constexpr uint8_t L2 = 0x04;
|
||||
static constexpr uint8_t R2 = 0x08;
|
||||
static constexpr uint8_t SHARE = 0x10;
|
||||
static constexpr uint8_t OPTIONS = 0x20;
|
||||
static constexpr uint8_t L3 = 0x40;
|
||||
static constexpr uint8_t R3 = 0x80;
|
||||
};
|
||||
|
||||
namespace Buttons2
|
||||
{
|
||||
static constexpr uint8_t PS = 0x01;
|
||||
static constexpr uint8_t TP = 0x02;
|
||||
static constexpr uint8_t MUTE = 0x04;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct InReport
|
||||
{
|
||||
uint8_t report_id;
|
||||
|
||||
uint8_t joystick_lx;
|
||||
uint8_t joystick_ly;
|
||||
uint8_t joystick_rx;
|
||||
uint8_t joystick_ry;
|
||||
uint8_t trigger_l;
|
||||
uint8_t trigger_r;
|
||||
|
||||
uint8_t seq_number;
|
||||
|
||||
uint8_t buttons[4];
|
||||
|
||||
// Motion sensors
|
||||
uint16_t gyro[3]; // Gyroscope data for x, y, z axes
|
||||
uint16_t accel[3]; // Accelerometer data for x, y, z axes
|
||||
uint32_t sensor_timestamp; // Timestamp for sensor data
|
||||
uint8_t reserved2;
|
||||
|
||||
// Touchpad
|
||||
struct dualsense_touch_point {
|
||||
uint8_t counter : 7; // Incremented every time a finger touches the touchpad
|
||||
uint8_t touching : 1; // Indicates if a finger is currently touching the touchpad
|
||||
uint16_t x : 12; // X coordinate of the touchpoint
|
||||
uint16_t y : 12; // Y coordinate of the touchpoint
|
||||
} points[2]; // Array of touchpoints (up to 2)
|
||||
|
||||
uint8_t reserved3[12];
|
||||
|
||||
uint8_t status; // ?
|
||||
|
||||
uint8_t reserved4[10];
|
||||
};
|
||||
// static_assert(sizeof(InReport) == 52, "PS5::InReport is not correct size");
|
||||
|
||||
struct OutReport
|
||||
{
|
||||
uint8_t report_id;
|
||||
|
||||
uint8_t valid_flag0;
|
||||
uint8_t valid_flag1;
|
||||
|
||||
/* For DualShock 4 compatibility mode. */
|
||||
uint8_t motor_right;
|
||||
uint8_t motor_left;
|
||||
|
||||
/* Audio controls */
|
||||
uint8_t headphone_audio_volume; /* 0-0x7f */
|
||||
uint8_t speaker_audio_volume; /* 0-255 */
|
||||
uint8_t internal_microphone_volume; /* 0-0x40 */
|
||||
uint8_t audio_flags;
|
||||
uint8_t mute_button_led;
|
||||
|
||||
uint8_t power_save_control;
|
||||
|
||||
/* right trigger motor */
|
||||
uint8_t right_trigger_motor_mode;
|
||||
uint8_t right_trigger_param[10];
|
||||
|
||||
/* right trigger motor */
|
||||
uint8_t left_trigger_motor_mode;
|
||||
uint8_t left_trigger_param[10];
|
||||
|
||||
uint8_t reserved2[4];
|
||||
|
||||
uint8_t reduce_motor_power;
|
||||
uint8_t audio_flags2; /* 3 first bits: speaker pre-gain */
|
||||
|
||||
/* LEDs and lightbar */
|
||||
uint8_t valid_flag2;
|
||||
uint8_t reserved3[2];
|
||||
uint8_t lightbar_setup;
|
||||
uint8_t led_brightness;
|
||||
uint8_t player_leds;
|
||||
uint8_t lightbar_red;
|
||||
uint8_t lightbar_green;
|
||||
uint8_t lightbar_blue;
|
||||
};
|
||||
static_assert(sizeof(OutReport) == 48);
|
||||
#pragma pack(pop)
|
||||
|
||||
}; // namespace PS5
|
||||
|
||||
#endif // _PS5_DESCRIPTORS_H_
|
||||
150
Firmware/RP2040/src/Descriptors/PSClassic.h
Normal file
150
Firmware/RP2040/src/Descriptors/PSClassic.h
Normal file
@@ -0,0 +1,150 @@
|
||||
#ifndef _PSCLASSIC_DESCRIPTORS_H_
|
||||
#define _PSCLASSIC_DESCRIPTORS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace PSClassic
|
||||
{
|
||||
static constexpr uint16_t DPAD_MASK = 0x3C00;
|
||||
static constexpr uint16_t JOTSTICK_MID = 0x7f;
|
||||
|
||||
namespace Buttons
|
||||
{
|
||||
static constexpr uint16_t TRIANGLE = (1U << 0);
|
||||
static constexpr uint16_t CIRCLE = (1U << 1);
|
||||
static constexpr uint16_t CROSS = (1U << 2);
|
||||
static constexpr uint16_t SQUARE = (1U << 3);
|
||||
static constexpr uint16_t L2 = (1U << 4);
|
||||
static constexpr uint16_t R2 = (1U << 5);
|
||||
static constexpr uint16_t L1 = (1U << 6);
|
||||
static constexpr uint16_t R1 = (1U << 7);
|
||||
static constexpr uint16_t SELECT = (1U << 8);
|
||||
static constexpr uint16_t START = (1U << 9);
|
||||
|
||||
static constexpr uint16_t UP_LEFT = 0x0000 & DPAD_MASK;
|
||||
static constexpr uint16_t UP = 0x0400 & DPAD_MASK;
|
||||
static constexpr uint16_t UP_RIGHT = 0x0800 & DPAD_MASK;
|
||||
static constexpr uint16_t LEFT = 0x1000 & DPAD_MASK;
|
||||
static constexpr uint16_t CENTER = 0x1400 & DPAD_MASK;
|
||||
static constexpr uint16_t RIGHT = 0x1800 & DPAD_MASK;
|
||||
static constexpr uint16_t DOWN_LEFT = 0x2000 & DPAD_MASK;
|
||||
static constexpr uint16_t DOWN = 0x2400 & DPAD_MASK;
|
||||
static constexpr uint16_t DOWN_RIGHT = 0x2800 & DPAD_MASK;
|
||||
};
|
||||
|
||||
struct InReport
|
||||
{
|
||||
uint16_t buttons;
|
||||
};
|
||||
|
||||
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
|
||||
static const uint8_t STRING_MANUFACTURER[] = "Sony Interactive Entertainment";
|
||||
static const uint8_t STRING_PRODUCT[] = "Controller";
|
||||
static const uint8_t STRING_VERSION[] = { };
|
||||
|
||||
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
|
||||
{
|
||||
STRING_LANGUAGE,
|
||||
STRING_MANUFACTURER,
|
||||
STRING_PRODUCT,
|
||||
STRING_VERSION
|
||||
};
|
||||
|
||||
static const uint8_t DEVICE_DESCRIPTORS[] =
|
||||
{
|
||||
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 HID_DESCRIPTORS[] =
|
||||
{
|
||||
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 CONFIGURATION_DESCRIPTORS[] =
|
||||
{
|
||||
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 REPORT_DESCRIPTORS[] =
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
}; // namespace PSClassic
|
||||
|
||||
#endif // _PSCLASSIC_DESCRIPTORS_H_
|
||||
270
Firmware/RP2040/src/Descriptors/SwitchPro.h
Normal file
270
Firmware/RP2040/src/Descriptors/SwitchPro.h
Normal file
@@ -0,0 +1,270 @@
|
||||
#ifndef SWITCH_PRO_DESCRIPTORS_H_
|
||||
#define SWITCH_PRO_DESCRIPTORS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace SwitchPro
|
||||
{
|
||||
static constexpr uint8_t INFO_CONN_MASK = 0xAB;
|
||||
static constexpr uint8_t INFO_BATTERY_MASK = 0x0F;
|
||||
|
||||
namespace CMD
|
||||
{
|
||||
static constexpr uint8_t HID = 0x80;
|
||||
static constexpr uint8_t RUMBLE_ONLY = 0x10;
|
||||
static constexpr uint8_t AND_RUMBLE = 0x01;
|
||||
static constexpr uint8_t LED = 0x30;
|
||||
static constexpr uint8_t LED_HOME = 0x38;
|
||||
static constexpr uint8_t GYRO = 0x40;
|
||||
static constexpr uint8_t MODE = 0x03;
|
||||
static constexpr uint8_t FULL_REPORT_MODE = 0x30;
|
||||
static constexpr uint8_t HANDSHAKE = 0x02;
|
||||
static constexpr uint8_t DISABLE_TIMEOUT = 0x04;
|
||||
}
|
||||
|
||||
namespace Buttons0
|
||||
{
|
||||
static constexpr uint8_t Y = 0x01;
|
||||
static constexpr uint8_t X = 0x02;
|
||||
static constexpr uint8_t B = 0x04;
|
||||
static constexpr uint8_t A = 0x08;
|
||||
static constexpr uint8_t R = 0x40;
|
||||
static constexpr uint8_t ZR = 0x80;
|
||||
};
|
||||
|
||||
namespace Buttons1
|
||||
{
|
||||
static constexpr uint8_t MINUS = 0x01;
|
||||
static constexpr uint8_t PLUS = 0x02;
|
||||
static constexpr uint8_t L3 = 0x04;
|
||||
static constexpr uint8_t R3 = 0x08;
|
||||
static constexpr uint8_t HOME = 0x10;
|
||||
static constexpr uint8_t CAPTURE = 0x20;
|
||||
};
|
||||
|
||||
namespace Buttons2
|
||||
{
|
||||
static constexpr uint8_t DPAD_DOWN = 0x01;
|
||||
static constexpr uint8_t DPAD_UP = 0x02;
|
||||
static constexpr uint8_t DPAD_RIGHT = 0x04;
|
||||
static constexpr uint8_t DPAD_LEFT = 0x08;
|
||||
static constexpr uint8_t L = 0x40;
|
||||
static constexpr uint8_t ZL = 0x80;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct InReport
|
||||
{
|
||||
uint8_t report_id;
|
||||
uint8_t timer;
|
||||
|
||||
// uint8_t connInfo : 4;
|
||||
// uint8_t battery : 4;
|
||||
uint8_t info;
|
||||
|
||||
uint8_t buttons[3];
|
||||
|
||||
// uint16_t leftX : 12;
|
||||
// uint16_t leftY : 12;
|
||||
// uint16_t rightX : 12;
|
||||
// uint16_t rightY : 12;
|
||||
uint8_t joysticks[6];
|
||||
|
||||
uint8_t vibrator;
|
||||
|
||||
uint16_t accelerX;
|
||||
uint16_t accelerY;
|
||||
uint16_t accelerZ;
|
||||
|
||||
uint16_t velocityX;
|
||||
uint16_t velocityY;
|
||||
uint16_t velocityZ;
|
||||
};
|
||||
static_assert(sizeof(InReport) == 25, "InReport size is not correct");
|
||||
|
||||
struct OutReport
|
||||
{
|
||||
uint8_t command;
|
||||
uint8_t sequence_counter;
|
||||
uint8_t rumble_l[4];
|
||||
uint8_t rumble_r[4];
|
||||
uint8_t sub_command;
|
||||
uint8_t sub_command_args[3];
|
||||
};
|
||||
static_assert(sizeof(OutReport) == 14, "OutReport size is not correct");
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
// static const uint8_t switch_pro_string_language[] = { 0x09, 0x04 };
|
||||
// static const uint8_t switch_pro_string_manufacturer[] = "Nintnedo Co., Ltd.";
|
||||
// static const uint8_t switch_pro_string_product[] = "Pro Controller";
|
||||
// static const uint8_t switch_pro_string_version[] = "000000000001";
|
||||
|
||||
// static const uint8_t *switch_pro_string_descriptors[] __attribute__((unused)) =
|
||||
// {
|
||||
// switch_pro_string_language,
|
||||
// switch_pro_string_product, // switch these?
|
||||
// switch_pro_string_manufacturer, // ?
|
||||
// switch_pro_string_version
|
||||
// };
|
||||
|
||||
// static const uint8_t switch_pro_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
|
||||
// 0x7E, 0x05, // idVendor 0x057E
|
||||
// 0x09, 0x20, // idProduct 0x2009
|
||||
// 0x00, 0x02, // bcdDevice 4.00
|
||||
// 0x01, // iManufacturer (String Index)
|
||||
// 0x02, // iProduct (String Index)
|
||||
// 0x03, // iSerialNumber (String Index)
|
||||
// 0x01, // bNumConfigurations 1
|
||||
// };
|
||||
|
||||
// static const uint8_t switch_pro_configuration_descriptor[] =
|
||||
// {
|
||||
// 0x09, // bLength
|
||||
// 0x02, // bDescriptorType (Configuration)
|
||||
// 0x29, 0x00, // wTotalLength 41
|
||||
// 0x01, // bNumInterfaces 1
|
||||
// 0x01, // bConfigurationValue
|
||||
// 0x00, // iConfiguration (String Index)
|
||||
// 0xA0, // bmAttributes Remote Wakeup
|
||||
// 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)
|
||||
// 0xCB, 0x00, // wDescriptorLength[0] 203
|
||||
|
||||
// 0x07, // bLength
|
||||
// 0x05, // bDescriptorType (Endpoint)
|
||||
// 0x81, // bEndpointAddress (IN/D2H)
|
||||
// 0x03, // bmAttributes (Interrupt)
|
||||
// 0x40, 0x00, // wMaxPacketSize 64
|
||||
// 0x08, // bInterval 8 (unit depends on device speed)
|
||||
|
||||
// 0x07, // bLength
|
||||
// 0x05, // bDescriptorType (Endpoint)
|
||||
// 0x01, // bEndpointAddress (OUT/H2D)
|
||||
// 0x03, // bmAttributes (Interrupt)
|
||||
// 0x40, 0x00, // wMaxPacketSize 64
|
||||
// 0x08, // bInterval 8 (unit depends on device speed)
|
||||
// };
|
||||
|
||||
// static const uint8_t switch_pro_report_descriptor[] =
|
||||
// {
|
||||
// 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
// 0x15, 0x00, // Logical Minimum (0)
|
||||
// 0x09, 0x04, // Usage (Joystick)
|
||||
// 0xA1, 0x01, // Collection (Application)
|
||||
// 0x85, 0x30, // Report ID (48)
|
||||
// 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
// 0x05, 0x09, // Usage Page (Button)
|
||||
// 0x19, 0x01, // Usage Minimum (0x01)
|
||||
// 0x29, 0x0A, // Usage Maximum (0x0A)
|
||||
// 0x15, 0x00, // Logical Minimum (0)
|
||||
// 0x25, 0x01, // Logical Maximum (1)
|
||||
// 0x75, 0x01, // Report Size (1)
|
||||
// 0x95, 0x0A, // Report Count (10)
|
||||
// 0x55, 0x00, // Unit Exponent (0)
|
||||
// 0x65, 0x00, // Unit (None)
|
||||
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0x05, 0x09, // Usage Page (Button)
|
||||
// 0x19, 0x0B, // Usage Minimum (0x0B)
|
||||
// 0x29, 0x0E, // Usage Maximum (0x0E)
|
||||
// 0x15, 0x00, // Logical Minimum (0)
|
||||
// 0x25, 0x01, // Logical Maximum (1)
|
||||
// 0x75, 0x01, // Report Size (1)
|
||||
// 0x95, 0x04, // Report Count (4)
|
||||
// 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, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0x0B, 0x01, 0x00, 0x01, 0x00, // Usage (0x010001)
|
||||
// 0xA1, 0x00, // Collection (Physical)
|
||||
// 0x0B, 0x30, 0x00, 0x01, 0x00, // Usage (0x010030)
|
||||
// 0x0B, 0x31, 0x00, 0x01, 0x00, // Usage (0x010031)
|
||||
// 0x0B, 0x32, 0x00, 0x01, 0x00, // Usage (0x010032)
|
||||
// 0x0B, 0x35, 0x00, 0x01, 0x00, // Usage (0x010035)
|
||||
// 0x15, 0x00, // Logical Minimum (0)
|
||||
// 0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534)
|
||||
// 0x75, 0x10, // Report Size (16)
|
||||
// 0x95, 0x04, // Report Count (4)
|
||||
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0xC0, // End Collection
|
||||
// 0x0B, 0x39, 0x00, 0x01, 0x00, // Usage (0x010039)
|
||||
// 0x15, 0x00, // Logical Minimum (0)
|
||||
// 0x25, 0x07, // Logical Maximum (7)
|
||||
// 0x35, 0x00, // Physical Minimum (0)
|
||||
// 0x46, 0x3B, 0x01, // Physical Maximum (315)
|
||||
// 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
|
||||
// 0x75, 0x04, // Report Size (4)
|
||||
// 0x95, 0x01, // Report Count (1)
|
||||
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0x05, 0x09, // Usage Page (Button)
|
||||
// 0x19, 0x0F, // Usage Minimum (0x0F)
|
||||
// 0x29, 0x12, // Usage Maximum (0x12)
|
||||
// 0x15, 0x00, // Logical Minimum (0)
|
||||
// 0x25, 0x01, // Logical Maximum (1)
|
||||
// 0x75, 0x01, // Report Size (1)
|
||||
// 0x95, 0x04, // Report Count (4)
|
||||
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x34, // Report Count (52)
|
||||
// 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
|
||||
// 0x85, 0x21, // Report ID (33)
|
||||
// 0x09, 0x01, // Usage (0x01)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x3F, // Report Count (63)
|
||||
// 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0x85, 0x81, // Report ID (-127)
|
||||
// 0x09, 0x02, // Usage (0x02)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x3F, // Report Count (63)
|
||||
// 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
// 0x85, 0x01, // Report ID (1)
|
||||
// 0x09, 0x03, // Usage (0x03)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x3F, // Report Count (63)
|
||||
// 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
|
||||
// 0x85, 0x10, // Report ID (16)
|
||||
// 0x09, 0x04, // Usage (0x04)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x3F, // Report Count (63)
|
||||
// 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
|
||||
// 0x85, 0x80, // Report ID (-128)
|
||||
// 0x09, 0x05, // Usage (0x05)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x3F, // Report Count (63)
|
||||
// 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
|
||||
// 0x85, 0x82, // Report ID (-126)
|
||||
// 0x09, 0x06, // Usage (0x06)
|
||||
// 0x75, 0x08, // Report Size (8)
|
||||
// 0x95, 0x3F, // Report Count (63)
|
||||
// 0x91, 0x83, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
|
||||
// 0xC0, // End Collection
|
||||
// };
|
||||
|
||||
}; // namespace SwitchPro
|
||||
|
||||
#endif // SWITCH_PRO_DESCRIPTORS_H_
|
||||
258
Firmware/RP2040/src/Descriptors/SwitchWired.h
Normal file
258
Firmware/RP2040/src/Descriptors/SwitchWired.h
Normal file
@@ -0,0 +1,258 @@
|
||||
#ifndef _SWITCH_WIRED_DESCRIPTORS_H_
|
||||
#define _SWITCH_WIRED_DESCRIPTORS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
namespace SwitchWired
|
||||
{
|
||||
static constexpr uint8_t JOYSTICK_MID = 0x80;
|
||||
|
||||
namespace DPad
|
||||
{
|
||||
static constexpr uint8_t UP = 0x00;
|
||||
static constexpr uint8_t UP_RIGHT = 0x01;
|
||||
static constexpr uint8_t RIGHT = 0x02;
|
||||
static constexpr uint8_t DOWN_RIGHT = 0x03;
|
||||
static constexpr uint8_t DOWN = 0x04;
|
||||
static constexpr uint8_t DOWN_LEFT = 0x05;
|
||||
static constexpr uint8_t LEFT = 0x06;
|
||||
static constexpr uint8_t UP_LEFT = 0x07;
|
||||
static constexpr uint8_t CENTER = 0x08;
|
||||
};
|
||||
|
||||
namespace Buttons
|
||||
{
|
||||
static constexpr uint16_t Y = (1U << 0);
|
||||
static constexpr uint16_t B = (1U << 1);
|
||||
static constexpr uint16_t A = (1U << 2);
|
||||
static constexpr uint16_t X = (1U << 3);
|
||||
static constexpr uint16_t L = (1U << 4);
|
||||
static constexpr uint16_t R = (1U << 5);
|
||||
static constexpr uint16_t ZL = (1U << 6);
|
||||
static constexpr uint16_t ZR = (1U << 7);
|
||||
static constexpr uint16_t MINUS = (1U << 8);
|
||||
static constexpr uint16_t PLUS = (1U << 9);
|
||||
static constexpr uint16_t L3 = (1U << 10);
|
||||
static constexpr uint16_t R3 = (1U << 11);
|
||||
static constexpr uint16_t HOME = (1U << 12);
|
||||
static constexpr uint16_t CAPTURE = (1U << 13);
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct InReport
|
||||
{
|
||||
uint16_t buttons{0};
|
||||
uint8_t dpad{DPad::CENTER};
|
||||
uint8_t joystick_lx{JOYSTICK_MID};
|
||||
uint8_t joystick_ly{JOYSTICK_MID};
|
||||
uint8_t joystick_rx{JOYSTICK_MID};
|
||||
uint8_t joystick_ry{JOYSTICK_MID};
|
||||
uint8_t vendor{0};
|
||||
};
|
||||
static_assert(sizeof(InReport) == 8, "SwitchWired::InReport is not the correct size");
|
||||
#pragma pack(pop)
|
||||
|
||||
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
|
||||
static const uint8_t STRING_MANUFACTURER[] = "HORI CO.,LTD.";
|
||||
static const uint8_t STRING_PRODUCT[] = "POKKEN CONTROLLER";
|
||||
static const uint8_t STRING_VERSION[] = "1.0";
|
||||
|
||||
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
|
||||
{
|
||||
STRING_LANGUAGE,
|
||||
STRING_MANUFACTURER,
|
||||
STRING_PRODUCT,
|
||||
STRING_VERSION
|
||||
};
|
||||
|
||||
static const uint8_t DEVICE_DESCRIPTORS[] =
|
||||
{
|
||||
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 HID_DESCRIPTORS[] =
|
||||
{
|
||||
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 REPORT_DESCRIPTORS[] =
|
||||
{
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x05, // Usage (Game Pad)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x45, 0x01, // Physical Maximum (1)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x10, // Report Count (16)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x10, // Usage Maximum (0x10)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x25, 0x07, // Logical Maximum (7)
|
||||
0x46, 0x3B, 0x01, // Physical Maximum (315)
|
||||
0x75, 0x04, // Report Size (4)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
|
||||
0x09, 0x39, // Usage (Hat switch)
|
||||
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
|
||||
0x65, 0x00, // Unit (None)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x46, 0xFF, 0x00, // Physical Maximum (255)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x09, 0x32, // Usage (Z)
|
||||
0x09, 0x35, // Usage (Rz)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
|
||||
0x09, 0x20, // Usage (0x20)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x0A, 0x21, 0x26, // Usage (0x2621)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
};
|
||||
|
||||
// static const uint8_t CONFIGURATION_DESCRIPTORS[] =
|
||||
// {
|
||||
// 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)
|
||||
// };
|
||||
|
||||
enum
|
||||
{
|
||||
ITF_NUM_HID1,
|
||||
#if MAX_GAMEPADS > 1
|
||||
ITF_NUM_HID2,
|
||||
#endif
|
||||
#if MAX_GAMEPADS > 2
|
||||
ITF_NUM_HID3,
|
||||
#endif
|
||||
#if MAX_GAMEPADS > 3
|
||||
ITF_NUM_HID4,
|
||||
#endif
|
||||
ITF_NUM_TOTAL
|
||||
};
|
||||
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + (TUD_HID_DESC_LEN * MAX_GAMEPADS))
|
||||
|
||||
#define EPNUM_HID1 0x81
|
||||
#define EPNUM_HID2 0x82
|
||||
#define EPNUM_HID3 0x83
|
||||
#define EPNUM_HID4 0x84
|
||||
|
||||
uint8_t const CONFIGURATION_DESCRIPTORS[] = {
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR( 1,
|
||||
ITF_NUM_TOTAL,
|
||||
0,
|
||||
CONFIG_TOTAL_LEN,
|
||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
|
||||
500),
|
||||
|
||||
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR( ITF_NUM_HID1,
|
||||
0,
|
||||
HID_ITF_PROTOCOL_NONE,
|
||||
sizeof(REPORT_DESCRIPTORS),
|
||||
EPNUM_HID1,
|
||||
CFG_TUD_HID_EP_BUFSIZE,
|
||||
1),
|
||||
#if MAX_GAMEPADS > 1
|
||||
TUD_HID_DESCRIPTOR( ITF_NUM_HID2,
|
||||
0,
|
||||
HID_ITF_PROTOCOL_NONE,
|
||||
sizeof(REPORT_DESCRIPTORS),
|
||||
EPNUM_HID2,
|
||||
CFG_TUD_HID_EP_BUFSIZE,
|
||||
1),
|
||||
#endif
|
||||
#if MAX_GAMEPADS > 2
|
||||
TUD_HID_DESCRIPTOR( ITF_NUM_HID3,
|
||||
0,
|
||||
HID_ITF_PROTOCOL_NONE,
|
||||
sizeof(REPORT_DESCRIPTORS),
|
||||
EPNUM_HID3,
|
||||
CFG_TUD_HID_EP_BUFSIZE,
|
||||
1),
|
||||
#endif
|
||||
#if MAX_GAMEPADS > 3
|
||||
TUD_HID_DESCRIPTOR( ITF_NUM_HID4,
|
||||
0,
|
||||
HID_ITF_PROTOCOL_NONE,
|
||||
sizeof(REPORT_DESCRIPTORS),
|
||||
EPNUM_HID4,
|
||||
CFG_TUD_HID_EP_BUFSIZE,
|
||||
1)
|
||||
#endif
|
||||
};
|
||||
|
||||
}; // namespace SwitchWired
|
||||
|
||||
#endif // _SWITCH_WIRED_DESCRIPTORS_H_
|
||||
233
Firmware/RP2040/src/Descriptors/XInput.h
Normal file
233
Firmware/RP2040/src/Descriptors/XInput.h
Normal file
@@ -0,0 +1,233 @@
|
||||
#ifndef _XINPUT_DESCRIPTORS_H_
|
||||
#define _XINPUT_DESCRIPTORS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace XInput
|
||||
{
|
||||
static constexpr size_t ENDPOINT_IN_SIZE = 20;
|
||||
static constexpr size_t ENDPOINT_OUT_SIZE = 32;
|
||||
|
||||
namespace Chatpad
|
||||
{
|
||||
static constexpr uint8_t CODE_1 = 23 ;
|
||||
static constexpr uint8_t CODE_2 = 22 ;
|
||||
static constexpr uint8_t CODE_3 = 21 ;
|
||||
static constexpr uint8_t CODE_4 = 20 ;
|
||||
static constexpr uint8_t CODE_5 = 19 ;
|
||||
static constexpr uint8_t CODE_6 = 18 ;
|
||||
static constexpr uint8_t CODE_7 = 17 ;
|
||||
static constexpr uint8_t CODE_8 = 103;
|
||||
static constexpr uint8_t CODE_9 = 102;
|
||||
static constexpr uint8_t CODE_0 = 101;
|
||||
|
||||
static constexpr uint8_t CODE_Q = 39 ;
|
||||
static constexpr uint8_t CODE_W = 38 ;
|
||||
static constexpr uint8_t CODE_E = 37 ;
|
||||
static constexpr uint8_t CODE_R = 36 ;
|
||||
static constexpr uint8_t CODE_T = 35 ;
|
||||
static constexpr uint8_t CODE_Y = 34 ;
|
||||
static constexpr uint8_t CODE_U = 33 ;
|
||||
static constexpr uint8_t CODE_I = 118;
|
||||
static constexpr uint8_t CODE_O = 117;
|
||||
static constexpr uint8_t CODE_P = 100;
|
||||
|
||||
static constexpr uint8_t CODE_A = 55;
|
||||
static constexpr uint8_t CODE_S = 54;
|
||||
static constexpr uint8_t CODE_D = 53;
|
||||
static constexpr uint8_t CODE_F = 52;
|
||||
static constexpr uint8_t CODE_G = 51;
|
||||
static constexpr uint8_t CODE_H = 50;
|
||||
static constexpr uint8_t CODE_J = 49;
|
||||
static constexpr uint8_t CODE_K = 119;
|
||||
static constexpr uint8_t CODE_L = 114;
|
||||
static constexpr uint8_t CODE_COMMA = 98;
|
||||
|
||||
static constexpr uint8_t CODE_Z = 70;
|
||||
static constexpr uint8_t CODE_X = 69;
|
||||
static constexpr uint8_t CODE_C = 68;
|
||||
static constexpr uint8_t CODE_V = 67;
|
||||
static constexpr uint8_t CODE_B = 66;
|
||||
static constexpr uint8_t CODE_N = 65;
|
||||
static constexpr uint8_t CODE_M = 82;
|
||||
static constexpr uint8_t CODE_PERIOD = 83;
|
||||
static constexpr uint8_t CODE_ENTER = 99;
|
||||
|
||||
static constexpr uint8_t CODE_LEFT = 85;
|
||||
static constexpr uint8_t CODE_SPACE = 84;
|
||||
static constexpr uint8_t CODE_RIGHT = 81;
|
||||
static constexpr uint8_t CODE_BACK = 113;
|
||||
|
||||
//Offset byte 25
|
||||
static constexpr uint8_t CODE_SHIFT = 1;
|
||||
static constexpr uint8_t CODE_GREEN = 2;
|
||||
static constexpr uint8_t CODE_ORANGE = 4;
|
||||
static constexpr uint8_t CODE_MESSENGER = 8;
|
||||
};
|
||||
|
||||
namespace OutReportID
|
||||
{
|
||||
static constexpr uint8_t RUMBLE = 0x00;
|
||||
static constexpr uint8_t LED = 0x01;
|
||||
};
|
||||
|
||||
namespace Buttons0
|
||||
{
|
||||
static constexpr uint8_t DPAD_UP = (1U << 0);
|
||||
static constexpr uint8_t DPAD_DOWN = (1U << 1);
|
||||
static constexpr uint8_t DPAD_LEFT = (1U << 2);
|
||||
static constexpr uint8_t DPAD_RIGHT = (1U << 3);
|
||||
static constexpr uint8_t START = (1U << 4);
|
||||
static constexpr uint8_t BACK = (1U << 5);
|
||||
static constexpr uint8_t L3 = (1U << 6);
|
||||
static constexpr uint8_t R3 = (1U << 7);
|
||||
};
|
||||
|
||||
namespace Buttons1
|
||||
{
|
||||
static constexpr uint8_t LB = (1U << 0);
|
||||
static constexpr uint8_t RB = (1U << 1);
|
||||
static constexpr uint8_t HOME = (1U << 2);
|
||||
static constexpr uint8_t A = (1U << 4);
|
||||
static constexpr uint8_t B = (1U << 5);
|
||||
static constexpr uint8_t X = (1U << 6);
|
||||
static constexpr uint8_t Y = (1U << 7);
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct InReport
|
||||
{
|
||||
uint8_t report_id;
|
||||
uint8_t report_size;
|
||||
uint8_t buttons[2];
|
||||
uint8_t trigger_l;
|
||||
uint8_t trigger_r;
|
||||
int16_t joystick_lx;
|
||||
int16_t joystick_ly;
|
||||
int16_t joystick_rx;
|
||||
int16_t joystick_ry;
|
||||
uint8_t reserved[6];
|
||||
};
|
||||
static_assert(sizeof(InReport) == 20, "XInput::InReport is not the correct size");
|
||||
|
||||
struct WiredChatpadReport
|
||||
{
|
||||
uint8_t report_id;
|
||||
uint8_t chatpad[3];
|
||||
};
|
||||
static_assert(sizeof(WiredChatpadReport) == 4, "XInput::WiredChatpadReport is not the correct size");
|
||||
|
||||
struct InReportWireless
|
||||
{
|
||||
uint8_t command[4];
|
||||
uint8_t report_id;
|
||||
uint8_t report_size;
|
||||
uint8_t buttons[2];
|
||||
uint8_t trigger_l;
|
||||
uint8_t trigger_r;
|
||||
int16_t joystick_lx;
|
||||
int16_t joystick_ly;
|
||||
int16_t joystick_rx;
|
||||
int16_t joystick_ry; // 18
|
||||
uint8_t reserved[6];
|
||||
uint8_t chatpad_status;
|
||||
uint8_t chatpad[3];
|
||||
};
|
||||
static_assert(sizeof(InReportWireless) == 28, "XInput::InReportWireless is not the correct size");
|
||||
|
||||
struct OutReport
|
||||
{
|
||||
uint8_t report_id;
|
||||
uint8_t report_size;
|
||||
uint8_t led;
|
||||
uint8_t rumble_l;
|
||||
uint8_t rumble_r;
|
||||
uint8_t reserved[3];
|
||||
};
|
||||
static_assert(sizeof(OutReport) == 8, "XInput::OutReport is not the correct size");
|
||||
#pragma pack(pop)
|
||||
|
||||
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
|
||||
static const uint8_t STRING_MANUFACTURER[] = "Microsoft";
|
||||
static const uint8_t STRING_PRODUCT[] = "XInput STANDARD GAMEPAD";
|
||||
static const uint8_t STRING_VERSION[] = "1.0";
|
||||
|
||||
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
|
||||
{
|
||||
STRING_LANGUAGE,
|
||||
STRING_MANUFACTURER,
|
||||
STRING_PRODUCT,
|
||||
STRING_VERSION
|
||||
};
|
||||
|
||||
static const uint8_t DEVICE_DESCRIPTORS[] =
|
||||
{
|
||||
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 CONFIGURATION_DESCRIPTORS[] =
|
||||
{
|
||||
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
|
||||
0x00, 0x01, // bcdHID 1.00
|
||||
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)
|
||||
};
|
||||
};
|
||||
|
||||
#endif // _XINPUT_DESCRIPTORS_H_
|
||||
635
Firmware/RP2040/src/Descriptors/XboxOG.h
Normal file
635
Firmware/RP2040/src/Descriptors/XboxOG.h
Normal file
@@ -0,0 +1,635 @@
|
||||
#ifndef _XBOX_OG_DESCRIPTORS_H_
|
||||
#define _XBOX_OG_DESCRIPTORS_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
namespace XboxOG
|
||||
{
|
||||
//Control request constants
|
||||
|
||||
static constexpr uint8_t GET_DESC_REQ_TYPE = 0xC1;
|
||||
static constexpr uint8_t GET_DESC_REQ = 0x06;
|
||||
static constexpr uint16_t GET_DESC_VALUE = 0x4200;
|
||||
static constexpr uint8_t GET_DESC_SUBTYPE_GP_DUKE = 0x01;
|
||||
static constexpr uint8_t GET_DESC_SUBTYPE_GP_S = 0x02;
|
||||
static constexpr uint8_t GET_DESC_SUBTYPE_WHEEL = 0x10;
|
||||
static constexpr uint8_t GET_DESC_SUBTYPE_STICK = 0x20;
|
||||
static constexpr uint8_t GET_DESC_SUBTYPE_LIGHTGUN = 0x50;
|
||||
|
||||
static constexpr uint8_t GET_CAP_REQ_TYPE = 0xC1;
|
||||
static constexpr uint8_t GET_CAP_REQ = 0x01;
|
||||
static constexpr uint16_t GET_CAP_VALUE_IN = 0x0100;
|
||||
static constexpr uint16_t GET_CAP_VALUE_OUT = 0x0200;
|
||||
|
||||
static constexpr uint8_t SET_REPORT_REQ_TYPE = 0x21;
|
||||
static constexpr uint8_t SET_REPORT_REQ = 0x09;
|
||||
static constexpr uint16_t SET_REPORT_VALUE = 0x0200;
|
||||
|
||||
static constexpr uint8_t GET_REPORT_REQ_TYPE = 0xA1;
|
||||
static constexpr uint8_t GET_REPORT_REQ = 1;
|
||||
static constexpr uint16_t GET_REPORT_VALUE = 0x0100;
|
||||
|
||||
namespace GP //Duke/S
|
||||
{
|
||||
namespace Buttons
|
||||
{
|
||||
static constexpr uint8_t DPAD_UP = (1 << 0);
|
||||
static constexpr uint8_t DPAD_DOWN = (1 << 1);
|
||||
static constexpr uint8_t DPAD_LEFT = (1 << 2);
|
||||
static constexpr uint8_t DPAD_RIGHT = (1 << 3);
|
||||
static constexpr uint8_t START = (1 << 4);
|
||||
static constexpr uint8_t BACK = (1 << 5);
|
||||
static constexpr uint8_t L3 = (1 << 6);
|
||||
static constexpr uint8_t R3 = (1 << 7);
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct InReport
|
||||
{
|
||||
uint8_t reserved1;
|
||||
uint8_t report_len;
|
||||
uint8_t buttons;
|
||||
uint8_t reserved2;
|
||||
uint8_t a;
|
||||
uint8_t b;
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t black;
|
||||
uint8_t white;
|
||||
uint8_t trigger_l;
|
||||
uint8_t trigger_r;
|
||||
int16_t joystick_lx;
|
||||
int16_t joystick_ly;
|
||||
int16_t joystick_rx;
|
||||
int16_t joystick_ry;
|
||||
};
|
||||
static_assert(sizeof(InReport) == 20, "XboxOG::InReport is not the correct size");
|
||||
|
||||
struct OutReport
|
||||
{
|
||||
uint8_t reserved;
|
||||
uint8_t report_len;
|
||||
uint16_t rumble_l;
|
||||
uint16_t rumble_r;
|
||||
};
|
||||
static_assert(sizeof(OutReport) == 6, "XboxOG::OutReport is not the correct size");
|
||||
#pragma pack(pop)
|
||||
|
||||
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
|
||||
static const uint8_t STRING_MANUFACTURER[] = "";
|
||||
static const uint8_t STRING_PRODUCT[] = "";
|
||||
static const uint8_t STRING_VERSION[] = "1.0";
|
||||
|
||||
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
|
||||
{
|
||||
STRING_LANGUAGE,
|
||||
STRING_MANUFACTURER,
|
||||
STRING_PRODUCT,
|
||||
STRING_VERSION
|
||||
};
|
||||
|
||||
static const tusb_desc_device_t DEVICE_DESCRIPTORS =
|
||||
{
|
||||
.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
|
||||
};
|
||||
|
||||
static constexpr uint8_t INTERFACE_CLASS = 0x58;
|
||||
static constexpr uint8_t INTERFACE_SUBCLASS = 0x42;
|
||||
static constexpr uint16_t DRIVER_LEN = 9+7+7;
|
||||
static constexpr uint16_t CONFIG_DESC_TOTAL_LEN = DRIVER_LEN + TUD_CONFIG_DESC_LEN;
|
||||
|
||||
enum Interface
|
||||
{
|
||||
NUM_DUKE = 0,
|
||||
NUM_TOTAL
|
||||
};
|
||||
|
||||
#define TUD_XID_DUKE_DESCRIPTOR(_itfnum, _epout, _epin) \
|
||||
/* Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, INTERFACE_CLASS, 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 CONFIGURATION_DESCRIPTORS[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR( 1,
|
||||
Interface::NUM_TOTAL,
|
||||
0,
|
||||
CONFIG_DESC_TOTAL_LEN,
|
||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
|
||||
500),
|
||||
|
||||
TUD_XID_DUKE_DESCRIPTOR(Interface::NUM_DUKE,
|
||||
Interface::NUM_DUKE + 1,
|
||||
0x80 | (Interface::NUM_DUKE + 1)),
|
||||
};
|
||||
|
||||
static const uint8_t XID_DEVICE_DESCRIPTORS[] =
|
||||
{
|
||||
0x10,
|
||||
0x42,
|
||||
0x00, 0x01,
|
||||
0x01,
|
||||
0x02,
|
||||
0x14,
|
||||
0x06,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
static const uint8_t XID_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 XID_CAPABILITIES_OUT[] =
|
||||
{
|
||||
0x00,
|
||||
0x06,
|
||||
0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
}; // GP
|
||||
|
||||
namespace SB // Steel Battalion
|
||||
{
|
||||
static constexpr uint16_t AIMING_MID = 32768;
|
||||
static constexpr uint16_t BUTTONS2_TOGGLE_MID = 0xFFFC;
|
||||
static constexpr int16_t DEFAULT_DEADZONE = 7500;
|
||||
static constexpr uint16_t DEFAULT_SENSE = 400;
|
||||
|
||||
namespace Buttons0
|
||||
{
|
||||
static constexpr uint16_t RIGHTJOYMAINWEAPON = 0x0001;
|
||||
static constexpr uint16_t RIGHTJOYFIRE = 0x0002;
|
||||
static constexpr uint16_t RIGHTJOYLOCKON = 0x0004;
|
||||
static constexpr uint16_t EJECT = 0x0008;
|
||||
static constexpr uint16_t COCKPITHATCH = 0x0010;
|
||||
static constexpr uint16_t IGNITION = 0x0020;
|
||||
static constexpr uint16_t START = 0x0040;
|
||||
static constexpr uint16_t MULTIMONOPENCLOSE = 0x0080;
|
||||
static constexpr uint16_t MULTIMONMAPZOOMINOUT = 0x0100;
|
||||
static constexpr uint16_t MULTIMONMODESELECT = 0x0200;
|
||||
static constexpr uint16_t MULTIMONSUBMONITOR = 0x0400;
|
||||
static constexpr uint16_t MAINMONZOOMIN = 0x0800;
|
||||
static constexpr uint16_t MAINMONZOOMOUT = 0x1000;
|
||||
static constexpr uint16_t FUNCTIONFSS = 0x2000;
|
||||
static constexpr uint16_t FUNCTIONMANIPULATOR = 0x4000;
|
||||
static constexpr uint16_t FUNCTIONLINECOLORCHANGE = 0x8000;
|
||||
};
|
||||
|
||||
namespace Buttons1
|
||||
{
|
||||
static constexpr uint16_t WASHING = 0x0001;
|
||||
static constexpr uint16_t EXTINGUISHER = 0x0002;
|
||||
static constexpr uint16_t CHAFF = 0x0004;
|
||||
static constexpr uint16_t FUNCTIONTANKDETACH = 0x0008;
|
||||
static constexpr uint16_t FUNCTIONOVERRIDE = 0x0010;
|
||||
static constexpr uint16_t FUNCTIONNIGHTSCOPE = 0x0020;
|
||||
static constexpr uint16_t FUNCTIONF1 = 0x0040;
|
||||
static constexpr uint16_t FUNCTIONF2 = 0x0080;
|
||||
static constexpr uint16_t FUNCTIONF3 = 0x0100;
|
||||
static constexpr uint16_t WEAPONCONMAIN = 0x0200;
|
||||
static constexpr uint16_t WEAPONCONSUB = 0x0400;
|
||||
static constexpr uint16_t WEAPONCONMAGAZINE = 0x0800;
|
||||
static constexpr uint16_t COMM1 = 0x1000;
|
||||
static constexpr uint16_t COMM2 = 0x2000;
|
||||
static constexpr uint16_t COMM3 = 0x4000;
|
||||
static constexpr uint16_t COMM4 = 0x8000;
|
||||
};
|
||||
|
||||
namespace Buttons2
|
||||
{
|
||||
static constexpr uint16_t COMM5 = 0x0001;
|
||||
static constexpr uint16_t LEFTJOYSIGHTCHANGE = 0x0002;
|
||||
static constexpr uint16_t TOGGLEFILTERCONTROL = 0x0004;
|
||||
static constexpr uint16_t TOGGLEOXYGENSUPPLY = 0x0008;
|
||||
static constexpr uint16_t TOGGLEFUELFLOWRATE = 0x0010;
|
||||
static constexpr uint16_t TOGGLEBUFFREMATERIAL = 0x0020;
|
||||
static constexpr uint16_t TOGGLEVTLOCATION = 0x0040;
|
||||
};
|
||||
|
||||
namespace Axis
|
||||
{
|
||||
static constexpr uint16_t AIMINGX = 0x0001;
|
||||
static constexpr uint16_t AIMINGY = 0x0002;
|
||||
static constexpr uint16_t LEVER = 0x0004;
|
||||
static constexpr uint16_t SIGHTX = 0x0008;
|
||||
static constexpr uint16_t SIGHTY = 0x0010;
|
||||
static constexpr uint16_t LPEDAL = 0x0020;
|
||||
static constexpr uint16_t MPEDAL = 0x0040;
|
||||
static constexpr uint16_t RPEDAL = 0x0080;
|
||||
static constexpr uint16_t TUNER = 0x0100;
|
||||
static constexpr uint16_t GEAR = 0x0200;
|
||||
};
|
||||
|
||||
namespace Gear
|
||||
{
|
||||
static constexpr int8_t R = 7;
|
||||
static constexpr int8_t N = 8;
|
||||
static constexpr int8_t G1 = 9;
|
||||
static constexpr int8_t G2 = 10;
|
||||
static constexpr int8_t G3 = 11;
|
||||
static constexpr int8_t G4 = 12;
|
||||
static constexpr int8_t G5 = 13;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct InReport
|
||||
{
|
||||
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
|
||||
};
|
||||
static_assert(sizeof(InReport) == 26, "XboxOGSB::InReport is not the correct size");
|
||||
|
||||
struct OutReport
|
||||
{
|
||||
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;
|
||||
};
|
||||
static_assert(sizeof(OutReport) == 22, "XboxOGSB::OutReport is not the correct size");
|
||||
#pragma pack(pop)
|
||||
|
||||
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
|
||||
static const uint8_t STRING_MANUFACTURER[] = "";
|
||||
static const uint8_t STRING_PRODUCT[] = "";
|
||||
static const uint8_t STRING_VERSION[] = "1.0";
|
||||
|
||||
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
|
||||
{
|
||||
STRING_LANGUAGE,
|
||||
STRING_MANUFACTURER,
|
||||
STRING_PRODUCT,
|
||||
STRING_VERSION
|
||||
};
|
||||
|
||||
static constexpr uint8_t INTERFACE_CLASS = 0x58;
|
||||
static constexpr uint8_t INTERFACE_SUBCLASS = 0x42;
|
||||
static constexpr uint16_t DRIVER_LEN = 9+7+7;
|
||||
static constexpr uint16_t CONFIG_DESC_TOTAL_LEN = DRIVER_LEN + TUD_CONFIG_DESC_LEN;
|
||||
|
||||
enum Interface
|
||||
{
|
||||
NUM_STEELBATTALION = 0,
|
||||
NUM_TOTAL
|
||||
};
|
||||
|
||||
#define TUD_XID_SB_DESCRIPTOR(_itfnum, _epout, _epin) \
|
||||
/* Interface */\
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, INTERFACE_CLASS, 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 uint8_t const CONFIGURATION_DESCRIPTORS[] =
|
||||
{
|
||||
TUD_CONFIG_DESCRIPTOR( 1,
|
||||
Interface::NUM_TOTAL,
|
||||
0,
|
||||
CONFIG_DESC_TOTAL_LEN,
|
||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
|
||||
500),
|
||||
TUD_XID_SB_DESCRIPTOR( Interface::NUM_STEELBATTALION,
|
||||
Interface::NUM_STEELBATTALION + 1,
|
||||
0x80 | (Interface::NUM_STEELBATTALION + 1)),
|
||||
};
|
||||
|
||||
static const uint8_t XID_DEVICE_DESCRIPTORS[] =
|
||||
{
|
||||
0x10,
|
||||
0x42,
|
||||
0x00, 0x01,
|
||||
0x80,
|
||||
0x01,
|
||||
0x1A,
|
||||
0x16,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
static const uint8_t XID_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 XID_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
|
||||
};
|
||||
|
||||
}; // SB
|
||||
|
||||
namespace XR //XRemote
|
||||
{
|
||||
#define XID_REMOTE_INTERFACE_CLASS 0x58
|
||||
#define XID_REMOTE_INTERFACE_SUBCLASS 0x42
|
||||
#define XID_XREMOTE_ROM_CLASS 0x59
|
||||
|
||||
namespace ButtonCode
|
||||
{
|
||||
static constexpr uint16_t SELECT = 0x0A0B;
|
||||
static constexpr uint16_t UP = 0x0AA6;
|
||||
static constexpr uint16_t DOWN = 0x0AA7;
|
||||
static constexpr uint16_t RIGHT = 0x0AA8;
|
||||
static constexpr uint16_t LEFT = 0x0AA9;
|
||||
static constexpr uint16_t INFO = 0x0AC3;
|
||||
static constexpr uint16_t NINE = 0x0AC6;
|
||||
static constexpr uint16_t EIGHT = 0x0AC7;
|
||||
static constexpr uint16_t SEVEN = 0x0AC8;
|
||||
static constexpr uint16_t SIX = 0x0AC9;
|
||||
static constexpr uint16_t FIVE = 0x0ACA;
|
||||
static constexpr uint16_t FOUR = 0x0ACB;
|
||||
static constexpr uint16_t THREE = 0x0ACC;
|
||||
static constexpr uint16_t TWO = 0x0ACD;
|
||||
static constexpr uint16_t ONE = 0x0ACE;
|
||||
static constexpr uint16_t ZERO = 0x0ACF;
|
||||
static constexpr uint16_t DISPLAY = 0x0AD5;
|
||||
static constexpr uint16_t BACK = 0x0AD8;
|
||||
static constexpr uint16_t SKIP_MINUS = 0x0ADD;
|
||||
static constexpr uint16_t SKIP_PLUS = 0x0ADF;
|
||||
static constexpr uint16_t STOP = 0x0AE0;
|
||||
static constexpr uint16_t REVERSE = 0x0AE2;
|
||||
static constexpr uint16_t FORWARD = 0x0AE3;
|
||||
static constexpr uint16_t TITLE = 0x0AE5;
|
||||
static constexpr uint16_t PAUSE = 0x0AE6;
|
||||
static constexpr uint16_t PLAY = 0x0AEA;
|
||||
static constexpr uint16_t MENU = 0x0AF7;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct InReport
|
||||
{
|
||||
uint8_t zero;
|
||||
uint8_t bLength;
|
||||
uint16_t buttonCode;
|
||||
uint16_t timeElapsed; // ms since last button press
|
||||
};
|
||||
static_assert(sizeof(InReport) == 6, "XboxOGXRemote::InReport is not the correct size");
|
||||
#pragma pack(pop)
|
||||
|
||||
static constexpr uint16_t DRIVER_LEN = 9+7+9;
|
||||
|
||||
// enum xboxog_xremote_xid_interface
|
||||
// {
|
||||
// ITF_NUM_XID_XREMOTE = 0,
|
||||
// ITF_NUM_XID_XREMOTE_ROM,
|
||||
// XID_REMOTE_ITF_NUM_TOTAL
|
||||
// };
|
||||
|
||||
// #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_REMOTE_INTERFACE_CLASS, XID_REMOTE_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
|
||||
|
||||
|
||||
// #define XID_REMOTE_CONFIG_TOTAL_LEN \
|
||||
// (TUD_CONFIG_DESC_LEN) + \
|
||||
// (TUD_XID_XREMOTE_DESC_LEN)
|
||||
|
||||
// static const uint8_t CONFIGURATION_DESCRIPTORS[] =
|
||||
// {
|
||||
// // Config number, interface count, string index, total length, attribute, power in mA
|
||||
// TUD_CONFIG_DESCRIPTOR( 1,
|
||||
// XID_REMOTE_ITF_NUM_TOTAL,
|
||||
// 0,
|
||||
// XID_REMOTE_CONFIG_TOTAL_LEN,
|
||||
// TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
|
||||
// 500),
|
||||
|
||||
// TUD_XID_XREMOTE_DESCRIPTOR( ITF_NUM_XID_XREMOTE,
|
||||
// 0x80 | (ITF_NUM_XID_XREMOTE + 1)),
|
||||
// };
|
||||
|
||||
static const uint8_t DEVICE_DESCRIPTORS[] =
|
||||
{
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x10, 0x01, // bcdUSB 1.10
|
||||
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x40, // bMaxPacketSize0 64
|
||||
0x5E, 0x04, // idVendor 0x045E
|
||||
0x84, 0x02, // idProduct 0x0284
|
||||
0x30, 0x01, // bcdDevice 2.30
|
||||
0x00, // iManufacturer (String Index)
|
||||
0x00, // iProduct (String Index)
|
||||
0x00, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
static const uint8_t CONFIGURATION_DESCRIPTORS[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x22, 0x00, // wTotalLength 34
|
||||
0x02, // bNumInterfaces 2
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0x80, // bmAttributes
|
||||
0xFA, // bMaxPower 500mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x01, // bNumEndpoints 1
|
||||
0x58, // bInterfaceClass
|
||||
0x42, // bInterfaceSubClass
|
||||
0x00, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x81, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x08, 0x00, // wMaxPacketSize 8
|
||||
0x10, // bInterval 16 (unit depends on device speed)
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x01, // bInterfaceNumber 1
|
||||
0x00, // bAlternateSetting
|
||||
0x00, // bNumEndpoints 0
|
||||
0x59, // bInterfaceClass
|
||||
0x00, // bInterfaceSubClass
|
||||
0x00, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
};
|
||||
|
||||
static const uint8_t XID_DEVICE_DESCRIPTORS[] =
|
||||
{
|
||||
0x08,
|
||||
0x42,
|
||||
0x00, 0x01,
|
||||
0x03,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00
|
||||
};
|
||||
|
||||
}; // XR
|
||||
|
||||
namespace COM
|
||||
{
|
||||
static constexpr uint8_t INTERFACE_CLASS = 0x78;
|
||||
static constexpr uint8_t INTERFACE_SUBCLASS = 0x00;
|
||||
|
||||
static const uint8_t DEVICE_DESCRIPTORS[] =
|
||||
{
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x10, 0x01, // bcdUSB 1.10
|
||||
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x08, // bMaxPacketSize0 8
|
||||
0x5E, 0x04, // idVendor 0x045E
|
||||
0x83, 0x02, // idProduct 0x0283
|
||||
0x58, 0x01, // bcdDevice 2.58
|
||||
0x00, // iManufacturer (String Index)
|
||||
0x00, // iProduct (String Index)
|
||||
0x00, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
static const uint8_t CONFIGURATION_DESCRIPTORS[]
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x2D, 0x00, // wTotalLength 45
|
||||
0x02, // bNumInterfaces 2
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0x80, // bmAttributes
|
||||
0x32, // bMaxPower 100mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x01, // bNumEndpoints 1
|
||||
0x78, // bInterfaceClass
|
||||
0x00, // bInterfaceSubClass
|
||||
0x00, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x09, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x04, // bEndpointAddress (OUT/H2D)
|
||||
0x05, // bmAttributes (Isochronous, Async, Data EP)
|
||||
0x30, 0x00, // wMaxPacketSize 48
|
||||
0x01, // bInterval 1 (unit depends on device speed)
|
||||
0x00, 0x00,
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x01, // bInterfaceNumber 1
|
||||
0x00, // bAlternateSetting
|
||||
0x01, // bNumEndpoints 1
|
||||
0x78, // bInterfaceClass
|
||||
0x00, // bInterfaceSubClass
|
||||
0x00, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x09, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x85, // bEndpointAddress (IN/D2H)
|
||||
0x05, // bmAttributes (Isochronous, Async, Data EP)
|
||||
0x30, 0x00, // wMaxPacketSize 48
|
||||
0x01, // bInterval 1 (unit depends on device speed)
|
||||
0x00, 0x00,
|
||||
};
|
||||
|
||||
}; // COM
|
||||
|
||||
}; // namespace XboxOG
|
||||
|
||||
#endif // _XBOX_OG_DESCRIPTORS_H_
|
||||
63
Firmware/RP2040/src/Descriptors/XboxOne.h
Normal file
63
Firmware/RP2040/src/Descriptors/XboxOne.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef _XBOX_ONE_DESCRIPTORS_H_
|
||||
#define _XBOX_ONE_DESCRIPTORS_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace XboxOne
|
||||
{
|
||||
namespace Buttons0
|
||||
{
|
||||
static constexpr uint8_t SYNC = (1 << 0);
|
||||
static constexpr uint8_t GUIDE = (1 << 1);
|
||||
static constexpr uint8_t START = (1 << 2);
|
||||
static constexpr uint8_t BACK = (1 << 3);
|
||||
static constexpr uint8_t A = (1 << 4);
|
||||
static constexpr uint8_t B = (1 << 5);
|
||||
static constexpr uint8_t X = (1 << 6);
|
||||
static constexpr uint8_t Y = (1 << 7);
|
||||
};
|
||||
namespace Buttons1
|
||||
{
|
||||
static constexpr uint8_t DPAD_UP = (1 << 0);
|
||||
static constexpr uint8_t DPAD_DOWN = (1 << 1);
|
||||
static constexpr uint8_t DPAD_LEFT = (1 << 2);
|
||||
static constexpr uint8_t DPAD_RIGHT = (1 << 3);
|
||||
static constexpr uint8_t LB = (1 << 4);
|
||||
static constexpr uint8_t RB = (1 << 5);
|
||||
static constexpr uint8_t L3 = (1 << 6);
|
||||
static constexpr uint8_t R3 = (1 << 7);
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct InReport
|
||||
{
|
||||
struct GipHeader
|
||||
{
|
||||
uint8_t command;
|
||||
uint8_t client : 4;
|
||||
uint8_t needsAck : 1;
|
||||
uint8_t internal : 1;
|
||||
uint8_t chunkStart : 1;
|
||||
uint8_t chunked : 1;
|
||||
uint8_t sequence;
|
||||
uint8_t length;
|
||||
} header;
|
||||
|
||||
uint8_t buttons[2];
|
||||
|
||||
uint16_t trigger_l;
|
||||
uint16_t trigger_r;
|
||||
|
||||
int16_t joystick_lx;
|
||||
int16_t joystick_ly;
|
||||
int16_t joystick_rx;
|
||||
int16_t joystick_ry;
|
||||
|
||||
uint8_t reserved[18]; // 18-byte padding at the end
|
||||
};
|
||||
static_assert(sizeof(InReport) == 36, "XboxOne::InReport is not the correct size");
|
||||
#pragma pack(pop)
|
||||
|
||||
}; // namespace XboxOne
|
||||
|
||||
#endif // _XBOX_ONE_DESCRIPTORS_H_
|
||||
623
Firmware/RP2040/src/Gamepad.h
Normal file
623
Firmware/RP2040/src/Gamepad.h
Normal file
@@ -0,0 +1,623 @@
|
||||
#ifndef _GAMEPAD_H_
|
||||
#define _GAMEPAD_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
#include <limits>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
#include "UserSettings/UserProfile.h"
|
||||
|
||||
namespace INT_16
|
||||
{
|
||||
static constexpr int16_t MIN = INT16_MIN;
|
||||
static constexpr int16_t MID = 0;
|
||||
static constexpr int16_t MAX = INT16_MAX;
|
||||
}
|
||||
namespace UINT_16
|
||||
{
|
||||
static constexpr uint16_t MIN = 0;
|
||||
static constexpr uint16_t MID = 0x8000;
|
||||
static constexpr uint16_t MAX = 0xFFFF;
|
||||
}
|
||||
namespace UINT_8
|
||||
{
|
||||
static constexpr uint8_t MAX = 0xFF;
|
||||
static constexpr uint8_t MID = 0x80;
|
||||
static constexpr uint8_t MIN = 0x00;
|
||||
}
|
||||
namespace INT_10
|
||||
{
|
||||
static constexpr int32_t MIN = -512;
|
||||
static constexpr int32_t MAX = 511;
|
||||
}
|
||||
namespace UINT_10
|
||||
{
|
||||
static constexpr int32_t MAX = 1023;
|
||||
}
|
||||
|
||||
class Gamepad
|
||||
{
|
||||
public:
|
||||
struct DPad
|
||||
{
|
||||
static constexpr uint8_t UP = 0x01;
|
||||
static constexpr uint8_t DOWN = 0x02;
|
||||
static constexpr uint8_t LEFT = 0x04;
|
||||
static constexpr uint8_t RIGHT = 0x08;
|
||||
static constexpr uint8_t UP_LEFT = UP | LEFT;
|
||||
static constexpr uint8_t UP_RIGHT = UP | RIGHT;
|
||||
static constexpr uint8_t DOWN_LEFT = DOWN | LEFT;
|
||||
static constexpr uint8_t DOWN_RIGHT = DOWN | RIGHT;
|
||||
static constexpr uint8_t NONE = 0x00;
|
||||
};
|
||||
|
||||
struct Button
|
||||
{
|
||||
static constexpr uint16_t A = 0x0001;
|
||||
static constexpr uint16_t B = 0x0002;
|
||||
static constexpr uint16_t X = 0x0004;
|
||||
static constexpr uint16_t Y = 0x0008;
|
||||
static constexpr uint16_t L3 = 0x0010;
|
||||
static constexpr uint16_t R3 = 0x0020;
|
||||
static constexpr uint16_t BACK = 0x0040;
|
||||
static constexpr uint16_t START = 0x0080;
|
||||
static constexpr uint16_t LB = 0x0100;
|
||||
static constexpr uint16_t RB = 0x0200;
|
||||
static constexpr uint16_t SYS = 0x0400;
|
||||
static constexpr uint16_t MISC = 0x0800;
|
||||
};
|
||||
|
||||
using Chatpad = std::array<uint8_t, 3>;
|
||||
|
||||
Gamepad()
|
||||
{
|
||||
reset_pad();
|
||||
reset_rumble();
|
||||
setup_deadzones();
|
||||
};
|
||||
|
||||
~Gamepad() = default;
|
||||
|
||||
class Values //Store and scale values
|
||||
{
|
||||
public:
|
||||
Values() : type_(Type::UINT8), uint8_(0) {};
|
||||
~Values() = default;
|
||||
|
||||
inline int16_t int16(const bool invert = false) const
|
||||
{
|
||||
int16_t result = 0;
|
||||
switch (type_.load())
|
||||
{
|
||||
case Type::INT16:
|
||||
result = int16_.load();
|
||||
break;
|
||||
case Type::UINT16:
|
||||
result = uint16_to_int16(uint16_.load());
|
||||
break;
|
||||
case Type::UINT8:
|
||||
result = uint8_to_int16(uint8_.load());
|
||||
break;
|
||||
case Type::INT8:
|
||||
result = int8_to_int16(int8_.load());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return invert ? invert_joy(result) : result;
|
||||
}
|
||||
|
||||
inline uint16_t uint16(const bool invert = false) const
|
||||
{
|
||||
uint16_t result = 0;
|
||||
switch (type_.load())
|
||||
{
|
||||
case Type::UINT16:
|
||||
result = uint16_.load();
|
||||
break;
|
||||
case Type::INT16:
|
||||
result = int16_to_uint16(int16_.load());
|
||||
break;
|
||||
case Type::UINT8:
|
||||
result = uint8_to_uint16(uint8_.load());
|
||||
break;
|
||||
case Type::INT8:
|
||||
result = int8_to_uint16(int8_.load());
|
||||
break;
|
||||
}
|
||||
|
||||
return invert ? invert_joy(result) : result;
|
||||
}
|
||||
|
||||
inline uint8_t uint8(const bool invert = false) const
|
||||
{
|
||||
uint8_t result = 0;
|
||||
switch (type_.load())
|
||||
{
|
||||
case Type::UINT8:
|
||||
result = uint8_.load();
|
||||
break;
|
||||
case Type::INT16:
|
||||
result = int16_to_uint8(int16_.load());
|
||||
break;
|
||||
case Type::UINT16:
|
||||
result = uint16_to_uint8(uint16_.load());
|
||||
break;
|
||||
case Type::INT8:
|
||||
result = int8_to_uint8(int8_.load());
|
||||
break;
|
||||
}
|
||||
return invert ? invert_joy(result) : result;
|
||||
}
|
||||
|
||||
inline int8_t int8(bool invert = false) const
|
||||
{
|
||||
int8_t result = 0;
|
||||
switch (type_.load())
|
||||
{
|
||||
case Type::INT8:
|
||||
result = int8_.load();
|
||||
break;
|
||||
case Type::INT16:
|
||||
result = int16_to_int8(int16_.load());
|
||||
break;
|
||||
case Type::UINT16:
|
||||
result = uint16_to_int8(uint16_.load());
|
||||
break;
|
||||
case Type::UINT8:
|
||||
result = uint8_to_int8(uint8_.load());
|
||||
break;
|
||||
}
|
||||
return invert ? invert_joy(result) : result;
|
||||
}
|
||||
|
||||
inline void set_value(int16_t value) { type_.store(Type::INT16); int16_.store(value); }
|
||||
inline void set_value(uint16_t value) { type_.store(Type::UINT16); uint16_.store(value); }
|
||||
inline void set_value(int8_t value) { type_.store(Type::INT8); int8_.store(value); }
|
||||
inline void set_value(uint8_t value) { type_.store(Type::UINT8); uint8_.store(value); }
|
||||
|
||||
inline void reset_value() { type_ = Type::INT16; uint16_ = 0; };
|
||||
|
||||
static inline uint8_t invert_joy(uint8_t value)
|
||||
{
|
||||
return static_cast<uint8_t>(UINT_8::MAX - value);
|
||||
}
|
||||
static inline int8_t invert_joy(int8_t value)
|
||||
{
|
||||
return (value == std::numeric_limits<int8_t>::min()) ? std::numeric_limits<int8_t>::max() : -value;
|
||||
}
|
||||
static inline uint16_t invert_joy(uint16_t value)
|
||||
{
|
||||
return static_cast<uint16_t>(std::numeric_limits<uint16_t>::max() - value);
|
||||
}
|
||||
static inline int16_t invert_joy(int16_t value)
|
||||
{
|
||||
return (value == std::numeric_limits<int16_t>::min()) ? std::numeric_limits<int16_t>::max() : -value;
|
||||
}
|
||||
|
||||
static inline uint8_t int16_to_uint8(int16_t value)
|
||||
{
|
||||
uint16_t shifted_value = static_cast<uint16_t>(value + UINT_16::MID);
|
||||
return static_cast<uint8_t>(shifted_value >> 8);
|
||||
}
|
||||
static inline uint16_t int16_to_uint16(int16_t value)
|
||||
{
|
||||
return static_cast<uint16_t>(value + UINT_16::MID);
|
||||
}
|
||||
static inline int8_t int16_to_int8(int16_t value)
|
||||
{
|
||||
return static_cast<int8_t>((value + UINT_16::MID) >> 8);
|
||||
}
|
||||
|
||||
static inline uint8_t uint16_to_uint8(uint16_t value)
|
||||
{
|
||||
return static_cast<uint8_t>(value >> 8);
|
||||
}
|
||||
static inline int16_t uint16_to_int16(uint16_t value)
|
||||
{
|
||||
return static_cast<int16_t>(value - UINT_16::MID);
|
||||
}
|
||||
static inline int8_t uint16_to_int8(uint16_t value)
|
||||
{
|
||||
return static_cast<int8_t>((value >> 8) - UINT_8::MID);
|
||||
}
|
||||
|
||||
static inline int16_t uint8_to_int16(uint8_t value)
|
||||
{
|
||||
return static_cast<int16_t>((static_cast<int32_t>(value) << 8) - UINT_16::MID);
|
||||
}
|
||||
static inline uint16_t uint8_to_uint16(uint8_t value)
|
||||
{
|
||||
return static_cast<uint16_t>(value) << 8;
|
||||
}
|
||||
static inline int8_t uint8_to_int8(uint8_t value)
|
||||
{
|
||||
return static_cast<int8_t>(value - UINT_8::MID);
|
||||
}
|
||||
|
||||
static inline int16_t int8_to_int16(int8_t value)
|
||||
{
|
||||
return static_cast<int16_t>(value) << 8;
|
||||
}
|
||||
static inline uint16_t int8_to_uint16(int8_t value)
|
||||
{
|
||||
return static_cast<uint16_t>((value + UINT_8::MID) << 8);
|
||||
}
|
||||
static inline uint8_t int8_to_uint8(int8_t value)
|
||||
{
|
||||
return static_cast<uint8_t>(value + UINT_8::MID);
|
||||
}
|
||||
|
||||
static inline uint8_t int10_to_uint8(int32_t value)
|
||||
{
|
||||
value = value - INT_10::MIN;
|
||||
|
||||
if (value >= UINT_10::MAX)
|
||||
{
|
||||
return UINT_8::MAX;
|
||||
}
|
||||
else if (value <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return static_cast<uint8_t>(value >> 2);
|
||||
}
|
||||
static inline int16_t int10_to_int16(int32_t value)
|
||||
{
|
||||
constexpr int32_t scale_factor = INT_16::MAX - INT_16::MIN;
|
||||
constexpr int32_t range = INT_10::MAX - INT_10::MIN;
|
||||
|
||||
if (value >= INT_10::MAX)
|
||||
{
|
||||
return INT_16::MAX;
|
||||
}
|
||||
else if (value <= INT_10::MIN)
|
||||
{
|
||||
return INT_16::MIN;
|
||||
}
|
||||
|
||||
int32_t scaled_value = (value - INT_10::MIN) * scale_factor;
|
||||
return static_cast<int16_t>(scaled_value / range + INT_16::MIN);
|
||||
}
|
||||
static inline uint8_t uint10_to_uint8(int32_t value)
|
||||
{
|
||||
if (value > UINT_10::MAX)
|
||||
{
|
||||
value = UINT_10::MAX;
|
||||
}
|
||||
else if (value < 0)
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
return static_cast<uint8_t>(value >> 2);
|
||||
}
|
||||
|
||||
private:
|
||||
enum class Type { INT16, UINT16, INT8, UINT8 };
|
||||
std::atomic<Type> type_;
|
||||
union {
|
||||
std::atomic<int16_t> int16_;
|
||||
std::atomic<uint16_t> uint16_;
|
||||
std::atomic<int8_t> int8_;
|
||||
std::atomic<uint8_t> uint8_;
|
||||
};
|
||||
}; //Values
|
||||
|
||||
//Get
|
||||
|
||||
inline bool analog_enabled() const { return analog_enabled_; }
|
||||
|
||||
inline uint8_t get_dpad_buttons() const { return pad_state_.dpad; }
|
||||
inline uint16_t get_buttons() const { return pad_state_.buttons; }
|
||||
|
||||
inline bool get_dpad_up() const { return (pad_state_.dpad & DPad::UP); }
|
||||
inline bool get_dpad_down() const { return (pad_state_.dpad & DPad::DOWN); }
|
||||
inline bool get_dpad_left() const { return (pad_state_.dpad & DPad::LEFT); }
|
||||
inline bool get_dpad_right() const { return (pad_state_.dpad & DPad::RIGHT); }
|
||||
|
||||
inline bool get_button_a() const { return (pad_state_.buttons & Button::A); }
|
||||
inline bool get_button_b() const { return (pad_state_.buttons & Button::B); }
|
||||
inline bool get_button_x() const { return (pad_state_.buttons & Button::X); }
|
||||
inline bool get_button_y() const { return (pad_state_.buttons & Button::Y); }
|
||||
inline bool get_button_lb() const { return (pad_state_.buttons & Button::LB); }
|
||||
inline bool get_button_rb() const { return (pad_state_.buttons & Button::RB); }
|
||||
inline bool get_button_l3() const { return (pad_state_.buttons & Button::L3); }
|
||||
inline bool get_button_r3() const { return (pad_state_.buttons & Button::R3); }
|
||||
inline bool get_button_start() const { return (pad_state_.buttons & Button::START); }
|
||||
inline bool get_button_back() const { return (pad_state_.buttons & Button::BACK); }
|
||||
inline bool get_button_sys() const { return (pad_state_.buttons & Button::SYS); }
|
||||
inline bool get_button_misc() const { return (pad_state_.buttons & Button::MISC); }
|
||||
|
||||
inline const Values& get_trigger_l() const { return pad_state_.triggers.l; }
|
||||
inline const Values& get_trigger_r() const { return pad_state_.triggers.r; }
|
||||
|
||||
inline const Values& get_joystick_lx() const { return pad_state_.joysticks.lx; }
|
||||
inline const Values& get_joystick_ly() const { return pad_state_.joysticks.ly; }
|
||||
inline const Values& get_joystick_rx() const { return pad_state_.joysticks.rx; }
|
||||
inline const Values& get_joystick_ry() const { return pad_state_.joysticks.ry; }
|
||||
|
||||
inline uint8_t get_analog_up() const { return pad_state_.analog_buttons.up; }
|
||||
inline uint8_t get_analog_down() const { return pad_state_.analog_buttons.down; }
|
||||
inline uint8_t get_analog_left() const { return pad_state_.analog_buttons.left; }
|
||||
inline uint8_t get_analog_right() const { return pad_state_.analog_buttons.right; }
|
||||
inline uint8_t get_analog_a() const { return pad_state_.analog_buttons.a; }
|
||||
inline uint8_t get_analog_b() const { return pad_state_.analog_buttons.b; }
|
||||
inline uint8_t get_analog_x() const { return pad_state_.analog_buttons.x; }
|
||||
inline uint8_t get_analog_y() const { return pad_state_.analog_buttons.y; }
|
||||
inline uint8_t get_analog_lb() const { return pad_state_.analog_buttons.lb; }
|
||||
inline uint8_t get_analog_rb() const { return pad_state_.analog_buttons.rb; }
|
||||
|
||||
inline const Values& get_rumble_l() const { return rumble_state_.l; }
|
||||
inline const Values& get_rumble_r() const { return rumble_state_.r; }
|
||||
|
||||
inline Chatpad get_chatpad() const
|
||||
{
|
||||
return
|
||||
{
|
||||
pad_state_.chatpad[0],
|
||||
pad_state_.chatpad[1],
|
||||
pad_state_.chatpad[2]
|
||||
};
|
||||
}
|
||||
|
||||
//Set
|
||||
|
||||
void set_analog_enabled(bool value) { analog_enabled_ = value; }
|
||||
void set_profile(const UserProfile& user_profile)
|
||||
{
|
||||
profile_ = user_profile;
|
||||
setup_deadzones();
|
||||
}
|
||||
|
||||
inline void set_dpad(uint8_t value) { pad_state_.dpad = value; }
|
||||
|
||||
inline void set_dpad_up() { pad_state_.dpad |= profile_.dpad_up; }
|
||||
inline void set_dpad_down() { pad_state_.dpad |= profile_.dpad_down; }
|
||||
inline void set_dpad_left() { pad_state_.dpad |= profile_.dpad_left; }
|
||||
inline void set_dpad_right() { pad_state_.dpad |= profile_.dpad_right; }
|
||||
inline void set_dpad_up_left() { pad_state_.dpad |= (profile_.dpad_up | profile_.dpad_left); }
|
||||
inline void set_dpad_up_right() { pad_state_.dpad |= (profile_.dpad_up | profile_.dpad_right); }
|
||||
inline void set_dpad_down_left() { pad_state_.dpad |= (profile_.dpad_down | profile_.dpad_left); }
|
||||
inline void set_dpad_down_right() { pad_state_.dpad |= (profile_.dpad_down | profile_.dpad_right); }
|
||||
|
||||
inline void set_buttons(uint16_t value) { pad_state_.buttons = value; }
|
||||
|
||||
inline void set_button_a() { pad_state_.buttons |= profile_.button_a; }
|
||||
inline void set_button_b() { pad_state_.buttons |= profile_.button_b; }
|
||||
inline void set_button_x() { pad_state_.buttons |= profile_.button_x; }
|
||||
inline void set_button_y() { pad_state_.buttons |= profile_.button_y; }
|
||||
inline void set_button_lb() { pad_state_.buttons |= profile_.button_lb; }
|
||||
inline void set_button_rb() { pad_state_.buttons |= profile_.button_rb; }
|
||||
inline void set_button_l3() { pad_state_.buttons |= profile_.button_l3; }
|
||||
inline void set_button_r3() { pad_state_.buttons |= profile_.button_r3; }
|
||||
inline void set_button_start() { pad_state_.buttons |= profile_.button_start; }
|
||||
inline void set_button_back() { pad_state_.buttons |= profile_.button_back; }
|
||||
inline void set_button_sys() { pad_state_.buttons |= profile_.button_sys; }
|
||||
inline void set_button_misc() { pad_state_.buttons |= profile_.button_misc; }
|
||||
|
||||
inline void set_analog_up(uint8_t value) { *analog_map_[profile_.analog_off_up] = value; }
|
||||
inline void set_analog_down(uint8_t value) { *analog_map_[profile_.analog_off_down] = value; }
|
||||
inline void set_analog_left(uint8_t value) { *analog_map_[profile_.analog_off_left] = value; }
|
||||
inline void set_analog_right(uint8_t value) { *analog_map_[profile_.analog_off_right] = value; }
|
||||
inline void set_analog_a(uint8_t value) { *analog_map_[profile_.analog_off_a] = value; }
|
||||
inline void set_analog_b(uint8_t value) { *analog_map_[profile_.analog_off_b] = value; }
|
||||
inline void set_analog_x(uint8_t value) { *analog_map_[profile_.analog_off_x] = value; }
|
||||
inline void set_analog_y(uint8_t value) { *analog_map_[profile_.analog_off_y] = value; }
|
||||
inline void set_analog_lb(uint8_t value) { *analog_map_[profile_.analog_off_lb] = value; }
|
||||
inline void set_analog_rb(uint8_t value) { *analog_map_[profile_.analog_off_rb] = value; }
|
||||
|
||||
inline void set_trigger_l(const uint8_t value) { pad_state_.triggers.l.set_value((value > dz_trig_uint8_.l) ? value : UINT_8::MIN); }
|
||||
inline void set_trigger_l(const uint16_t value) { pad_state_.triggers.l.set_value((value > dz_trig_uint16_.l) ? value : UINT_16::MIN); }
|
||||
|
||||
inline void set_trigger_r(const uint8_t value) { pad_state_.triggers.r.set_value((value > dz_trig_uint8_.r) ? value : UINT_8::MIN); }
|
||||
inline void set_trigger_r(const uint16_t value) { pad_state_.triggers.r.set_value((value > dz_trig_uint16_.r) ? value : UINT_16::MIN); }
|
||||
|
||||
inline void set_joystick_lx(const int16_t value, const bool invert = false) { pad_state_.joysticks.lx.set_value((value < dz_joy_int16_.l_neg || value > dz_joy_int16_.l_pos) ? (invert ? Values::invert_joy(value) : value) : INT_16::MID); }
|
||||
inline void set_joystick_lx(const uint8_t value, const bool invert = false) { pad_state_.joysticks.lx.set_value((value < dz_joy_uint8_.l_neg || value > dz_joy_uint8_.l_pos) ? (invert ? Values::invert_joy(value) : value) : UINT_8::MID); }
|
||||
|
||||
inline void set_joystick_ly(const int16_t value, const bool invert = false) { pad_state_.joysticks.ly.set_value((value < dz_joy_int16_.l_neg || value > dz_joy_int16_.l_pos) ? ((invert ^ profile_.invert_ly) ? Values::invert_joy(value) : value) : INT_16::MID); }
|
||||
inline void set_joystick_ly(const uint8_t value, const bool invert = false) { pad_state_.joysticks.ly.set_value((value < dz_joy_uint8_.l_neg || value > dz_joy_uint8_.l_pos) ? ((invert ^ profile_.invert_ly) ? Values::invert_joy(value) : value) : UINT_8::MID); }
|
||||
|
||||
inline void set_joystick_rx(const int16_t value, const bool invert = false) { pad_state_.joysticks.rx.set_value((value < dz_joy_int16_.r_neg || value > dz_joy_int16_.r_pos) ? (invert ? Values::invert_joy(value) : value) : INT_16::MID); }
|
||||
inline void set_joystick_rx(const uint8_t value, const bool invert = false) { pad_state_.joysticks.rx.set_value((value < dz_joy_uint8_.r_neg || value > dz_joy_uint8_.r_pos) ? (invert ? Values::invert_joy(value) : value) : UINT_8::MID); }
|
||||
|
||||
inline void set_joystick_ry(const int16_t value, const bool invert = false) { pad_state_.joysticks.ry.set_value((value < dz_joy_int16_.r_neg || value > dz_joy_int16_.r_pos) ? ((invert ^ profile_.invert_ry) ? Values::invert_joy(value) : value) : INT_16::MID); }
|
||||
inline void set_joystick_ry(const uint8_t value, const bool invert = false) { pad_state_.joysticks.ry.set_value((value < dz_joy_uint8_.r_neg || value > dz_joy_uint8_.r_pos) ? ((invert ^ profile_.invert_ry) ? Values::invert_joy(value) : value) : UINT_8::MID); }
|
||||
|
||||
// 10 bit methods for weird stuff
|
||||
inline void set_trigger_l_uint10(const int32_t value)
|
||||
{
|
||||
uint8_t new_value = Values::uint10_to_uint8(value);
|
||||
pad_state_.triggers.l.set_value((new_value > dz_trig_uint8_.l) ? new_value : UINT_8::MIN);
|
||||
}
|
||||
inline void set_trigger_r_uint10(const int32_t value)
|
||||
{
|
||||
uint8_t new_value = Values::uint10_to_uint8(value);
|
||||
pad_state_.triggers.r.set_value((new_value > dz_trig_uint8_.r) ? new_value : UINT_8::MIN);
|
||||
}
|
||||
inline void set_joystick_lx_int10(const int32_t value, const bool invert = false)
|
||||
{
|
||||
uint8_t new_value = Values::int10_to_uint8(value);
|
||||
pad_state_.joysticks.lx.set_value((new_value < dz_joy_uint8_.l_neg || new_value > dz_joy_uint8_.l_pos) ? (invert ? Values::invert_joy(new_value) : new_value) : UINT_8::MID);
|
||||
}
|
||||
inline void set_joystick_ly_int10(const int32_t value, const bool invert = false)
|
||||
{
|
||||
uint8_t new_value = Values::int10_to_uint8(value);
|
||||
pad_state_.joysticks.ly.set_value((new_value < dz_joy_uint8_.l_neg || new_value > dz_joy_uint8_.l_pos) ? ((invert ^ profile_.invert_ly) ? Values::invert_joy(new_value) : new_value) : UINT_8::MID);
|
||||
}
|
||||
inline void set_joystick_rx_int10(const int32_t value, const bool invert = false)
|
||||
{
|
||||
uint8_t new_value = Values::int10_to_uint8(value);
|
||||
pad_state_.joysticks.rx.set_value((new_value < dz_joy_uint8_.r_neg || new_value > dz_joy_uint8_.r_pos) ? (invert ? Values::invert_joy(new_value) : new_value) : UINT_8::MID);
|
||||
}
|
||||
inline void set_joystick_ry_int10(const int32_t value, const bool invert = false)
|
||||
{
|
||||
uint8_t new_value = Values::int10_to_uint8(value);
|
||||
pad_state_.joysticks.ry.set_value((new_value < dz_joy_uint8_.r_neg || new_value > dz_joy_uint8_.r_pos) ? ((invert ^ profile_.invert_ry) ? Values::invert_joy(new_value) : new_value) : UINT_8::MID);
|
||||
}
|
||||
// inline void set_joystick_lx_int10(const int32_t value, const bool invert = false)
|
||||
// {
|
||||
// int16_t new_value = Values::int10_to_int16(value);
|
||||
// pad_state_.joysticks.lx.set_value((new_value < dz_joy_int16_.l_neg || new_value > dz_joy_int16_.l_pos) ? (invert ? Values::invert_joy(new_value) : new_value) : INT_16::MID);
|
||||
// }
|
||||
// inline void set_joystick_ly_int10(const int32_t value, const bool invert = false)
|
||||
// {
|
||||
// int16_t new_value = Values::int10_to_int16(value);
|
||||
// pad_state_.joysticks.ly.set_value((new_value < dz_joy_int16_.l_neg || new_value > dz_joy_int16_.l_pos) ? ((invert ^ profile_.invert_ly) ? Values::invert_joy(new_value) : new_value) : INT_16::MID);
|
||||
// }
|
||||
// inline void set_joystick_rx_int10(const int32_t value, const bool invert = false)
|
||||
// {
|
||||
// int16_t new_value = Values::int10_to_int16(value);
|
||||
// pad_state_.joysticks.rx.set_value((new_value < dz_joy_int16_.r_neg || new_value > dz_joy_int16_.r_pos) ? (invert ? Values::invert_joy(new_value) : new_value) : INT_16::MID);
|
||||
// }
|
||||
// inline void set_joystick_ry_int10(const int32_t value, const bool invert = false)
|
||||
// {
|
||||
// int16_t new_value = Values::int10_to_int16(value);
|
||||
// pad_state_.joysticks.ry.set_value((new_value < dz_joy_int16_.r_neg || new_value > dz_joy_int16_.r_pos) ? ((invert ^ profile_.invert_ry) ? Values::invert_joy(new_value) : new_value) : INT_16::MID);
|
||||
// }
|
||||
|
||||
inline void set_rumble_l(uint8_t value) { rumble_state_.l.set_value(value); }
|
||||
inline void set_rumble_l(uint16_t value) { rumble_state_.l.set_value(value); }
|
||||
inline void set_rumble_r(uint8_t value) { rumble_state_.r.set_value(value); }
|
||||
inline void set_rumble_r(uint16_t value) { rumble_state_.r.set_value(value); }
|
||||
|
||||
inline void set_chatpad(const Chatpad& chatpad_array)
|
||||
{
|
||||
pad_state_.chatpad[0] = chatpad_array[0];
|
||||
pad_state_.chatpad[1] = chatpad_array[1];
|
||||
pad_state_.chatpad[2] = chatpad_array[2];
|
||||
}
|
||||
|
||||
inline void reset_buttons() { pad_state_.dpad = 0; pad_state_.buttons = 0; }
|
||||
inline void reset_pad()
|
||||
{
|
||||
pad_state_.dpad = 0;
|
||||
pad_state_.buttons = 0;
|
||||
pad_state_.triggers.l.set_value(UINT_8::MIN);
|
||||
pad_state_.triggers.r.set_value(UINT_8::MIN);
|
||||
pad_state_.joysticks.lx.set_value(UINT_8::MID);
|
||||
pad_state_.joysticks.ly.set_value(UINT_8::MID);
|
||||
pad_state_.joysticks.rx.set_value(UINT_8::MID);
|
||||
pad_state_.joysticks.ry.set_value(UINT_8::MID);
|
||||
|
||||
std::memset(&pad_state_.analog_buttons, 0, sizeof(PadState::AnalogButtons));
|
||||
std::memset(&pad_state_.chatpad, 0, sizeof(Chatpad));
|
||||
}
|
||||
inline void reset_rumble()
|
||||
{
|
||||
rumble_state_.l.set_value(UINT_8::MIN);
|
||||
rumble_state_.l.set_value(UINT_8::MIN);
|
||||
}
|
||||
|
||||
private:
|
||||
struct PadState
|
||||
{
|
||||
uint8_t dpad{0};
|
||||
uint16_t buttons{0};
|
||||
|
||||
struct Triggers
|
||||
{
|
||||
Values l;
|
||||
Values r;
|
||||
} triggers;
|
||||
|
||||
struct Joysticks
|
||||
{
|
||||
Values lx;
|
||||
Values ly;
|
||||
Values rx;
|
||||
Values ry;
|
||||
} joysticks;
|
||||
|
||||
struct AnalogButtons
|
||||
{
|
||||
uint8_t up{0};
|
||||
uint8_t down{0};
|
||||
uint8_t left{0};
|
||||
uint8_t right{0};
|
||||
uint8_t a{0};
|
||||
uint8_t b{0};
|
||||
uint8_t x{0};
|
||||
uint8_t y{0};
|
||||
uint8_t lb{0};
|
||||
uint8_t rb{0};
|
||||
} analog_buttons;
|
||||
|
||||
std::array<uint8_t, 3> chatpad = { 0, 0, 0 };
|
||||
};
|
||||
|
||||
struct Rumble
|
||||
{
|
||||
Values l;
|
||||
Values r;
|
||||
};
|
||||
|
||||
bool analog_enabled_{false};
|
||||
UserProfile profile_;
|
||||
|
||||
template<typename type>
|
||||
struct DZTrigger
|
||||
{
|
||||
type l{0};
|
||||
type r{0};
|
||||
};
|
||||
|
||||
template<typename type>
|
||||
struct DZJoystick
|
||||
{
|
||||
type l_neg;
|
||||
type l_pos;
|
||||
type r_neg;
|
||||
type r_pos;
|
||||
|
||||
DZJoystick()
|
||||
{
|
||||
if constexpr (std::is_same_v<type, uint8_t>) { l_neg = l_pos = r_neg = r_pos = UINT_8::MID; }
|
||||
else if constexpr (std::is_same_v<type, int16_t>) { l_neg = l_pos = r_neg = r_pos = 0; }
|
||||
}
|
||||
};
|
||||
|
||||
DZTrigger<uint8_t> dz_trig_uint8_;
|
||||
DZTrigger<uint16_t> dz_trig_uint16_;
|
||||
DZJoystick<int16_t> dz_joy_int16_;
|
||||
DZJoystick<uint8_t> dz_joy_uint8_;
|
||||
|
||||
PadState pad_state_;
|
||||
Rumble rumble_state_;
|
||||
|
||||
std::array<uint8_t*, sizeof(PadState::AnalogButtons)> analog_map_ =
|
||||
{
|
||||
&pad_state_.analog_buttons.up,
|
||||
&pad_state_.analog_buttons.down,
|
||||
&pad_state_.analog_buttons.left,
|
||||
&pad_state_.analog_buttons.right,
|
||||
&pad_state_.analog_buttons.a,
|
||||
&pad_state_.analog_buttons.b,
|
||||
&pad_state_.analog_buttons.x,
|
||||
&pad_state_.analog_buttons.y,
|
||||
&pad_state_.analog_buttons.lb,
|
||||
&pad_state_.analog_buttons.rb
|
||||
};
|
||||
|
||||
void setup_deadzones() //Deadzones in the profile are 0-255 (0-100%)
|
||||
{
|
||||
dz_trig_uint8_.l = profile_.dz_trigger_l;
|
||||
dz_trig_uint8_.r = profile_.dz_trigger_r;
|
||||
dz_trig_uint16_.l = Values::uint8_to_uint16(profile_.dz_trigger_l);
|
||||
dz_trig_uint16_.r = Values::uint8_to_uint16(profile_.dz_trigger_r);
|
||||
|
||||
dz_joy_uint8_.l_neg = UINT_8::MID - (profile_.dz_joystick_l / 2);
|
||||
dz_joy_uint8_.l_pos = UINT_8::MID + (profile_.dz_joystick_l / 2);
|
||||
dz_joy_uint8_.r_neg = UINT_8::MID - (profile_.dz_joystick_r / 2);
|
||||
dz_joy_uint8_.r_pos = UINT_8::MID + (profile_.dz_joystick_r / 2);
|
||||
|
||||
dz_joy_int16_.l_neg = Values::uint8_to_int16(dz_joy_uint8_.l_neg);
|
||||
dz_joy_int16_.l_pos = Values::uint8_to_int16(dz_joy_uint8_.l_pos);
|
||||
dz_joy_int16_.r_neg = Values::uint8_to_int16(dz_joy_uint8_.r_neg);
|
||||
dz_joy_int16_.r_pos = Values::uint8_to_int16(dz_joy_uint8_.r_pos);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _GAMEPAD_H_
|
||||
630
Firmware/RP2040/src/I2CDriver/i2c_driver_4ch.cpp
Normal file
630
Firmware/RP2040/src/I2CDriver/i2c_driver_4ch.cpp
Normal file
@@ -0,0 +1,630 @@
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <atomic>
|
||||
|
||||
#include <pico/i2c_slave.h>
|
||||
#include <pico/time.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/i2c.h>
|
||||
|
||||
#include "board_config.h"
|
||||
#include "I2CDriver/i2c_driver_4ch.h"
|
||||
|
||||
namespace i2c_driver_4ch {
|
||||
|
||||
enum class Mode { MASTER = 0, SLAVE };
|
||||
enum class ReportID : uint8_t { UNKNOWN = 0, PAD, STATUS, CONNECT, DISCONNECT };
|
||||
enum class SlaveStatus : uint8_t { NC = 0, NOT_READY, READY, RESP_OK };
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct ReportIn
|
||||
{
|
||||
uint8_t report_len;
|
||||
uint8_t report_id;
|
||||
uint16_t buttons;
|
||||
uint8_t dpad;
|
||||
uint8_t trigger_l;
|
||||
uint8_t trigger_r;
|
||||
int16_t joystick_lx;
|
||||
int16_t joystick_ly;
|
||||
int16_t joystick_rx;
|
||||
int16_t joystick_ry;
|
||||
std::array<uint8_t, 3> chatpad;
|
||||
|
||||
ReportIn()
|
||||
{
|
||||
std::memset(this, 0, sizeof(ReportIn));
|
||||
report_len = sizeof(ReportIn);
|
||||
report_id = static_cast<uint8_t>(ReportID::PAD);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(ReportIn) == 18, "I2CDriver::ReportIn size mismatch");
|
||||
|
||||
struct ReportOut
|
||||
{
|
||||
uint8_t report_len;
|
||||
uint8_t report_id;
|
||||
uint8_t rumble_l;
|
||||
uint8_t rumble_r;
|
||||
|
||||
ReportOut()
|
||||
{
|
||||
std::memset(this, 0, sizeof(ReportOut));
|
||||
report_len = sizeof(ReportOut);
|
||||
report_id = static_cast<uint8_t>(ReportID::PAD);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(ReportOut) == 4, "I2CDriver::ReportOut size mismatch");
|
||||
|
||||
struct ReportStatus
|
||||
{
|
||||
uint8_t report_len;
|
||||
uint8_t report_id;
|
||||
uint8_t status;
|
||||
|
||||
ReportStatus()
|
||||
{
|
||||
std::memset(this, 0, sizeof(ReportStatus));
|
||||
report_len = sizeof(ReportStatus);
|
||||
report_id = static_cast<uint8_t>(ReportID::STATUS);
|
||||
status = static_cast<uint8_t>(SlaveStatus::NC);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(ReportStatus) == 3, "I2CDriver::ReportStatus size mismatch");
|
||||
#pragma pack(pop)
|
||||
|
||||
static constexpr size_t MAX_BUFFER_SIZE = std::max(sizeof(ReportStatus), std::max(sizeof(ReportIn), sizeof(ReportOut)));
|
||||
|
||||
namespace i2c_master
|
||||
{
|
||||
struct Master
|
||||
{
|
||||
std::atomic<bool> enabled{false};
|
||||
|
||||
struct Slave
|
||||
{
|
||||
uint8_t address{0xFF};
|
||||
Gamepad* gamepad{nullptr};
|
||||
|
||||
SlaveStatus status{SlaveStatus::NC};
|
||||
std::atomic<bool> enabled{false};
|
||||
uint32_t last_update{0};
|
||||
};
|
||||
|
||||
std::array<Slave, MAX_GAMEPADS - 1> slaves;
|
||||
};
|
||||
|
||||
Master master_;
|
||||
|
||||
bool slave_detected(uint8_t address);
|
||||
bool send_in_report(Master::Slave& slave);
|
||||
bool get_out_report(Master::Slave& slave);
|
||||
bool update_slave_enabled(uint8_t address, bool connected);
|
||||
void notify_tud_deinit();
|
||||
void update_slave_status(Master::Slave& slave);
|
||||
|
||||
void notify_tuh_mounted(HostDriver::Type host_type);
|
||||
void notify_tuh_unmounted(HostDriver::Type host_type);
|
||||
void notify_xbox360w_connected(uint8_t idx);
|
||||
void notify_xbox360w_disconnected(uint8_t idx);
|
||||
bool is_active();
|
||||
void process();
|
||||
void init(std::array<Gamepad, MAX_GAMEPADS>& gamepads);
|
||||
}
|
||||
|
||||
namespace i2c_slave
|
||||
{
|
||||
struct Slave
|
||||
{
|
||||
uint8_t address{0xFF};
|
||||
Gamepad* gamepad{nullptr};
|
||||
std::atomic<bool> i2c_enabled{false};
|
||||
std::atomic<bool> tuh_enabled{false};
|
||||
};
|
||||
|
||||
Slave slave_;
|
||||
|
||||
void process_in_report(ReportIn* report_in);
|
||||
void fill_out_report(ReportOut* report_out);
|
||||
ReportID get_report_id(uint8_t* buffer_in);
|
||||
void slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event);
|
||||
|
||||
bool is_active();
|
||||
void notify_tuh_mounted();
|
||||
void notify_tuh_unmounted();
|
||||
void init(uint8_t address, Gamepad& gamepad);
|
||||
}
|
||||
|
||||
namespace i2c_master {
|
||||
|
||||
bool is_active()
|
||||
{
|
||||
return master_.enabled.load();
|
||||
}
|
||||
|
||||
bool slave_detected(uint8_t address)
|
||||
{
|
||||
uint8_t dummy_data = 0;
|
||||
int result = i2c_write_timeout_us(I2C_PORT, address, &dummy_data, 0, false, 1000);
|
||||
return (result >= 0);
|
||||
}
|
||||
|
||||
void notify_tuh_mounted(HostDriver::Type host_type)
|
||||
{
|
||||
if (host_type == HostDriver::Type::XBOX360W)
|
||||
{
|
||||
master_.enabled.store(true);
|
||||
}
|
||||
}
|
||||
|
||||
void notify_tuh_unmounted(HostDriver::Type host_type)
|
||||
{
|
||||
master_.enabled.store(false);
|
||||
}
|
||||
|
||||
void notify_xbox360w_connected(uint8_t idx)
|
||||
{
|
||||
if (idx < 1 || idx >= MAX_GAMEPADS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
master_.slaves[idx - 1].enabled.store(true);
|
||||
}
|
||||
|
||||
void notify_xbox360w_disconnected(uint8_t idx)
|
||||
{
|
||||
if (idx < 1 || idx >= MAX_GAMEPADS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
master_.slaves[idx - 1].enabled.store(false);
|
||||
// bool any_connected = false;
|
||||
// for (auto& slave : master_.slaves)
|
||||
// {
|
||||
// if (slave.enabled.load())
|
||||
// {
|
||||
// any_connected = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (!any_connected)
|
||||
// {
|
||||
// master_.enabled.store(false);
|
||||
// }
|
||||
}
|
||||
|
||||
bool send_in_report(Master::Slave& slave)
|
||||
{
|
||||
static ReportIn report_in = ReportIn();
|
||||
|
||||
report_in.buttons = slave.gamepad->get_buttons();
|
||||
report_in.dpad = slave.gamepad->get_dpad_buttons();
|
||||
report_in.trigger_l = slave.gamepad->get_trigger_l().uint8();
|
||||
report_in.trigger_r = slave.gamepad->get_trigger_r().uint8();
|
||||
report_in.joystick_lx = slave.gamepad->get_joystick_lx().int16();
|
||||
report_in.joystick_ly = slave.gamepad->get_joystick_ly().int16();
|
||||
report_in.joystick_rx = slave.gamepad->get_joystick_rx().int16();
|
||||
report_in.joystick_ry = slave.gamepad->get_joystick_ry().int16();
|
||||
report_in.chatpad = slave.gamepad->get_chatpad();
|
||||
|
||||
int count = i2c_write_blocking(I2C_PORT, slave.address, reinterpret_cast<uint8_t*>(&report_in), sizeof(report_in), false);
|
||||
return (count == sizeof(ReportIn));
|
||||
}
|
||||
|
||||
bool get_out_report(Master::Slave& slave)
|
||||
{
|
||||
static ReportOut report_out = ReportOut();
|
||||
|
||||
int count = i2c_read_blocking(I2C_PORT, slave.address, reinterpret_cast<uint8_t*>(&report_out), sizeof(ReportOut), false);
|
||||
if (count != sizeof(ReportOut))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
slave.gamepad->set_rumble_l(report_out.rumble_l);
|
||||
slave.gamepad->set_rumble_r(report_out.rumble_r);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool update_slave_enabled(uint8_t address, bool connected)
|
||||
{
|
||||
ReportStatus report;
|
||||
report.report_id = connected ? static_cast<uint8_t>(ReportID::CONNECT) : static_cast<uint8_t>(ReportID::DISCONNECT);
|
||||
int count = i2c_write_blocking(I2C_PORT, address, reinterpret_cast<uint8_t*>(&report), sizeof(ReportStatus), false);
|
||||
if (count == sizeof(ReportStatus))
|
||||
{
|
||||
count = i2c_read_blocking(I2C_PORT, address, reinterpret_cast<uint8_t*>(&report), sizeof(ReportStatus), false);
|
||||
return (static_cast<SlaveStatus>(report.status) == SlaveStatus::RESP_OK);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void notify_tud_deinit()
|
||||
{
|
||||
for (auto& slave : master_.slaves)
|
||||
{
|
||||
if (slave.status != SlaveStatus::NC)
|
||||
{
|
||||
int retries = 0;
|
||||
while (!update_slave_enabled(slave.address, false) && retries < 6)
|
||||
{
|
||||
sleep_ms(1);
|
||||
++retries;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_slave_status(Master::Slave& slave)
|
||||
{
|
||||
bool slave_enabled = slave.enabled.load();
|
||||
|
||||
if (!slave_detected(slave.address))
|
||||
{
|
||||
slave.status = SlaveStatus::NC;
|
||||
return;
|
||||
}
|
||||
if (!update_slave_enabled(slave.address, slave_enabled))
|
||||
{
|
||||
slave.status = SlaveStatus::NOT_READY;
|
||||
return;
|
||||
}
|
||||
if (slave_enabled)
|
||||
{
|
||||
ReportStatus report = ReportStatus();
|
||||
int count = 0;
|
||||
|
||||
report.report_id = static_cast<uint8_t>(ReportID::STATUS);
|
||||
count = i2c_write_blocking(I2C_PORT, slave.address, reinterpret_cast<uint8_t*>(&report), sizeof(ReportStatus), false);
|
||||
if (count == sizeof(ReportStatus))
|
||||
{
|
||||
count = i2c_read_blocking(I2C_PORT, slave.address, reinterpret_cast<uint8_t*>(&report), sizeof(ReportStatus), false);
|
||||
slave.status = (count == sizeof(ReportStatus)) ? static_cast<SlaveStatus>(report.status) : SlaveStatus::NC;
|
||||
}
|
||||
else
|
||||
{
|
||||
slave.status = SlaveStatus::NOT_READY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
slave.status = SlaveStatus::NOT_READY;
|
||||
}
|
||||
}
|
||||
|
||||
void process()
|
||||
{
|
||||
for (auto& slave : master_.slaves)
|
||||
{
|
||||
if ((time_us_32() / 1000) - slave.last_update > 1000)
|
||||
{
|
||||
update_slave_status(slave);
|
||||
slave.last_update = time_us_32() / 1000;
|
||||
}
|
||||
sleep_us(100);
|
||||
}
|
||||
if (!master_.enabled.load())
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (auto& slave : master_.slaves)
|
||||
{
|
||||
if (slave.status == SlaveStatus::READY)
|
||||
{
|
||||
if (send_in_report(slave))
|
||||
{
|
||||
get_out_report(slave);
|
||||
}
|
||||
sleep_us(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init(std::array<Gamepad, MAX_GAMEPADS>& gamepads)
|
||||
{
|
||||
for (uint8_t i = 0; i < master_.slaves.size(); ++i)
|
||||
{
|
||||
master_.slaves[i].address = i + 1;
|
||||
master_.slaves[i].gamepad = &gamepads[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace i2c_master
|
||||
|
||||
namespace i2c_slave {
|
||||
|
||||
bool is_active()
|
||||
{
|
||||
return slave_.i2c_enabled.load();
|
||||
}
|
||||
|
||||
void notify_tuh_mounted()
|
||||
{
|
||||
slave_.tuh_enabled.store(true);
|
||||
}
|
||||
|
||||
void notify_tuh_unmounted()
|
||||
{
|
||||
slave_.tuh_enabled.store(false);
|
||||
}
|
||||
|
||||
void process_in_report(ReportIn* report_in)
|
||||
{
|
||||
Gamepad& gamepad = *slave_.gamepad;
|
||||
|
||||
gamepad.set_buttons(report_in->buttons);
|
||||
gamepad.set_dpad(report_in->dpad);
|
||||
gamepad.set_trigger_l(report_in->trigger_l);
|
||||
gamepad.set_trigger_r(report_in->trigger_r);
|
||||
gamepad.set_joystick_lx(report_in->joystick_lx);
|
||||
gamepad.set_joystick_ly(report_in->joystick_ly);
|
||||
gamepad.set_joystick_rx(report_in->joystick_rx);
|
||||
gamepad.set_joystick_ry(report_in->joystick_ry);
|
||||
gamepad.set_chatpad(report_in->chatpad);
|
||||
}
|
||||
|
||||
void fill_out_report(ReportOut* report_out)
|
||||
{
|
||||
Gamepad& gamepad = *slave_.gamepad;
|
||||
|
||||
report_out->report_len = sizeof(ReportOut);
|
||||
report_out->report_id = static_cast<uint8_t>(ReportID::PAD);
|
||||
report_out->rumble_l = gamepad.get_rumble_l().uint8();
|
||||
report_out->rumble_r = gamepad.get_rumble_r().uint8();
|
||||
}
|
||||
|
||||
ReportID get_report_id(uint8_t* buffer_in)
|
||||
{
|
||||
switch (static_cast<ReportID>(buffer_in[1]))
|
||||
{
|
||||
case ReportID::PAD:
|
||||
if (buffer_in[0] == sizeof(ReportIn))
|
||||
{
|
||||
return ReportID::PAD;
|
||||
}
|
||||
break;
|
||||
case ReportID::DISCONNECT:
|
||||
if (buffer_in[0] == sizeof(ReportStatus))
|
||||
{
|
||||
return ReportID::DISCONNECT;
|
||||
}
|
||||
break;
|
||||
case ReportID::CONNECT:
|
||||
if (buffer_in[0] == sizeof(ReportStatus))
|
||||
{
|
||||
return ReportID::CONNECT;
|
||||
}
|
||||
break;
|
||||
case ReportID::STATUS:
|
||||
if (buffer_in[0] == sizeof(ReportStatus))
|
||||
{
|
||||
return ReportID::STATUS;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ReportID::UNKNOWN;
|
||||
}
|
||||
|
||||
void slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event)
|
||||
{
|
||||
static int count = 0;
|
||||
static std::array<uint8_t, MAX_BUFFER_SIZE> buffer_in{0};
|
||||
static std::array<uint8_t, MAX_BUFFER_SIZE> buffer_out{0};
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case I2C_SLAVE_RECEIVE: // master has written
|
||||
if (count < MAX_BUFFER_SIZE)
|
||||
{
|
||||
buffer_in.data()[count] = i2c_read_byte_raw(i2c);
|
||||
++count;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_FINISH: // master signalled Stop / Restart
|
||||
// Each master write has an ID indicating the type of data to send back on the next request
|
||||
// Every write has an associated read
|
||||
switch (get_report_id(buffer_in.data()))
|
||||
{
|
||||
case ReportID::PAD:
|
||||
process_in_report(reinterpret_cast<ReportIn*>(buffer_in.data()));
|
||||
fill_out_report(reinterpret_cast<ReportOut*>(buffer_out.data()));
|
||||
break;
|
||||
case ReportID::STATUS:
|
||||
buffer_out.data()[0] = sizeof(ReportStatus);
|
||||
buffer_out.data()[1] = static_cast<uint8_t>(ReportID::STATUS);
|
||||
// if something is mounted by tuh, signal to not send gamepad data
|
||||
buffer_out.data()[2] = slave_.tuh_enabled.load() ? static_cast<uint8_t>(SlaveStatus::NOT_READY) : static_cast<uint8_t>(SlaveStatus::READY);
|
||||
break;
|
||||
case ReportID::CONNECT:
|
||||
slave_.i2c_enabled.store(true);
|
||||
buffer_out.data()[0] = sizeof(ReportStatus);
|
||||
buffer_out.data()[1] = static_cast<uint8_t>(ReportID::STATUS);
|
||||
buffer_out.data()[2] = static_cast<uint8_t>(SlaveStatus::RESP_OK);
|
||||
break;
|
||||
case ReportID::DISCONNECT:
|
||||
slave_.i2c_enabled.store(false);
|
||||
buffer_out.data()[0] = sizeof(ReportStatus);
|
||||
buffer_out.data()[1] = static_cast<uint8_t>(ReportID::STATUS);
|
||||
buffer_out.data()[2] = static_cast<uint8_t>(SlaveStatus::RESP_OK);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
count = 0;
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_REQUEST: // master requesting data
|
||||
i2c_write_raw_blocking(i2c, buffer_out.data(), buffer_out.data()[0]);
|
||||
buffer_in.fill(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void init(uint8_t address, Gamepad& gamepad)
|
||||
{
|
||||
slave_.address = address;
|
||||
slave_.gamepad = &gamepad;
|
||||
i2c_slave_init(I2C_PORT, slave_.address, &slave_handler);
|
||||
}
|
||||
|
||||
} // namespace i2c_slave
|
||||
|
||||
Mode mode_{Mode::MASTER};
|
||||
|
||||
uint8_t get_address()
|
||||
{
|
||||
gpio_init(SLAVE_ADDR_PIN_1);
|
||||
gpio_init(SLAVE_ADDR_PIN_2);
|
||||
gpio_pull_up(SLAVE_ADDR_PIN_1);
|
||||
gpio_pull_up(SLAVE_ADDR_PIN_2);
|
||||
|
||||
if (gpio_get(SLAVE_ADDR_PIN_1) == 1 && gpio_get(SLAVE_ADDR_PIN_2) == 1)
|
||||
{
|
||||
return 0x00;
|
||||
}
|
||||
else if (gpio_get(SLAVE_ADDR_PIN_1) == 1 && gpio_get(SLAVE_ADDR_PIN_2) == 0)
|
||||
{
|
||||
return 0x01;
|
||||
}
|
||||
else if (gpio_get(SLAVE_ADDR_PIN_1) == 0 && gpio_get(SLAVE_ADDR_PIN_2) == 1)
|
||||
{
|
||||
return 0x02;
|
||||
}
|
||||
else if (gpio_get(SLAVE_ADDR_PIN_1) == 0 && gpio_get(SLAVE_ADDR_PIN_2) == 0)
|
||||
{
|
||||
return 0x03;
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
//Public methods
|
||||
|
||||
//Call before core1 starts
|
||||
void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepads)
|
||||
{
|
||||
uint8_t address = get_address();
|
||||
if (address == 0xFF)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
i2c_init(I2C_PORT, I2C_BAUDRATE);
|
||||
|
||||
gpio_init(I2C_SDA_PIN);
|
||||
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(I2C_SDA_PIN);
|
||||
|
||||
gpio_init(I2C_SCL_PIN);
|
||||
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(I2C_SCL_PIN);
|
||||
|
||||
if (address < 1)
|
||||
{
|
||||
mode_ = Mode::MASTER;
|
||||
i2c_master::init(gamepads);
|
||||
}
|
||||
else
|
||||
{
|
||||
mode_ = Mode::SLAVE;
|
||||
i2c_slave::init(address, gamepads.front());
|
||||
}
|
||||
}
|
||||
|
||||
void process()
|
||||
{
|
||||
switch (mode_)
|
||||
{
|
||||
case Mode::MASTER:
|
||||
i2c_master::process();
|
||||
break;
|
||||
case Mode::SLAVE:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_active()
|
||||
{
|
||||
switch (mode_)
|
||||
{
|
||||
case Mode::MASTER:
|
||||
return i2c_master::is_active();
|
||||
case Mode::SLAVE:
|
||||
return i2c_slave::is_active();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//Called from core1
|
||||
void notify_tuh_mounted(HostDriver::Type host_type)
|
||||
{
|
||||
switch (mode_)
|
||||
{
|
||||
case Mode::MASTER:
|
||||
i2c_master::notify_tuh_mounted(host_type);
|
||||
break;
|
||||
case Mode::SLAVE:
|
||||
i2c_slave::notify_tuh_mounted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
//Called from core1
|
||||
void notify_tuh_unmounted(HostDriver::Type host_type)
|
||||
{
|
||||
switch (mode_)
|
||||
{
|
||||
case Mode::MASTER:
|
||||
i2c_master::notify_tuh_unmounted(host_type);
|
||||
break;
|
||||
case Mode::SLAVE:
|
||||
i2c_slave::notify_tuh_unmounted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
//Called from core1
|
||||
void notify_xbox360w_connected(uint8_t idx)
|
||||
{
|
||||
switch (mode_)
|
||||
{
|
||||
case Mode::MASTER:
|
||||
i2c_master::notify_xbox360w_connected(idx);
|
||||
break;
|
||||
case Mode::SLAVE:
|
||||
return;
|
||||
}
|
||||
}
|
||||
//Called from core1
|
||||
void notify_xbox360w_disconnected(uint8_t idx)
|
||||
{
|
||||
switch (mode_)
|
||||
{
|
||||
case Mode::MASTER:
|
||||
i2c_master::notify_xbox360w_disconnected(idx);
|
||||
break;
|
||||
case Mode::SLAVE:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void notify_tud_deinit()
|
||||
{
|
||||
switch (mode_)
|
||||
{
|
||||
case Mode::MASTER:
|
||||
i2c_master::notify_tud_deinit();
|
||||
break;
|
||||
case Mode::SLAVE:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace I2CDriver
|
||||
28
Firmware/RP2040/src/I2CDriver/i2c_driver_4ch.h
Normal file
28
Firmware/RP2040/src/I2CDriver/i2c_driver_4ch.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef _I2C_DRIVER_H_
|
||||
#define _I2C_DRIVER_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Gamepad.h"
|
||||
#include "USBHost/HostManager.h"
|
||||
|
||||
// /* Master will only write/read slave if an Xbox360 wireless controller is connected for its corresponding slave.
|
||||
// Slaves will read/write from the bus only if they don't have a device mounted by tusb host.
|
||||
// Could be made to work so that a USB hub could be connected to port 1 and host 4 controllers, maybe later.
|
||||
// This is run on core0 with the device stack, but some values are checked and modified by core1 (atomic types). */
|
||||
namespace i2c_driver_4ch
|
||||
{
|
||||
void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepads);
|
||||
void process();
|
||||
bool is_active();
|
||||
|
||||
void notify_tud_deinit();
|
||||
|
||||
void notify_tuh_mounted(HostDriver::Type host_type = HostDriver::Type::UNKNOWN);
|
||||
void notify_tuh_unmounted(HostDriver::Type host_type = HostDriver::Type::UNKNOWN);
|
||||
|
||||
void notify_xbox360w_connected(uint8_t idx);
|
||||
void notify_xbox360w_disconnected(uint8_t idx);
|
||||
}
|
||||
|
||||
#endif // _I2C_DRIVER_H_
|
||||
281
Firmware/RP2040/src/I2CDriver/i2c_driver_esp32.cpp
Normal file
281
Firmware/RP2040/src/I2CDriver/i2c_driver_esp32.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <pico/i2c_slave.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/i2c.h>
|
||||
|
||||
#include "Gamepad.h"
|
||||
#include "board_config.h"
|
||||
#include "Board/board_api.h"
|
||||
#include "I2CDriver/i2c_driver_esp32.h"
|
||||
|
||||
namespace i2c_driver_esp32 {
|
||||
|
||||
//May expand commands in the future
|
||||
enum class ReportID : uint8_t { UNKNOWN = 0, SET_PAD, GET_PAD };
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct ReportIn
|
||||
{
|
||||
uint8_t report_len;
|
||||
uint8_t report_id;
|
||||
uint8_t index;
|
||||
uint8_t dpad;
|
||||
uint16_t buttons;
|
||||
uint8_t trigger_l;
|
||||
uint8_t trigger_r;
|
||||
int16_t joystick_lx;
|
||||
int16_t joystick_ly;
|
||||
int16_t joystick_rx;
|
||||
int16_t joystick_ry;
|
||||
|
||||
ReportIn()
|
||||
{
|
||||
std::memset(this, 0, sizeof(ReportIn));
|
||||
report_len = sizeof(ReportIn);
|
||||
report_id = static_cast<uint8_t>(ReportID::SET_PAD);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(ReportIn) == 16, "i2c_driver_esp::ReportIn size mismatch");
|
||||
|
||||
struct ReportOut
|
||||
{
|
||||
uint8_t report_len;
|
||||
uint8_t report_id;
|
||||
uint8_t index;
|
||||
uint8_t rumble_l;
|
||||
uint8_t rumble_r;
|
||||
|
||||
ReportOut()
|
||||
{
|
||||
std::memset(this, 0, sizeof(ReportOut));
|
||||
report_len = sizeof(ReportOut);
|
||||
report_id = static_cast<uint8_t>(ReportID::GET_PAD);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(ReportOut) == 5, "i2c_driver_esp::ReportOut size mismatch");
|
||||
#pragma pack(pop)
|
||||
|
||||
static constexpr size_t MAX_BUFFER_SIZE = std::max(sizeof(ReportOut), sizeof(ReportIn));
|
||||
static constexpr uint8_t I2C_ADDR = 0x01;
|
||||
|
||||
std::array<Gamepad*, MAX_GAMEPADS> gamepads_{nullptr};
|
||||
std::atomic<bool> pad_connected_ = false;
|
||||
|
||||
inline void process_in_report(ReportIn* report_in)
|
||||
{
|
||||
if (report_in->index >= MAX_GAMEPADS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Gamepad* gamepad = gamepads_[report_in->index];
|
||||
|
||||
gamepad->set_buttons(report_in->buttons);
|
||||
gamepad->set_dpad(report_in->dpad);
|
||||
gamepad->set_trigger_l(report_in->trigger_l);
|
||||
gamepad->set_trigger_r(report_in->trigger_r);
|
||||
gamepad->set_joystick_lx_int10(static_cast<int32_t>(report_in->joystick_lx));
|
||||
gamepad->set_joystick_ly_int10(static_cast<int32_t>(report_in->joystick_ly));
|
||||
gamepad->set_joystick_rx_int10(static_cast<int32_t>(report_in->joystick_rx));
|
||||
gamepad->set_joystick_ry_int10(static_cast<int32_t>(report_in->joystick_ry));
|
||||
}
|
||||
|
||||
inline void fill_out_report(uint8_t index, ReportOut* report_out)
|
||||
{
|
||||
if (index >= MAX_GAMEPADS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Gamepad* gamepad = gamepads_[index];
|
||||
|
||||
report_out->report_len = sizeof(ReportOut);
|
||||
report_out->report_id = static_cast<uint8_t>(ReportID::GET_PAD);
|
||||
report_out->rumble_l = gamepad->get_rumble_l().uint8();
|
||||
report_out->rumble_r = gamepad->get_rumble_r().uint8();
|
||||
}
|
||||
|
||||
ReportID get_report_id(uint8_t* buffer_in)
|
||||
{
|
||||
switch (static_cast<ReportID>(buffer_in[1]))
|
||||
{
|
||||
case ReportID::SET_PAD:
|
||||
if (buffer_in[0] == sizeof(ReportIn))
|
||||
{
|
||||
return ReportID::SET_PAD;
|
||||
}
|
||||
break;
|
||||
//Unused ATM
|
||||
// case ReportID::GET_PAD:
|
||||
// if (buffer_in[0] == sizeof(ReportOut))
|
||||
// {
|
||||
// return ReportID::GET_PAD;
|
||||
// }
|
||||
// break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ReportID::UNKNOWN;
|
||||
}
|
||||
|
||||
static inline void slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event)
|
||||
{
|
||||
static int count = 0;
|
||||
static std::array<uint8_t, MAX_BUFFER_SIZE> buffer_in{0};
|
||||
static std::array<uint8_t, MAX_BUFFER_SIZE> buffer_out{0};
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case I2C_SLAVE_RECEIVE: // master has written
|
||||
if (count < MAX_BUFFER_SIZE)
|
||||
{
|
||||
buffer_in.data()[count] = i2c_read_byte_raw(i2c);
|
||||
++count;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_FINISH: // master signalled Stop / Restart
|
||||
if (get_report_id(buffer_in.data()) == ReportID::SET_PAD)
|
||||
{
|
||||
process_in_report(reinterpret_cast<ReportIn*>(buffer_in.data()));
|
||||
}
|
||||
count = 0;
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_REQUEST: // master requesting data
|
||||
fill_out_report(reinterpret_cast<ReportIn*>(buffer_in.data())->index, reinterpret_cast<ReportOut*>(buffer_out.data()));
|
||||
i2c_write_raw_blocking(i2c, buffer_out.data(), buffer_out.data()[0]);
|
||||
buffer_in.fill(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepad)
|
||||
{
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
|
||||
{
|
||||
gamepads_[i] = &gamepad[i];
|
||||
}
|
||||
|
||||
i2c_init(I2C_PORT, I2C_BAUDRATE);
|
||||
|
||||
gpio_init(I2C_SDA_PIN);
|
||||
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(I2C_SDA_PIN);
|
||||
|
||||
gpio_init(I2C_SCL_PIN);
|
||||
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(I2C_SCL_PIN);
|
||||
|
||||
i2c_slave_init(I2C_PORT, I2C_ADDR, &slave_handler);
|
||||
}
|
||||
|
||||
bool pad_connected()
|
||||
{
|
||||
return pad_connected_.load();
|
||||
}
|
||||
|
||||
} // namespace i2c_driver_esp
|
||||
|
||||
// #include <array>
|
||||
// #include <cstring>
|
||||
// #include <pico/i2c_slave.h>
|
||||
// #include <hardware/gpio.h>
|
||||
// #include <hardware/i2c.h>
|
||||
|
||||
// #include "Gamepad.h"
|
||||
// #include "board_config.h"
|
||||
// #include "Board/board_api.h"
|
||||
// #include "I2CDriver/I2CDriver_ESP.h"
|
||||
|
||||
// namespace i2c_driver_esp {
|
||||
|
||||
// #pragma pack(push, 1)
|
||||
// struct ReportIn
|
||||
// {
|
||||
// uint8_t dpad;
|
||||
// uint16_t buttons;
|
||||
// uint8_t trigger_l;
|
||||
// uint8_t trigger_r;
|
||||
// int16_t joystick_lx;
|
||||
// int16_t joystick_ly;
|
||||
// int16_t joystick_rx;
|
||||
// int16_t joystick_ry;
|
||||
|
||||
// ReportIn()
|
||||
// {
|
||||
// std::memset(this, 0, sizeof(ReportIn));
|
||||
// }
|
||||
// };
|
||||
// static_assert(sizeof(ReportIn) == 13, "i2c_driver_esp::ReportIn size mismatch");
|
||||
|
||||
// struct ReportOut
|
||||
// {
|
||||
// uint8_t rumble_l;
|
||||
// uint8_t rumble_r;
|
||||
|
||||
// ReportOut()
|
||||
// {
|
||||
// std::memset(this, 0, sizeof(ReportOut));
|
||||
// }
|
||||
// };
|
||||
// static_assert(sizeof(ReportOut) == 2, "i2c_driver_esp::ReportOut size mismatch");
|
||||
// #pragma pack(pop)
|
||||
|
||||
// static constexpr uint8_t I2C_ADDR = 0x50;
|
||||
|
||||
// Gamepad* gamepad_ = nullptr;
|
||||
|
||||
// inline void get_in_report()
|
||||
// {
|
||||
// static ReportIn report_in = ReportIn();
|
||||
// int count = i2c_read_timeout_us(I2C_PORT, I2C_ADDR, reinterpret_cast<uint8_t*>(&report_in), sizeof(ReportIn), false, 1000);
|
||||
// if (count > 0)
|
||||
// {
|
||||
// gamepad_->set_buttons(report_in.buttons);
|
||||
// gamepad_->set_dpad(report_in.dpad);
|
||||
// gamepad_->set_trigger_l(report_in.trigger_l);
|
||||
// gamepad_->set_trigger_r(report_in.trigger_r);
|
||||
// gamepad_->set_joystick_lx_int10(report_in.joystick_lx);
|
||||
// gamepad_->set_joystick_ly_int10(report_in.joystick_ly);
|
||||
// gamepad_->set_joystick_rx_int10(report_in.joystick_rx);
|
||||
// gamepad_->set_joystick_ry_int10(report_in.joystick_ry);
|
||||
// }
|
||||
// }
|
||||
|
||||
// inline void set_out_report()
|
||||
// {
|
||||
// static ReportOut report_out = ReportOut();
|
||||
// report_out.rumble_l = gamepad_->get_rumble_l().uint8();
|
||||
// report_out.rumble_r = gamepad_->get_rumble_r().uint8();
|
||||
// i2c_write_timeout_us(I2C_PORT, I2C_ADDR, reinterpret_cast<uint8_t*>(&report_out), sizeof(ReportOut), false, 1000);
|
||||
// }
|
||||
|
||||
// void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepad)
|
||||
// {
|
||||
// gamepad_ = &gamepad[0];
|
||||
|
||||
// i2c_init(I2C_PORT, I2C_BAUDRATE);
|
||||
|
||||
// gpio_init(I2C_SDA_PIN);
|
||||
// gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
// gpio_pull_up(I2C_SDA_PIN);
|
||||
|
||||
// gpio_init(I2C_SCL_PIN);
|
||||
// gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
// gpio_pull_up(I2C_SCL_PIN);
|
||||
// }
|
||||
|
||||
// void task()
|
||||
// {
|
||||
// get_in_report();
|
||||
// sleep_us(100);
|
||||
// // set_out_report();
|
||||
// // sleep_us(100);
|
||||
// }
|
||||
|
||||
// } // namespace i2c_driver_esp
|
||||
14
Firmware/RP2040/src/I2CDriver/i2c_driver_esp32.h
Normal file
14
Firmware/RP2040/src/I2CDriver/i2c_driver_esp32.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef _I2CDRIVER_ESP_H_
|
||||
#define _I2CDRIVER_ESP_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
namespace i2c_driver_esp32
|
||||
{
|
||||
void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepad);
|
||||
bool pad_connected();
|
||||
}
|
||||
|
||||
#endif // _I2CDRIVER_ESP_H_
|
||||
27
Firmware/RP2040/src/OGXMini/OGXMini.h
Normal file
27
Firmware/RP2040/src/OGXMini/OGXMini.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef _OGX_MINI_H_
|
||||
#define _OGX_MINI_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "UserSettings/UserSettings.h"
|
||||
|
||||
namespace OGXMini
|
||||
{
|
||||
enum class TUDStatus { INIT, DEINIT, NOCHANGE };
|
||||
|
||||
struct GPCheckContext
|
||||
{
|
||||
bool driver_changed;
|
||||
UserSettings& user_settings;
|
||||
};
|
||||
|
||||
static constexpr int32_t FEEDBACK_DELAY_MS = 100;
|
||||
static constexpr int32_t TUD_CHECK_DELAY_MS = 500;
|
||||
|
||||
void run_program();
|
||||
|
||||
// Callback to notify main loop of tuh mounted
|
||||
void update_tuh_status(bool mounted) __attribute__((weak));
|
||||
}
|
||||
|
||||
#endif // _OGX_MINI_H_
|
||||
139
Firmware/RP2040/src/OGXMini/OGXMini_4Ch.cpp
Normal file
139
Firmware/RP2040/src/OGXMini/OGXMini_4Ch.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "board_config.h"
|
||||
#if OGXM_BOARD == INTERNAL_4CH || OGXM_BOARD == EXTERNAL_4CH
|
||||
|
||||
#include <array>
|
||||
#include <pico/multicore.h>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "bsp/board_api.h"
|
||||
#include "pio_usb.h"
|
||||
|
||||
#include "USBDevice/DeviceManager.h"
|
||||
#include "Board/board_api.h"
|
||||
#include "OGXMini/OGXMini.h"
|
||||
#include "I2CDriver/i2c_driver_4ch.h"
|
||||
#include "Gamepad.h"
|
||||
|
||||
namespace OGXMini {
|
||||
|
||||
std::array<Gamepad, MAX_GAMEPADS> gamepads_;
|
||||
|
||||
bool feedback_cb(repeating_timer_t* rt)
|
||||
{
|
||||
static_cast<HostManager*>(rt->user_data)->send_feedback();
|
||||
return true;
|
||||
}
|
||||
|
||||
void core1_task()
|
||||
{
|
||||
HostManager& host_manager = HostManager::get_instance();
|
||||
host_manager.initialize(gamepads_);
|
||||
|
||||
pio_usb_configuration_t pio_cfg = PIO_USB_CONFIG;
|
||||
tuh_configure(BOARD_TUH_RHPORT, TUH_CFGID_RPI_PIO_USB_CONFIGURATION, &pio_cfg);
|
||||
|
||||
tuh_init(BOARD_TUH_RHPORT);
|
||||
|
||||
repeating_timer_t feedback_timer;
|
||||
add_repeating_timer_ms(FEEDBACK_DELAY_MS, feedback_cb, &host_manager, &feedback_timer);
|
||||
|
||||
while (true)
|
||||
{
|
||||
tuh_task();
|
||||
sleep_us(100);
|
||||
}
|
||||
}
|
||||
|
||||
bool gp_check_cb(repeating_timer_t* rt)
|
||||
{
|
||||
GPCheckContext* gp_check_ctx = static_cast<GPCheckContext*>(rt->user_data);
|
||||
gp_check_ctx->driver_changed = gp_check_ctx->user_settings.check_for_driver_change(gamepads_.front());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_status_cb(repeating_timer_t* rt)
|
||||
{
|
||||
TUDStatusContext* tud_status_ctx = static_cast<TUDStatusContext*>(rt->user_data);
|
||||
bool host_mounted = HostManager::get_instance().mounted() || i2c_driver::is_active();
|
||||
bool device_ininted = tud_inited();
|
||||
board_api::set_led(host_mounted);
|
||||
|
||||
if (host_mounted && !device_ininted)
|
||||
{
|
||||
tud_status_ctx->status = TUDStatus::INIT;
|
||||
}
|
||||
else if (!host_mounted && device_ininted)
|
||||
{
|
||||
tud_status_ctx->status = TUDStatus::DEINIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
tud_status_ctx->status = TUDStatus::NOCHANGE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void run_program()
|
||||
{
|
||||
UserSettings user_settings;
|
||||
user_settings.initialize_flash();
|
||||
|
||||
board_init();
|
||||
board_api::init_gpio();
|
||||
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
|
||||
{
|
||||
gamepads_[i].set_profile(user_settings.get_profile_by_index(i));
|
||||
}
|
||||
|
||||
DeviceManager& device_manager = DeviceManager::get_instance();
|
||||
device_manager.initialize_driver(user_settings.get_current_driver());
|
||||
|
||||
i2c_driver::initialize(gamepads_);
|
||||
|
||||
multicore_reset_core1();
|
||||
multicore_launch_core1(core1_task);
|
||||
|
||||
GPCheckContext gp_check_ctx = { false, user_settings };
|
||||
repeating_timer_t gp_check_timer;
|
||||
add_repeating_timer_ms(UserSettings::GP_CHECK_DELAY_MS, gp_check_cb, &gp_check_ctx, &gp_check_timer);
|
||||
|
||||
TUDStatusContext tud_status_ctx = { TUDStatus::NOCHANGE, &HostManager::get_instance() };
|
||||
repeating_timer_t tud_status_timer;
|
||||
add_repeating_timer_ms(500, tud_status_cb, &tud_status_ctx, &tud_status_timer);
|
||||
|
||||
DeviceDriver* device_driver = device_manager.get_driver();
|
||||
|
||||
while (true)
|
||||
{
|
||||
switch (tud_status_ctx.status)
|
||||
{
|
||||
case TUDStatus::NOCHANGE:
|
||||
break;
|
||||
case TUDStatus::INIT:
|
||||
tud_init(BOARD_TUD_RHPORT);
|
||||
break;
|
||||
case TUDStatus::DEINIT:
|
||||
tud_disconnect();
|
||||
sleep_ms(300);
|
||||
board_api::reboot();
|
||||
break;
|
||||
}
|
||||
|
||||
device_driver->process(0, gamepads_[0]);
|
||||
tud_task();
|
||||
sleep_us(100);
|
||||
i2c_driver::process();
|
||||
|
||||
if (gp_check_ctx.driver_changed)
|
||||
{
|
||||
cancel_repeating_timer(&gp_check_timer);
|
||||
user_settings.store_driver_type_safe(user_settings.get_current_driver());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OGXMini
|
||||
|
||||
#endif // OGXM_BOARD == INTERNAL_4CH || OGXM_BOARD == EXTERNAL_4CH
|
||||
172
Firmware/RP2040/src/OGXMini/OGXMini_ESP32.cpp
Normal file
172
Firmware/RP2040/src/OGXMini/OGXMini_ESP32.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#include "board_config.h"
|
||||
#if OGXM_BOARD == W_ESP32
|
||||
|
||||
#include <array>
|
||||
#include <pico/stdlib.h>
|
||||
#include <pico/multicore.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/sync.h>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "bsp/board_api.h"
|
||||
|
||||
#include "USBDevice/DeviceManager.h"
|
||||
#include "Board/board_api.h"
|
||||
#include "OGXMini/OGXMini.h"
|
||||
#include "I2CDriver/I2CDriver_ESP.h"
|
||||
#include "Gamepad.h"
|
||||
|
||||
namespace OGXMini {
|
||||
|
||||
std::array<Gamepad, MAX_GAMEPADS> gamepads_;
|
||||
|
||||
// void init_esp32_gpio()
|
||||
// {
|
||||
// gpio_init(ESP_PROG_PIN);
|
||||
// gpio_set_dir(ESP_PROG_PIN, GPIO_IN);
|
||||
// gpio_pull_up(ESP_PROG_PIN);
|
||||
|
||||
// gpio_init(ESP_RST_PIN);
|
||||
// gpio_set_dir(ESP_RST_PIN, GPIO_IN);
|
||||
// gpio_pull_up(ESP_RST_PIN);
|
||||
// }
|
||||
|
||||
// bool uart_passthrough_mode()
|
||||
// {
|
||||
// gpio_init(MODE_SEL_PIN);
|
||||
// gpio_set_dir(MODE_SEL_PIN, GPIO_IN);
|
||||
// gpio_pull_up(MODE_SEL_PIN);
|
||||
|
||||
// if (gpio_get(MODE_SEL_PIN) == 0)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// void reset_esp32()
|
||||
// {
|
||||
// gpio_init(ESP_RST_PIN);
|
||||
// gpio_set_dir(ESP_RST_PIN, GPIO_OUT);
|
||||
// gpio_put(ESP_RST_PIN, 0);
|
||||
// sleep_ms(500);
|
||||
// gpio_put(ESP_RST_PIN, 1);
|
||||
// sleep_ms(250);
|
||||
// }
|
||||
|
||||
// void reset_esp32()
|
||||
// {
|
||||
// gpio_init(ESP_PROG_PIN);
|
||||
// gpio_set_dir(ESP_PROG_PIN, GPIO_OUT);
|
||||
// gpio_put(ESP_PROG_PIN, 1);
|
||||
|
||||
// gpio_init(ESP_RST_PIN);
|
||||
// gpio_set_dir(ESP_RST_PIN, GPIO_OUT);
|
||||
// gpio_put(ESP_PROG_PIN, 1);
|
||||
|
||||
// gpio_put(ESP_PROG_PIN, 0);
|
||||
// sleep_ms(250);
|
||||
|
||||
// gpio_put(ESP_RST_PIN, 0);
|
||||
// sleep_ms(500);
|
||||
// gpio_put(ESP_RST_PIN, 1);
|
||||
// sleep_ms(250);
|
||||
// gpio_put(ESP_PROG_PIN, 1);
|
||||
// }
|
||||
|
||||
void run_esp32_uart_bridge()
|
||||
{
|
||||
DeviceManager& device_manager = DeviceManager::get_instance();
|
||||
device_manager.initialize_driver(DeviceDriver::Type::UART_BRIDGE);
|
||||
board_api::enter_esp32_prog_mode();
|
||||
// reset_esp32();
|
||||
device_manager.get_driver()->process(0, gamepads_.front()); //Runs UART Bridge task
|
||||
}
|
||||
|
||||
void core1_task()
|
||||
{
|
||||
i2c_driver_esp::initialize(gamepads_);
|
||||
|
||||
while (true)
|
||||
{
|
||||
__wfi();
|
||||
}
|
||||
}
|
||||
|
||||
bool gp_check_cb(repeating_timer_t* rt)
|
||||
{
|
||||
GPCheckContext* gp_check_ctx = static_cast<GPCheckContext*>(rt->user_data);
|
||||
gp_check_ctx->driver_changed = gp_check_ctx->user_settings.check_for_driver_change(gamepads_.front());
|
||||
return true;
|
||||
}
|
||||
|
||||
void run_program()
|
||||
{
|
||||
UserSettings user_settings;
|
||||
user_settings.initialize_flash();
|
||||
|
||||
board_api::init_gpio();
|
||||
|
||||
// init_esp32_gpio();
|
||||
|
||||
if (board_api::uart_bridge_mode())
|
||||
// if (uart_passthrough_mode())
|
||||
{
|
||||
run_esp32_uart_bridge();
|
||||
return;
|
||||
}
|
||||
// else if (!user_settings.verify_firmware_version())
|
||||
// {
|
||||
// user_settings.write_firmware_version();
|
||||
// }
|
||||
|
||||
// user_settings.initialize_flash();
|
||||
|
||||
board_init();
|
||||
|
||||
for (uint8_t i = 0; i < gamepads_.size(); ++i)
|
||||
{
|
||||
gamepads_[i].set_profile(user_settings.get_profile_by_index(i));
|
||||
}
|
||||
|
||||
DeviceManager& device_manager = DeviceManager::get_instance();
|
||||
// device_manager.initialize_driver(user_settings.get_current_driver());
|
||||
device_manager.initialize_driver(DeviceDriver::Type::XINPUT);
|
||||
|
||||
multicore_reset_core1();
|
||||
multicore_launch_core1(core1_task);
|
||||
|
||||
board_api::reset_esp32();
|
||||
// reset_esp32();
|
||||
|
||||
// GPCheckContext gp_check_ctx = { false, user_settings };
|
||||
// repeating_timer_t gp_check_timer;
|
||||
// add_repeating_timer_ms(UserSettings::GP_CHECK_DELAY_MS, gp_check_cb, &gp_check_ctx, &gp_check_timer);
|
||||
|
||||
DeviceDriver* device_driver = device_manager.get_driver();
|
||||
|
||||
tud_init(BOARD_TUD_RHPORT);
|
||||
|
||||
while (true)
|
||||
{
|
||||
for (uint8_t i = 0; i < gamepads_.size(); ++i)
|
||||
{
|
||||
device_driver->process(i, gamepads_[i]);
|
||||
tud_task();
|
||||
}
|
||||
// device_driver->process(0, gamepads_.front());
|
||||
// tud_task();
|
||||
|
||||
sleep_us(100);
|
||||
|
||||
// if (gp_check_ctx.driver_changed)
|
||||
// {
|
||||
// cancel_repeating_timer(&gp_check_timer);
|
||||
// user_settings.store_driver_type_safe(user_settings.get_current_driver());
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OGXMini
|
||||
|
||||
#endif // OGXM_BOARD == W_ESP32
|
||||
89
Firmware/RP2040/src/OGXMini/OGXMini_PicoW.cpp
Normal file
89
Firmware/RP2040/src/OGXMini/OGXMini_PicoW.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "board_config.h"
|
||||
#if (OGXM_BOARD == PI_PICOW)
|
||||
|
||||
#include <array>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <pico/multicore.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/clocks.h>
|
||||
#include <pico/cyw43_arch.h>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "bsp/board_api.h"
|
||||
|
||||
#include "USBDevice/DeviceManager.h"
|
||||
#include "Board/board_api.h"
|
||||
#include "Bluepad32/Bluepad32.h"
|
||||
#include "OGXMini/OGXMini.h"
|
||||
|
||||
namespace OGXMini {
|
||||
|
||||
std::array<Gamepad, MAX_GAMEPADS> gamepads_;
|
||||
|
||||
void core1_task()
|
||||
{
|
||||
if (cyw43_arch_init() != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bluepad32::initialize(gamepads_);
|
||||
bluepad32::run_loop();
|
||||
}
|
||||
|
||||
bool gp_check_cb(repeating_timer_t* rt)
|
||||
{
|
||||
GPCheckContext* gp_check_ctx = static_cast<GPCheckContext*>(rt->user_data);
|
||||
gp_check_ctx->driver_changed = gp_check_ctx->user_settings.check_for_driver_change(gamepads_.front());
|
||||
return true;
|
||||
}
|
||||
|
||||
void run_program()
|
||||
{
|
||||
UserSettings user_settings;
|
||||
user_settings.initialize_flash();
|
||||
|
||||
board_init();
|
||||
board_api::init_gpio();
|
||||
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
|
||||
{
|
||||
gamepads_[i].set_profile(user_settings.get_profile_by_index(i));
|
||||
}
|
||||
|
||||
DeviceManager& device_manager = DeviceManager::get_instance();
|
||||
device_manager.initialize_driver(user_settings.get_current_driver());
|
||||
|
||||
multicore_reset_core1();
|
||||
multicore_launch_core1(core1_task);
|
||||
|
||||
GPCheckContext gp_check_ctx = { false, user_settings };
|
||||
repeating_timer_t gp_check_timer;
|
||||
add_repeating_timer_ms(UserSettings::GP_CHECK_DELAY_MS, gp_check_cb, &gp_check_ctx, &gp_check_timer);
|
||||
|
||||
DeviceDriver* device_driver = device_manager.get_driver();
|
||||
|
||||
tud_init(BOARD_TUD_RHPORT);
|
||||
|
||||
while (true)
|
||||
{
|
||||
for (uint8_t i = 0; i < gamepads_.size(); ++i)
|
||||
{
|
||||
device_driver->process(i, gamepads_[i]);
|
||||
tud_task();
|
||||
}
|
||||
|
||||
sleep_us(100);
|
||||
|
||||
if (gp_check_ctx.driver_changed)
|
||||
{
|
||||
cancel_repeating_timer(&gp_check_timer);
|
||||
user_settings.store_driver_type_safe(user_settings.get_current_driver());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OGXMini
|
||||
|
||||
#endif // (OGXM_BOARD == PI_PICOW)
|
||||
135
Firmware/RP2040/src/OGXMini/OGXMini_Standard.cpp
Normal file
135
Firmware/RP2040/src/OGXMini/OGXMini_Standard.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include "board_config.h"
|
||||
#if (OGXM_BOARD == ADA_FEATHER) || (OGXM_BOARD == RP_ZERO) || (OGXM_BOARD == PI_PICO)
|
||||
|
||||
#include <array>
|
||||
#include <pico/multicore.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "bsp/board_api.h"
|
||||
#include "pio_usb.h"
|
||||
|
||||
#include "USBHost/HostManager.h"
|
||||
#include "USBDevice/DeviceManager.h"
|
||||
#include "Board/board_api.h"
|
||||
#include "OGXMini/OGXMini.h"
|
||||
#include "Gamepad.h"
|
||||
|
||||
namespace OGXMini {
|
||||
|
||||
std::atomic<TUDStatus> tud_status_ = TUDStatus::NOCHANGE;
|
||||
std::array<Gamepad, MAX_GAMEPADS> gamepads_;
|
||||
|
||||
void update_tuh_status(bool mounted)
|
||||
{
|
||||
if (mounted)
|
||||
{
|
||||
tud_status_.store(TUDStatus::INIT);
|
||||
board_api::set_led(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
tud_status_.store(TUDStatus::DEINIT);
|
||||
board_api::set_led(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool feedback_cb(repeating_timer_t* rt)
|
||||
{
|
||||
static_cast<HostManager*>(rt->user_data)->send_feedback();
|
||||
return true;
|
||||
}
|
||||
|
||||
void core1_task()
|
||||
{
|
||||
HostManager& host_manager = HostManager::get_instance();
|
||||
host_manager.initialize(gamepads_);
|
||||
|
||||
pio_usb_configuration_t pio_cfg = PIO_USB_CONFIG;
|
||||
tuh_configure(BOARD_TUH_RHPORT, TUH_CFGID_RPI_PIO_USB_CONFIGURATION, &pio_cfg);
|
||||
|
||||
tuh_init(BOARD_TUH_RHPORT);
|
||||
|
||||
repeating_timer_t feedback_timer;
|
||||
add_repeating_timer_ms(FEEDBACK_DELAY_MS, feedback_cb, &host_manager, &feedback_timer);
|
||||
|
||||
while (true)
|
||||
{
|
||||
tuh_task();
|
||||
sleep_us(100);
|
||||
}
|
||||
}
|
||||
|
||||
bool gp_check_cb(repeating_timer_t* rt)
|
||||
{
|
||||
GPCheckContext* gp_check_ctx = static_cast<GPCheckContext*>(rt->user_data);
|
||||
gp_check_ctx->driver_changed = gp_check_ctx->user_settings.check_for_driver_change(gamepads_.front());
|
||||
return true;
|
||||
}
|
||||
|
||||
void run_program()
|
||||
{
|
||||
UserSettings user_settings;
|
||||
user_settings.initialize_flash();
|
||||
|
||||
board_init();
|
||||
|
||||
board_api::init_gpio();
|
||||
board_api::set_led(false);
|
||||
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
|
||||
{
|
||||
gamepads_[i].set_profile(user_settings.get_profile_by_index(i));
|
||||
}
|
||||
|
||||
DeviceManager& device_manager = DeviceManager::get_instance();
|
||||
device_manager.initialize_driver(user_settings.get_current_driver());
|
||||
|
||||
multicore_reset_core1();
|
||||
multicore_launch_core1(core1_task);
|
||||
|
||||
GPCheckContext gp_check_ctx = { false, user_settings };
|
||||
repeating_timer_t gp_check_timer;
|
||||
add_repeating_timer_ms(UserSettings::GP_CHECK_DELAY_MS, gp_check_cb, &gp_check_ctx, &gp_check_timer);
|
||||
|
||||
DeviceDriver* device_driver = device_manager.get_driver();
|
||||
|
||||
while (true)
|
||||
{
|
||||
switch (tud_status_.load())
|
||||
{
|
||||
case TUDStatus::NOCHANGE:
|
||||
break;
|
||||
case TUDStatus::INIT:
|
||||
tud_init(BOARD_TUD_RHPORT);
|
||||
tud_status_.store(TUDStatus::NOCHANGE);
|
||||
break;
|
||||
case TUDStatus::DEINIT:
|
||||
tud_disconnect();
|
||||
sleep_ms(300);
|
||||
multicore_reset_core1();
|
||||
sleep_ms(300);
|
||||
board_api::reboot();
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < gamepads_.size(); ++i)
|
||||
{
|
||||
device_driver->process(i, gamepads_[i]);
|
||||
tud_task();
|
||||
}
|
||||
|
||||
sleep_us(100);
|
||||
|
||||
if (gp_check_ctx.driver_changed)
|
||||
{
|
||||
cancel_repeating_timer(&gp_check_timer);
|
||||
user_settings.store_driver_type_safe(user_settings.get_current_driver());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OGXMini
|
||||
|
||||
#endif // (OGXM_BOARD == ADA_FEATHER) || (OGXM_BOARD == RP_ZERO) || (OGXM_BOARD == PI_PICO)
|
||||
171
Firmware/RP2040/src/USBDevice/DeviceDriver/DInput/DInput.cpp
Normal file
171
Firmware/RP2040/src/USBDevice/DeviceDriver/DInput/DInput.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "class/hid/hid_device.h"
|
||||
|
||||
#include "Descriptors/PS3.h"
|
||||
#include "USBDevice/DeviceDriver/DInput/DInput.h"
|
||||
|
||||
bool DInputDevice::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, const_cast<void*>(static_cast<const void*>(PS3::MAGIC_BYTES)), sizeof(PS3::MAGIC_BYTES));
|
||||
}
|
||||
return hidd_control_xfer_cb(rhport, stage, request);
|
||||
}
|
||||
|
||||
void DInputDevice::initialize()
|
||||
{
|
||||
std::memset(&in_report_, 0, sizeof(DInput::InReport));
|
||||
in_report_.dpad = DInput::DPad::CENTER;
|
||||
in_report_.joystick_lx = DInput::AXIS_MID;
|
||||
in_report_.joystick_ly = DInput::AXIS_MID;
|
||||
in_report_.joystick_rx = DInput::AXIS_MID;
|
||||
in_report_.joystick_ry = DInput::AXIS_MID;
|
||||
|
||||
for (auto& prev_in_report : prev_in_reports_)
|
||||
{
|
||||
std::memcpy(&prev_in_report, &in_report_, sizeof(DInput::InReport));
|
||||
}
|
||||
|
||||
class_driver_ = {
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "HID",
|
||||
#endif
|
||||
.init = hidd_init,
|
||||
.reset = hidd_reset,
|
||||
.open = hidd_open,
|
||||
.control_xfer_cb = DInputDevice::control_xfer_cb,
|
||||
.xfer_cb = hidd_xfer_cb,
|
||||
.sof = NULL
|
||||
};
|
||||
}
|
||||
|
||||
void DInputDevice::process(const uint8_t idx, Gamepad& gamepad)
|
||||
{
|
||||
switch (gamepad.get_dpad_buttons())
|
||||
{
|
||||
case Gamepad::DPad::UP:
|
||||
in_report_.dpad = DInput::DPad::UP;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN:
|
||||
in_report_.dpad = DInput::DPad::DOWN;
|
||||
break;
|
||||
case Gamepad::DPad::LEFT:
|
||||
in_report_.dpad = DInput::DPad::LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::RIGHT:
|
||||
in_report_.dpad = DInput::DPad::RIGHT;
|
||||
break;
|
||||
case Gamepad::DPad::UP_LEFT:
|
||||
in_report_.dpad = DInput::DPad::UP_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::UP_RIGHT:
|
||||
in_report_.dpad = DInput::DPad::UP_RIGHT;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN_LEFT:
|
||||
in_report_.dpad = DInput::DPad::DOWN_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN_RIGHT:
|
||||
in_report_.dpad = DInput::DPad::DOWN_RIGHT;
|
||||
break;
|
||||
default:
|
||||
in_report_.dpad = DInput::DPad::CENTER;
|
||||
break;
|
||||
}
|
||||
|
||||
std::memset(in_report_.buttons, 0, sizeof(in_report_.buttons));
|
||||
|
||||
uint16_t gamepad_buttons = gamepad.get_buttons();
|
||||
|
||||
if (gamepad_buttons & Gamepad::Button::A) in_report_.buttons[0] |= DInput::Buttons0::CROSS;
|
||||
if (gamepad_buttons & Gamepad::Button::B) in_report_.buttons[0] |= DInput::Buttons0::CIRCLE;
|
||||
if (gamepad_buttons & Gamepad::Button::X) in_report_.buttons[0] |= DInput::Buttons0::SQUARE;
|
||||
if (gamepad_buttons & Gamepad::Button::Y) in_report_.buttons[0] |= DInput::Buttons0::TRIANGLE;
|
||||
if (gamepad_buttons & Gamepad::Button::LB) in_report_.buttons[0] |= DInput::Buttons0::L1;
|
||||
if (gamepad_buttons & Gamepad::Button::RB) in_report_.buttons[0] |= DInput::Buttons0::R1;
|
||||
|
||||
if (gamepad_buttons & Gamepad::Button::L3) in_report_.buttons[1] |= DInput::Buttons1::L3;
|
||||
if (gamepad_buttons & Gamepad::Button::R3) in_report_.buttons[1] |= DInput::Buttons1::R3;
|
||||
if (gamepad_buttons & Gamepad::Button::BACK) in_report_.buttons[1] |= DInput::Buttons1::SELECT;
|
||||
if (gamepad_buttons & Gamepad::Button::START) in_report_.buttons[1] |= DInput::Buttons1::START;
|
||||
if (gamepad_buttons & Gamepad::Button::SYS) in_report_.buttons[1] |= DInput::Buttons1::PS;
|
||||
if (gamepad_buttons & Gamepad::Button::MISC) in_report_.buttons[1] |= DInput::Buttons1::TP;
|
||||
|
||||
if (gamepad.analog_enabled())
|
||||
{
|
||||
in_report_.up_axis = gamepad.get_analog_up();
|
||||
in_report_.down_axis = gamepad.get_analog_down();
|
||||
in_report_.right_axis = gamepad.get_analog_right();
|
||||
in_report_.left_axis = gamepad.get_analog_left();
|
||||
|
||||
in_report_.triangle_axis = gamepad.get_analog_y();
|
||||
in_report_.circle_axis = gamepad.get_analog_x();
|
||||
in_report_.cross_axis = gamepad.get_analog_b();
|
||||
in_report_.square_axis = gamepad.get_analog_a();
|
||||
|
||||
in_report_.r1_axis = gamepad.get_analog_rb();
|
||||
in_report_.l1_axis = gamepad.get_analog_lb();
|
||||
}
|
||||
|
||||
in_report_.joystick_lx = gamepad.get_joystick_lx().uint8();
|
||||
in_report_.joystick_ly = gamepad.get_joystick_ly().uint8();
|
||||
in_report_.joystick_rx = gamepad.get_joystick_rx().uint8();
|
||||
in_report_.joystick_ry = gamepad.get_joystick_ry().uint8();
|
||||
|
||||
in_report_.l2_axis = gamepad.get_trigger_l().uint8();
|
||||
in_report_.r2_axis = gamepad.get_trigger_r().uint8();
|
||||
|
||||
if (tud_suspended())
|
||||
{
|
||||
tud_remote_wakeup();
|
||||
}
|
||||
|
||||
if (tud_hid_n_ready(idx) &&
|
||||
// std::memcmp(&in_report_, &prev_in_reports_[idx], sizeof(DInput::InReport)) != 0 &&
|
||||
tud_hid_n_report(idx, 0, reinterpret_cast<void*>(&in_report_), sizeof(DInput::InReport)))
|
||||
{
|
||||
std::memcpy(&prev_in_reports_[idx], &in_report_, sizeof(DInput::InReport));
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t DInputDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
std::memcpy(buffer, &in_report_, sizeof(DInput::InReport));
|
||||
return sizeof(DInput::InReport);
|
||||
}
|
||||
|
||||
void DInputDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
|
||||
|
||||
bool DInputDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t* DInputDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
const char* value = reinterpret_cast<const char*>(DInput::STRING_DESCRIPTORS[index]);
|
||||
return get_string_descriptor(value, index);
|
||||
}
|
||||
|
||||
const uint8_t* DInputDevice::get_descriptor_device_cb()
|
||||
{
|
||||
return DInput::DEVICE_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* DInputDevice::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return DInput::REPORT_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* DInputDevice::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return DInput::CONFIGURATION_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* DInputDevice::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
32
Firmware/RP2040/src/USBDevice/DeviceDriver/DInput/DInput.h
Normal file
32
Firmware/RP2040/src/USBDevice/DeviceDriver/DInput/DInput.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef _DINPUT_DEVICE_H_
|
||||
#define _DINPUT_DEVICE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "board_config.h"
|
||||
#include "USBDevice/DeviceDriver/DeviceDriver.h"
|
||||
#include "Descriptors/DInput.h"
|
||||
|
||||
class DInputDevice : public DeviceDriver
|
||||
{
|
||||
public:
|
||||
void initialize() override;
|
||||
void process(const uint8_t idx, Gamepad& gamepad) override;
|
||||
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
|
||||
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
|
||||
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
|
||||
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
|
||||
const uint8_t* get_descriptor_device_cb() override;
|
||||
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
|
||||
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
|
||||
const uint8_t* get_descriptor_device_qualifier_cb() override;
|
||||
|
||||
private:
|
||||
DInput::InReport in_report_;
|
||||
std::array<DInput::InReport, MAX_GAMEPADS> prev_in_reports_;
|
||||
|
||||
static bool control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
};
|
||||
|
||||
#endif // _DINPUT_DEVICE_H_
|
||||
27
Firmware/RP2040/src/USBDevice/DeviceDriver/DeviceDriver.cpp
Normal file
27
Firmware/RP2040/src/USBDevice/DeviceDriver/DeviceDriver.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "USBDevice/DeviceDriver/DeviceDriver.h"
|
||||
|
||||
uint16_t* DeviceDriver::get_string_descriptor(const char* value, uint8_t index)
|
||||
{
|
||||
static uint16_t string_desc_buffer[32];
|
||||
size_t char_count;
|
||||
|
||||
if ( index == 0 )
|
||||
{
|
||||
char_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
char_count = strlen(value);
|
||||
if (char_count > 31)
|
||||
{
|
||||
char_count = 31;
|
||||
}
|
||||
}
|
||||
for (uint8_t i = 0; i < char_count; i++)
|
||||
{
|
||||
string_desc_buffer[i + 1] = value[i];
|
||||
}
|
||||
|
||||
string_desc_buffer[0] = static_cast<uint16_t>((0x03 << 8) | (2 * static_cast<uint8_t>(char_count) + 2));
|
||||
return string_desc_buffer;
|
||||
}
|
||||
49
Firmware/RP2040/src/USBDevice/DeviceDriver/DeviceDriver.h
Normal file
49
Firmware/RP2040/src/USBDevice/DeviceDriver/DeviceDriver.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef _DEVICE_DRIVER_H_
|
||||
#define _DEVICE_DRIVER_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "class/hid/hid.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
class DeviceDriver
|
||||
{
|
||||
public:
|
||||
enum class Type : uint8_t
|
||||
{
|
||||
NONE = 0,
|
||||
PS3 = 1,
|
||||
DINPUT = 2,
|
||||
SWITCH = 4,
|
||||
PSCLASSIC = 5,
|
||||
XINPUT = 6,
|
||||
XBOXOG = 7,
|
||||
XBOXOG_SB = 8,
|
||||
XBOXOG_XR = 9,
|
||||
WEBAPP = 100,
|
||||
UART_BRIDGE = 101
|
||||
};
|
||||
|
||||
virtual void initialize() = 0;
|
||||
virtual void process(const uint8_t idx, Gamepad& gamepad) = 0;
|
||||
virtual uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t req_len) = 0;
|
||||
virtual void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t buffer_size) = 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;
|
||||
|
||||
const usbd_class_driver_t* get_class_driver() { return &class_driver_; };
|
||||
|
||||
protected:
|
||||
usbd_class_driver_t class_driver_;
|
||||
|
||||
uint16_t* get_string_descriptor(const char* value, uint8_t index);
|
||||
};
|
||||
|
||||
#endif // _DEVICE_DRIVER_H_
|
||||
332
Firmware/RP2040/src/USBDevice/DeviceDriver/PS3/PS3.cpp
Normal file
332
Firmware/RP2040/src/USBDevice/DeviceDriver/PS3/PS3.cpp
Normal file
@@ -0,0 +1,332 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "USBDevice/DeviceDriver/PS3/PS3.h"
|
||||
|
||||
void PS3Device::initialize()
|
||||
{
|
||||
class_driver_ =
|
||||
{
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "PS3",
|
||||
#endif
|
||||
.init = hidd_init,
|
||||
.deinit = hidd_deinit,
|
||||
.reset = hidd_reset,
|
||||
.open = hidd_open,
|
||||
.control_xfer_cb = hidd_control_xfer_cb,
|
||||
.xfer_cb = hidd_xfer_cb,
|
||||
.sof = NULL
|
||||
};
|
||||
|
||||
in_report_ = PS3::InReport();
|
||||
|
||||
// bt_info_ =
|
||||
// {
|
||||
// .reserved0 = {0xFF,0xFF},
|
||||
// .device_address = { 0x00, 0x20, 0x40, 0xCE, 0x00, 0x00, 0x00 },
|
||||
// .host_address = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
|
||||
// };
|
||||
|
||||
// for (uint8_t addr = 0; addr < 3; addr++)
|
||||
// {
|
||||
// bt_info_.device_address[4 + addr] = static_cast<uint8_t>(get_rand_32() % 0xFF);
|
||||
// }
|
||||
|
||||
// for (uint8_t addr = 0; addr < 6; addr++)
|
||||
// {
|
||||
// bt_info_.host_address[1 + addr] = static_cast<uint8_t>(get_rand_32() % 0xFF);
|
||||
// }
|
||||
}
|
||||
|
||||
void PS3Device::process(const uint8_t idx, Gamepad& gamepad)
|
||||
{
|
||||
uint8_t gp_dpad = gamepad.get_dpad_buttons();
|
||||
uint16_t gp_buttons = gamepad.get_buttons();
|
||||
uint8_t gp_trigger_l = gamepad.get_trigger_l().uint8();
|
||||
uint8_t gp_trigger_r = gamepad.get_trigger_r().uint8();
|
||||
|
||||
std::memset(in_report_.buttons, 0, sizeof(in_report_.buttons));
|
||||
|
||||
switch (gp_dpad)
|
||||
{
|
||||
case Gamepad::DPad::UP:
|
||||
in_report_.buttons[0] = PS3::Buttons0::DPAD_UP;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN:
|
||||
in_report_.buttons[0] = PS3::Buttons0::DPAD_DOWN;
|
||||
break;
|
||||
case Gamepad::DPad::LEFT:
|
||||
in_report_.buttons[0] = PS3::Buttons0::DPAD_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::RIGHT:
|
||||
in_report_.buttons[0] = PS3::Buttons0::DPAD_RIGHT;
|
||||
break;
|
||||
case Gamepad::DPad::UP_LEFT:
|
||||
in_report_.buttons[0] = PS3::Buttons0::DPAD_UP | PS3::Buttons0::DPAD_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::UP_RIGHT:
|
||||
in_report_.buttons[0] = PS3::Buttons0::DPAD_UP | PS3::Buttons0::DPAD_RIGHT;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN_LEFT:
|
||||
in_report_.buttons[0] = PS3::Buttons0::DPAD_DOWN | PS3::Buttons0::DPAD_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN_RIGHT:
|
||||
in_report_.buttons[0] = PS3::Buttons0::DPAD_DOWN | PS3::Buttons0::DPAD_RIGHT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (gp_buttons & Gamepad::Button::X) in_report_.buttons[1] |= PS3::Buttons1::SQUARE;
|
||||
if (gp_buttons & Gamepad::Button::A) in_report_.buttons[1] |= PS3::Buttons1::CROSS;
|
||||
if (gp_buttons & Gamepad::Button::Y) in_report_.buttons[1] |= PS3::Buttons1::TRIANGLE;
|
||||
if (gp_buttons & Gamepad::Button::B) in_report_.buttons[1] |= PS3::Buttons1::CIRCLE;
|
||||
if (gp_buttons & Gamepad::Button::LB) in_report_.buttons[1] |= PS3::Buttons1::L1;
|
||||
if (gp_buttons & Gamepad::Button::RB) in_report_.buttons[1] |= PS3::Buttons1::R1;
|
||||
if (gp_buttons & Gamepad::Button::BACK) in_report_.buttons[0] |= PS3::Buttons0::SELECT;
|
||||
if (gp_buttons & Gamepad::Button::START) in_report_.buttons[0] |= PS3::Buttons0::START;
|
||||
if (gp_buttons & Gamepad::Button::L3) in_report_.buttons[0] |= PS3::Buttons0::L3;
|
||||
if (gp_buttons & Gamepad::Button::R3) in_report_.buttons[0] |= PS3::Buttons0::R3;
|
||||
if (gp_buttons & Gamepad::Button::SYS) in_report_.buttons[2] |= PS3::Buttons2::PS;
|
||||
if (gp_buttons & Gamepad::Button::MISC) in_report_.buttons[2] |= PS3::Buttons2::TP;
|
||||
|
||||
if (gp_trigger_l > 0) in_report_.buttons[1] |= PS3::Buttons1::L2;
|
||||
if (gp_trigger_r > 0) in_report_.buttons[1] |= PS3::Buttons1::R2;
|
||||
|
||||
in_report_.joystick_lx = gamepad.get_joystick_lx().uint8();
|
||||
in_report_.joystick_ly = gamepad.get_joystick_ly().uint8();
|
||||
in_report_.joystick_rx = gamepad.get_joystick_rx().uint8();
|
||||
in_report_.joystick_ry = gamepad.get_joystick_ry().uint8();
|
||||
|
||||
if (gamepad.analog_enabled())
|
||||
{
|
||||
in_report_.up_axis = gamepad.get_analog_up();
|
||||
in_report_.down_axis = gamepad.get_analog_down();
|
||||
in_report_.right_axis = gamepad.get_analog_right();
|
||||
in_report_.left_axis = gamepad.get_analog_left();
|
||||
|
||||
in_report_.triangle_axis = gamepad.get_analog_y();
|
||||
in_report_.circle_axis = gamepad.get_analog_x();
|
||||
in_report_.cross_axis = gamepad.get_analog_b();
|
||||
in_report_.square_axis = gamepad.get_analog_a();
|
||||
|
||||
in_report_.r1_axis = gamepad.get_analog_rb();
|
||||
in_report_.l1_axis = gamepad.get_analog_lb();
|
||||
}
|
||||
else
|
||||
{
|
||||
in_report_.up_axis = (gp_dpad & Gamepad::DPad::UP) ? 0xFF : 0;
|
||||
in_report_.down_axis = (gp_dpad & Gamepad::DPad::DOWN) ? 0xFF : 0;
|
||||
in_report_.right_axis = (gp_dpad & Gamepad::DPad::RIGHT) ? 0xFF : 0;
|
||||
in_report_.left_axis = (gp_dpad & Gamepad::DPad::LEFT) ? 0xFF : 0;
|
||||
|
||||
in_report_.triangle_axis = (gp_buttons & Gamepad::Button::Y) ? 0xFF : 0;
|
||||
in_report_.circle_axis = (gp_buttons & Gamepad::Button::X) ? 0xFF : 0;
|
||||
in_report_.cross_axis = (gp_buttons & Gamepad::Button::B) ? 0xFF : 0;
|
||||
in_report_.square_axis = (gp_buttons & Gamepad::Button::A) ? 0xFF : 0;
|
||||
|
||||
in_report_.r1_axis = (gp_buttons & Gamepad::Button::RB) ? 0xFF : 0;
|
||||
in_report_.l1_axis = (gp_buttons & Gamepad::Button::LB) ? 0xFF : 0;
|
||||
}
|
||||
|
||||
if (tud_suspended())
|
||||
{
|
||||
tud_remote_wakeup();
|
||||
}
|
||||
|
||||
if (std::memcmp(&prev_in_report_, &in_report_, sizeof(PS3::InReport)) != 0 &&
|
||||
tud_hid_ready() &&
|
||||
tud_hid_report(0, reinterpret_cast<uint8_t*>(&in_report_), sizeof(PS3::InReport)))
|
||||
{
|
||||
std::memcpy(&prev_in_report_, &in_report_, sizeof(PS3::InReport));
|
||||
}
|
||||
|
||||
if (new_out_report_)
|
||||
{
|
||||
gamepad.set_rumble_l(out_report_.rumble.left_motor_force);
|
||||
gamepad.set_rumble_r(out_report_.rumble.right_motor_on);
|
||||
new_out_report_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr uint8_t output_ps3_0x01[] =
|
||||
{
|
||||
0x01, 0x04, 0x00, 0x0b, 0x0c, 0x01, 0x02, 0x18,
|
||||
0x18, 0x18, 0x18, 0x09, 0x0a, 0x10, 0x11, 0x12,
|
||||
0x13, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02,
|
||||
0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x04, 0x04,
|
||||
0x04, 0x04, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02,
|
||||
0x07, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
// calibration data
|
||||
static constexpr uint8_t output_ps3_0xef[] =
|
||||
{
|
||||
0xef, 0x04, 0x00, 0x0b, 0x03, 0x01, 0xa0, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff,
|
||||
0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
|
||||
};
|
||||
|
||||
// unknown
|
||||
static constexpr uint8_t output_ps3_0xf5[] =
|
||||
{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // host address - must match 0xf2
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
// unknown
|
||||
static constexpr uint8_t output_ps3_0xf7[] =
|
||||
{
|
||||
0x02, 0x01, 0xf8, 0x02, 0xe2, 0x01, 0x05, 0xff,
|
||||
0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
// unknown
|
||||
static constexpr uint8_t output_ps3_0xf8[] =
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
/* Based on: https://github.com/OpenStickCommunity/GP2040-CE/blob/main/src/drivers/ps3/PS3Driver.cpp */
|
||||
|
||||
uint16_t PS3Device::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
if (report_type == HID_REPORT_TYPE_INPUT)
|
||||
{
|
||||
std::memcpy(buffer, &in_report_, sizeof(PS3::InReport));
|
||||
return sizeof(PS3::InReport);
|
||||
}
|
||||
else if (report_type == HID_REPORT_TYPE_FEATURE)
|
||||
{
|
||||
uint16_t resp_len = 0;
|
||||
uint8_t ctr = 0;
|
||||
|
||||
switch(report_id)
|
||||
{
|
||||
case PS3::ReportID::FEATURE_01:
|
||||
resp_len = reqlen;
|
||||
std::memcpy(buffer, output_ps3_0x01, resp_len);
|
||||
return resp_len;
|
||||
|
||||
case PS3::ReportID::FEATURE_EF:
|
||||
resp_len = reqlen;
|
||||
std::memcpy(buffer, output_ps3_0xef, resp_len);
|
||||
buffer[6] = ef_byte_;
|
||||
return resp_len;
|
||||
|
||||
case PS3::ReportID::GET_PAIRING_INFO:
|
||||
resp_len = reqlen;
|
||||
std::memcpy(buffer, &bt_info_, resp_len);
|
||||
return resp_len;
|
||||
|
||||
case PS3::ReportID::FEATURE_F5:
|
||||
resp_len = reqlen;
|
||||
std::memcpy(buffer, output_ps3_0xf5, resp_len);
|
||||
|
||||
for (ctr = 0; ctr < 6; ctr++)
|
||||
{
|
||||
buffer[1 + ctr] = bt_info_.host_address[ctr];
|
||||
}
|
||||
return resp_len;
|
||||
|
||||
case PS3::ReportID::FEATURE_F7:
|
||||
resp_len = reqlen;
|
||||
std::memcpy(buffer, output_ps3_0xf7, resp_len);
|
||||
return resp_len;
|
||||
|
||||
case PS3::ReportID::FEATURE_F8:
|
||||
resp_len = reqlen;
|
||||
std::memcpy(buffer, output_ps3_0xf8, resp_len);
|
||||
buffer[6] = ef_byte_;
|
||||
return resp_len;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void PS3Device::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
|
||||
{
|
||||
if (report_type == HID_REPORT_TYPE_FEATURE)
|
||||
{
|
||||
switch(report_id)
|
||||
{
|
||||
case PS3::ReportID::FEATURE_EF:
|
||||
ef_byte_ = buffer[6];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (report_type == HID_REPORT_TYPE_OUTPUT )
|
||||
{
|
||||
// DS3 command
|
||||
uint8_t const *buf = buffer;
|
||||
if (report_id == 0 && bufsize > 0)
|
||||
{
|
||||
report_id = buffer[0];
|
||||
bufsize = bufsize - 1;
|
||||
buf = &buffer[1];
|
||||
}
|
||||
|
||||
switch(report_id)
|
||||
{
|
||||
case PS3::ReportID::FEATURE_01:
|
||||
std::memcpy(&out_report_, buf, std::min(bufsize, static_cast<uint16_t>(sizeof(PS3::OutReport))));
|
||||
new_out_report_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PS3Device::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t* PS3Device::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
const char *value = reinterpret_cast<const char*>(PS3::STRING_DESCRIPTORS[index]);
|
||||
return get_string_descriptor(value, index);
|
||||
}
|
||||
|
||||
const uint8_t* PS3Device::get_descriptor_device_cb()
|
||||
{
|
||||
return PS3::DEVICE_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* PS3Device::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return PS3::REPORT_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* PS3Device::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return PS3::CONFIGURATION_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* PS3Device::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
33
Firmware/RP2040/src/USBDevice/DeviceDriver/PS3/PS3.h
Normal file
33
Firmware/RP2040/src/USBDevice/DeviceDriver/PS3/PS3.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef _PS3_DEVICE_H_
|
||||
#define _PS3_DEVICE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "USBDevice/DeviceDriver/DeviceDriver.h"
|
||||
#include "Descriptors/PS3.h"
|
||||
|
||||
class PS3Device : public DeviceDriver
|
||||
{
|
||||
public:
|
||||
void initialize() override;
|
||||
void process(const uint8_t idx, Gamepad& gamepad) override;
|
||||
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
|
||||
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
|
||||
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
|
||||
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
|
||||
const uint8_t* get_descriptor_device_cb() override;
|
||||
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
|
||||
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
|
||||
const uint8_t* get_descriptor_device_qualifier_cb() override;
|
||||
|
||||
private:
|
||||
PS3::InReport in_report_;
|
||||
PS3::InReport prev_in_report_;
|
||||
PS3::OutReport out_report_;
|
||||
PS3::BTInfo bt_info_;
|
||||
uint8_t ef_byte_;
|
||||
bool new_out_report_{false};
|
||||
};
|
||||
|
||||
#endif // _PS3_DEVICE_H_
|
||||
@@ -0,0 +1,162 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "USBDevice/DeviceDriver/PSClassic/PSClassic.h"
|
||||
|
||||
void PSClassicDevice::initialize()
|
||||
{
|
||||
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 PSClassicDevice::process(const uint8_t idx, Gamepad& gamepad)
|
||||
{
|
||||
switch (gamepad.get_dpad_buttons())
|
||||
{
|
||||
case Gamepad::DPad::UP:
|
||||
in_report_.buttons = PSClassic::Buttons::UP;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN:
|
||||
in_report_.buttons = PSClassic::Buttons::DOWN;
|
||||
break;
|
||||
case Gamepad::DPad::LEFT:
|
||||
in_report_.buttons = PSClassic::Buttons::LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::RIGHT:
|
||||
in_report_.buttons = PSClassic::Buttons::RIGHT;
|
||||
break;
|
||||
case Gamepad::DPad::UP_LEFT:
|
||||
in_report_.buttons = PSClassic::Buttons::UP_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::UP_RIGHT:
|
||||
in_report_.buttons = PSClassic::Buttons::UP_RIGHT;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN_LEFT:
|
||||
in_report_.buttons = PSClassic::Buttons::DOWN_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN_RIGHT:
|
||||
in_report_.buttons = PSClassic::Buttons::DOWN_RIGHT;
|
||||
break;
|
||||
default:
|
||||
in_report_.buttons = PSClassic::Buttons::CENTER;
|
||||
break;
|
||||
}
|
||||
|
||||
int16_t joy_lx = gamepad.get_joystick_lx().int16();
|
||||
int16_t joy_ly = gamepad.get_joystick_ly().int16(true);
|
||||
int16_t joy_rx = gamepad.get_joystick_rx().int16();
|
||||
int16_t joy_ry = gamepad.get_joystick_ry().int16(true);
|
||||
|
||||
if (meets_pos_threshold(joy_lx, joy_rx))
|
||||
{
|
||||
if (meets_neg_45_threshold(joy_ly, joy_ry))
|
||||
{
|
||||
in_report_.buttons = PSClassic::Buttons::DOWN_RIGHT;
|
||||
}
|
||||
else if (meets_pos_45_threshold(joy_ly, joy_ry))
|
||||
{
|
||||
in_report_.buttons = PSClassic::Buttons::UP_RIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
in_report_.buttons = PSClassic::Buttons::RIGHT;
|
||||
}
|
||||
}
|
||||
else if (meets_neg_threshold(joy_lx, joy_rx))
|
||||
{
|
||||
if (meets_neg_45_threshold(joy_ly, joy_ry))
|
||||
{
|
||||
in_report_.buttons = PSClassic::Buttons::DOWN_LEFT;
|
||||
}
|
||||
else if (meets_pos_45_threshold(joy_ly, joy_ry))
|
||||
{
|
||||
in_report_.buttons = PSClassic::Buttons::UP_LEFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
in_report_.buttons = PSClassic::Buttons::LEFT;
|
||||
}
|
||||
}
|
||||
else if (meets_neg_threshold(joy_ly, joy_ry))
|
||||
{
|
||||
in_report_.buttons = PSClassic::Buttons::DOWN;
|
||||
}
|
||||
else if (meets_pos_threshold(joy_ly, joy_ry))
|
||||
{
|
||||
in_report_.buttons = PSClassic::Buttons::UP;
|
||||
}
|
||||
|
||||
uint16_t gamepad_buttons = gamepad.get_buttons();
|
||||
|
||||
if (gamepad_buttons & Gamepad::Button::A) in_report_.buttons |= PSClassic::Buttons::CROSS;
|
||||
if (gamepad_buttons & Gamepad::Button::B) in_report_.buttons |= PSClassic::Buttons::CIRCLE;
|
||||
if (gamepad_buttons & Gamepad::Button::X) in_report_.buttons |= PSClassic::Buttons::SQUARE;
|
||||
if (gamepad_buttons & Gamepad::Button::Y) in_report_.buttons |= PSClassic::Buttons::TRIANGLE;
|
||||
if (gamepad_buttons & Gamepad::Button::LB) in_report_.buttons |= PSClassic::Buttons::L1;
|
||||
if (gamepad_buttons & Gamepad::Button::RB) in_report_.buttons |= PSClassic::Buttons::R1;
|
||||
if (gamepad_buttons & Gamepad::Button::BACK) in_report_.buttons |= PSClassic::Buttons::SELECT;
|
||||
if (gamepad_buttons & Gamepad::Button::START) in_report_.buttons |= PSClassic::Buttons::START;
|
||||
|
||||
if (gamepad.get_trigger_l().uint8()) in_report_.buttons |= PSClassic::Buttons::L2;
|
||||
if (gamepad.get_trigger_r().uint8()) in_report_.buttons |= PSClassic::Buttons::R2;
|
||||
|
||||
if (tud_suspended())
|
||||
{
|
||||
tud_remote_wakeup();
|
||||
}
|
||||
|
||||
if (tud_hid_n_ready(idx) &&
|
||||
std::memcmp(&in_report_, &prev_in_report_, sizeof(PSClassic::InReport)) != 0 &&
|
||||
tud_hid_n_report(idx, 0, reinterpret_cast<uint8_t*>(&in_report_), sizeof(PSClassic::InReport)))
|
||||
{
|
||||
std::memcpy(&prev_in_report_, &in_report_, sizeof(PSClassic::InReport));
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t PSClassicDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
std::memcpy(buffer, &in_report_, sizeof(PSClassic::InReport));
|
||||
return sizeof(PSClassic::InReport);
|
||||
}
|
||||
|
||||
void PSClassicDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
|
||||
|
||||
bool PSClassicDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t* PSClassicDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
const char* value = reinterpret_cast<const char*>(PSClassic::STRING_DESCRIPTORS[index]);
|
||||
return get_string_descriptor(value, index);
|
||||
}
|
||||
|
||||
const uint8_t* PSClassicDevice::get_descriptor_device_cb()
|
||||
{
|
||||
return PSClassic::DEVICE_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* PSClassicDevice::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return PSClassic::REPORT_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* PSClassicDevice::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return PSClassic::CONFIGURATION_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* PSClassicDevice::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef _D_PSCLASSIC_DRIVER_H_
|
||||
#define _D_PSCLASSIC_DRIVER_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
#include "Descriptors/PSClassic.h"
|
||||
#include "USBDevice/DeviceDriver/DeviceDriver.h"
|
||||
|
||||
class PSClassicDevice : public DeviceDriver
|
||||
{
|
||||
public:
|
||||
void initialize() override;
|
||||
void process(const uint8_t idx, Gamepad& gamepad) override;
|
||||
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
|
||||
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
|
||||
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
|
||||
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
|
||||
const uint8_t* get_descriptor_device_cb() override;
|
||||
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) ;
|
||||
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
|
||||
const uint8_t* get_descriptor_device_qualifier_cb() override;
|
||||
|
||||
private:
|
||||
static constexpr int16_t JOY_POS_THRESHOLD = 10000;
|
||||
static constexpr int16_t JOY_NEG_THRESHOLD = -10000;
|
||||
static constexpr int16_t JOY_POS_45_THRESHOLD = JOY_POS_THRESHOLD * 2;
|
||||
static constexpr int16_t JOY_NEG_45_THRESHOLD = JOY_NEG_THRESHOLD * 2;
|
||||
|
||||
PSClassic::InReport prev_in_report_{0};
|
||||
PSClassic::InReport in_report_{0};
|
||||
|
||||
inline bool meets_pos_threshold(int16_t joy_l, int16_t joy_r) { return (joy_l >= JOY_POS_THRESHOLD) || (joy_r >= JOY_POS_THRESHOLD); }
|
||||
inline bool meets_neg_threshold(int16_t joy_l, int16_t joy_r) { return (joy_l <= JOY_NEG_THRESHOLD) || (joy_r <= JOY_NEG_THRESHOLD); }
|
||||
inline bool meets_pos_45_threshold(int16_t joy_l, int16_t joy_r) { return (joy_l >= JOY_POS_45_THRESHOLD) || (joy_r >= JOY_POS_45_THRESHOLD); }
|
||||
inline bool meets_neg_45_threshold(int16_t joy_l, int16_t joy_r) { return (joy_l <= JOY_NEG_45_THRESHOLD) || (joy_r <= JOY_NEG_45_THRESHOLD); }
|
||||
};
|
||||
|
||||
#endif // _D_PSCLASSIC_DRIVER_H_
|
||||
136
Firmware/RP2040/src/USBDevice/DeviceDriver/Switch/Switch.cpp
Normal file
136
Firmware/RP2040/src/USBDevice/DeviceDriver/Switch/Switch.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "USBDevice/DeviceDriver/Switch/Switch.h"
|
||||
|
||||
void SwitchDevice::initialize()
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
in_report_ = SwitchWired::InReport();
|
||||
for (auto& prev_in_report : prev_in_reports_)
|
||||
{
|
||||
prev_in_report = SwitchWired::InReport();
|
||||
}
|
||||
}
|
||||
|
||||
void SwitchDevice::process(const uint8_t idx, Gamepad& gamepad)
|
||||
{
|
||||
switch (gamepad.get_dpad_buttons())
|
||||
{
|
||||
case Gamepad::DPad::UP:
|
||||
in_report_.dpad = SwitchWired::DPad::UP;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN:
|
||||
in_report_.dpad = SwitchWired::DPad::DOWN;
|
||||
break;
|
||||
case Gamepad::DPad::LEFT:
|
||||
in_report_.dpad = SwitchWired::DPad::LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::RIGHT:
|
||||
in_report_.dpad = SwitchWired::DPad::RIGHT;
|
||||
break;
|
||||
case Gamepad::DPad::UP_LEFT:
|
||||
in_report_.dpad = SwitchWired::DPad::UP_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::UP_RIGHT:
|
||||
in_report_.dpad = SwitchWired::DPad::UP_RIGHT;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN_LEFT:
|
||||
in_report_.dpad = SwitchWired::DPad::DOWN_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN_RIGHT:
|
||||
in_report_.dpad = SwitchWired::DPad::DOWN_RIGHT;
|
||||
break;
|
||||
default:
|
||||
in_report_.dpad = SwitchWired::DPad::CENTER;
|
||||
break;
|
||||
}
|
||||
|
||||
in_report_.buttons = 0;
|
||||
|
||||
uint16_t gp_buttons = gamepad.get_buttons();
|
||||
|
||||
if (gp_buttons & Gamepad::Button::X) in_report_.buttons |= SwitchWired::Buttons::Y;
|
||||
if (gp_buttons & Gamepad::Button::A) in_report_.buttons |= SwitchWired::Buttons::B;
|
||||
if (gp_buttons & Gamepad::Button::Y) in_report_.buttons |= SwitchWired::Buttons::X;
|
||||
if (gp_buttons & Gamepad::Button::B) in_report_.buttons |= SwitchWired::Buttons::A;
|
||||
if (gp_buttons & Gamepad::Button::LB) in_report_.buttons |= SwitchWired::Buttons::L;
|
||||
if (gp_buttons & Gamepad::Button::RB) in_report_.buttons |= SwitchWired::Buttons::R;
|
||||
if (gp_buttons & Gamepad::Button::BACK) in_report_.buttons |= SwitchWired::Buttons::MINUS;
|
||||
if (gp_buttons & Gamepad::Button::START) in_report_.buttons |= SwitchWired::Buttons::PLUS;
|
||||
if (gp_buttons & Gamepad::Button::L3) in_report_.buttons |= SwitchWired::Buttons::L3;
|
||||
if (gp_buttons & Gamepad::Button::R3) in_report_.buttons |= SwitchWired::Buttons::R3;
|
||||
if (gp_buttons & Gamepad::Button::SYS) in_report_.buttons |= SwitchWired::Buttons::HOME;
|
||||
if (gp_buttons & Gamepad::Button::MISC) in_report_.buttons |= SwitchWired::Buttons::CAPTURE;
|
||||
|
||||
if (gamepad.get_trigger_l().uint8()) in_report_.buttons |= SwitchWired::Buttons::ZL;
|
||||
if (gamepad.get_trigger_r().uint8()) in_report_.buttons |= SwitchWired::Buttons::ZR;
|
||||
|
||||
in_report_.joystick_lx = gamepad.get_joystick_lx().uint8();
|
||||
in_report_.joystick_ly = gamepad.get_joystick_ly().uint8();
|
||||
in_report_.joystick_rx = gamepad.get_joystick_rx().uint8();
|
||||
in_report_.joystick_ry = gamepad.get_joystick_ry().uint8();
|
||||
|
||||
if (tud_suspended())
|
||||
{
|
||||
tud_remote_wakeup();
|
||||
}
|
||||
|
||||
if (std::memcmp(&prev_in_reports_[idx], &in_report_, sizeof(SwitchWired::InReport)) != 0 &&
|
||||
tud_hid_n_ready(idx) &&
|
||||
tud_hid_n_report(idx, 0, reinterpret_cast<uint8_t*>(&in_report_), sizeof(SwitchWired::InReport)))
|
||||
{
|
||||
std::memcpy(&prev_in_reports_[idx], &in_report_, sizeof(SwitchWired::InReport));
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t SwitchDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
// std::memcpy(buffer, &in_report_, sizeof(SwitchWired::InReport));
|
||||
// return sizeof(SwitchWired::InReport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SwitchDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
|
||||
|
||||
bool SwitchDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t* SwitchDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
const char *value = reinterpret_cast<const char*>(SwitchWired::STRING_DESCRIPTORS[index]);
|
||||
return get_string_descriptor(value, index);
|
||||
}
|
||||
|
||||
const uint8_t* SwitchDevice::get_descriptor_device_cb()
|
||||
{
|
||||
return SwitchWired::DEVICE_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* SwitchDevice::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return SwitchWired::REPORT_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* SwitchDevice::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return SwitchWired::CONFIGURATION_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* SwitchDevice::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
29
Firmware/RP2040/src/USBDevice/DeviceDriver/Switch/Switch.h
Normal file
29
Firmware/RP2040/src/USBDevice/DeviceDriver/Switch/Switch.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef _SWITCH_DEVICE_H_
|
||||
#define _SWITCH_DEVICE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "USBDevice/DeviceDriver/DeviceDriver.h"
|
||||
#include "Descriptors/SwitchWired.h"
|
||||
|
||||
class SwitchDevice : public DeviceDriver
|
||||
{
|
||||
public:
|
||||
void initialize() override;
|
||||
void process(const uint8_t idx, Gamepad& gamepad) override;
|
||||
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
|
||||
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
|
||||
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
|
||||
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
|
||||
const uint8_t* get_descriptor_device_cb() override;
|
||||
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
|
||||
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
|
||||
const uint8_t* get_descriptor_device_qualifier_cb() override;
|
||||
|
||||
private:
|
||||
SwitchWired::InReport in_report_;
|
||||
std::array<SwitchWired::InReport, MAX_GAMEPADS> prev_in_reports_;
|
||||
};
|
||||
|
||||
#endif // _SWITCH_DEVICE_H_
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "USBDevice/DeviceDriver/UARTBridge/UARTBridge.h"
|
||||
#include "USBDevice/DeviceDriver/UARTBridge/uart_bridge/uart_bridge.h"
|
||||
|
||||
void UARTBridgeDevice::initialize()
|
||||
{
|
||||
class_driver_ = {
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "UART",
|
||||
#endif
|
||||
.init = cdcd_init,
|
||||
.reset = cdcd_reset,
|
||||
.open = cdcd_open,
|
||||
.control_xfer_cb = cdcd_control_xfer_cb,
|
||||
.xfer_cb = cdcd_xfer_cb,
|
||||
.sof = NULL
|
||||
};
|
||||
}
|
||||
|
||||
void UARTBridgeDevice::process(const uint8_t idx, Gamepad& gamepad)
|
||||
{
|
||||
if (!uart_initialized_)
|
||||
{
|
||||
uart_initialized_ = true;
|
||||
uart_bridge_run();
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t UARTBridgeDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
return sizeof(buffer);
|
||||
}
|
||||
|
||||
void UARTBridgeDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool UARTBridgeDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t * UARTBridgeDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
return uart_bridge_descriptor_string_cb(index, langid);
|
||||
}
|
||||
|
||||
const uint8_t * UARTBridgeDevice::get_descriptor_device_cb()
|
||||
{
|
||||
return uart_bridge_descriptor_device_cb();
|
||||
}
|
||||
|
||||
const uint8_t * UARTBridgeDevice::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t * UARTBridgeDevice::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return uart_bridge_descriptor_configuration_cb(index);
|
||||
}
|
||||
|
||||
const uint8_t * UARTBridgeDevice::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef _UART_BRIDGE_DEVICE_H_
|
||||
#define _UART_BRIDGE_DEVICE_H_
|
||||
|
||||
#include "USBDevice/DeviceDriver/DeviceDriver.h"
|
||||
#include "UserSettings/UserProfile.h"
|
||||
|
||||
//process() only needs to be called once to start the UART bridge
|
||||
class UARTBridgeDevice : public DeviceDriver
|
||||
{
|
||||
public:
|
||||
void initialize() override;
|
||||
void process(const uint8_t idx, Gamepad& gamepad) override;
|
||||
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
|
||||
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
|
||||
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
|
||||
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
|
||||
const uint8_t* get_descriptor_device_cb() override;
|
||||
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
|
||||
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
|
||||
const uint8_t* get_descriptor_device_qualifier_cb() override;
|
||||
|
||||
private:
|
||||
bool uart_initialized_ = false;
|
||||
};
|
||||
|
||||
#endif // _UART_BRIDGE_DEVICE_H_
|
||||
@@ -0,0 +1,456 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright (c) 2021 Álvaro Fernández Rojas <noltari@gmail.com>
|
||||
*
|
||||
* This file is based on a file originally part of the
|
||||
* MicroPython project, http://micropython.org/
|
||||
*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
* Copyright (c) 2019 Damien P. George
|
||||
*/
|
||||
|
||||
#include <hardware/irq.h>
|
||||
#include <hardware/structs/sio.h>
|
||||
#include <hardware/uart.h>
|
||||
#include <hardware/flash.h>
|
||||
#include <pico/multicore.h>
|
||||
#include <pico/stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#if !defined(MIN)
|
||||
#define MIN(a, b) ((a > b) ? b : a)
|
||||
#endif /* MIN */
|
||||
|
||||
#define BUFFER_SIZE 2560
|
||||
|
||||
#define DEF_BIT_RATE 115200
|
||||
#define DEF_STOP_BITS 1
|
||||
#define DEF_PARITY 0
|
||||
#define DEF_DATA_BITS 8
|
||||
|
||||
typedef struct {
|
||||
uart_inst_t *const inst;
|
||||
uint irq;
|
||||
void *irq_fn;
|
||||
uint8_t tx_pin;
|
||||
uint8_t rx_pin;
|
||||
} uart_id_t;
|
||||
|
||||
typedef struct {
|
||||
cdc_line_coding_t usb_lc;
|
||||
cdc_line_coding_t uart_lc;
|
||||
mutex_t lc_mtx;
|
||||
uint8_t uart_buffer[BUFFER_SIZE];
|
||||
uint32_t uart_pos;
|
||||
mutex_t uart_mtx;
|
||||
uint8_t usb_buffer[BUFFER_SIZE];
|
||||
uint32_t usb_pos;
|
||||
mutex_t usb_mtx;
|
||||
} uart_data_t;
|
||||
|
||||
void uart0_irq_fn(void);
|
||||
void uart1_irq_fn(void);
|
||||
|
||||
const uart_id_t UART_ID[CFG_TUD_CDC] = {
|
||||
{
|
||||
.inst = uart0,
|
||||
.irq = UART0_IRQ,
|
||||
.irq_fn = &uart0_irq_fn,
|
||||
.tx_pin = UART0_TX_PIN,
|
||||
.rx_pin = UART0_RX_PIN,
|
||||
},
|
||||
#if CFG_TUD_CDC > 1
|
||||
{
|
||||
.inst = uart1,
|
||||
.irq = UART1_IRQ,
|
||||
.irq_fn = &uart1_irq_fn,
|
||||
.tx_pin = 4,
|
||||
.rx_pin = 5,
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
uart_data_t UART_DATA[CFG_TUD_CDC];
|
||||
|
||||
static inline uint databits_usb2uart(uint8_t data_bits)
|
||||
{
|
||||
switch (data_bits) {
|
||||
case 5:
|
||||
return 5;
|
||||
case 6:
|
||||
return 6;
|
||||
case 7:
|
||||
return 7;
|
||||
default:
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uart_parity_t parity_usb2uart(uint8_t usb_parity)
|
||||
{
|
||||
switch (usb_parity) {
|
||||
case 1:
|
||||
return UART_PARITY_ODD;
|
||||
case 2:
|
||||
return UART_PARITY_EVEN;
|
||||
default:
|
||||
return UART_PARITY_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint stopbits_usb2uart(uint8_t stop_bits)
|
||||
{
|
||||
switch (stop_bits) {
|
||||
case 2:
|
||||
return 2;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void update_uart_cfg(uint8_t itf)
|
||||
{
|
||||
const uart_id_t *ui = &UART_ID[itf];
|
||||
uart_data_t *ud = &UART_DATA[itf];
|
||||
|
||||
mutex_enter_blocking(&ud->lc_mtx);
|
||||
|
||||
if (ud->usb_lc.bit_rate != ud->uart_lc.bit_rate) {
|
||||
uart_set_baudrate(ui->inst, ud->usb_lc.bit_rate);
|
||||
ud->uart_lc.bit_rate = ud->usb_lc.bit_rate;
|
||||
}
|
||||
|
||||
if ((ud->usb_lc.stop_bits != ud->uart_lc.stop_bits) ||
|
||||
(ud->usb_lc.parity != ud->uart_lc.parity) ||
|
||||
(ud->usb_lc.data_bits != ud->uart_lc.data_bits)) {
|
||||
uart_set_format(ui->inst,
|
||||
databits_usb2uart(ud->usb_lc.data_bits),
|
||||
stopbits_usb2uart(ud->usb_lc.stop_bits),
|
||||
parity_usb2uart(ud->usb_lc.parity));
|
||||
ud->uart_lc.data_bits = ud->usb_lc.data_bits;
|
||||
ud->uart_lc.parity = ud->usb_lc.parity;
|
||||
ud->uart_lc.stop_bits = ud->usb_lc.stop_bits;
|
||||
}
|
||||
|
||||
mutex_exit(&ud->lc_mtx);
|
||||
}
|
||||
|
||||
void usb_read_bytes(uint8_t itf)
|
||||
{
|
||||
uart_data_t *ud = &UART_DATA[itf];
|
||||
uint32_t len = tud_cdc_n_available(itf);
|
||||
|
||||
if (len &&
|
||||
mutex_try_enter(&ud->usb_mtx, NULL)) {
|
||||
len = MIN(len, BUFFER_SIZE - ud->usb_pos);
|
||||
if (len) {
|
||||
uint32_t count;
|
||||
|
||||
count = tud_cdc_n_read(itf, &ud->usb_buffer[ud->usb_pos], len);
|
||||
ud->usb_pos += count;
|
||||
}
|
||||
|
||||
mutex_exit(&ud->usb_mtx);
|
||||
}
|
||||
}
|
||||
|
||||
void usb_write_bytes(uint8_t itf)
|
||||
{
|
||||
uart_data_t *ud = &UART_DATA[itf];
|
||||
|
||||
if (ud->uart_pos &&
|
||||
mutex_try_enter(&ud->uart_mtx, NULL)) {
|
||||
uint32_t count;
|
||||
|
||||
count = tud_cdc_n_write(itf, ud->uart_buffer, ud->uart_pos);
|
||||
if (count < ud->uart_pos)
|
||||
memmove(ud->uart_buffer, &ud->uart_buffer[count],
|
||||
ud->uart_pos - count);
|
||||
ud->uart_pos -= count;
|
||||
|
||||
mutex_exit(&ud->uart_mtx);
|
||||
|
||||
if (count)
|
||||
tud_cdc_n_write_flush(itf);
|
||||
}
|
||||
}
|
||||
|
||||
void usb_cdc_process(uint8_t itf)
|
||||
{
|
||||
uart_data_t *ud = &UART_DATA[itf];
|
||||
|
||||
mutex_enter_blocking(&ud->lc_mtx);
|
||||
tud_cdc_n_get_line_coding(itf, &ud->usb_lc);
|
||||
mutex_exit(&ud->lc_mtx);
|
||||
|
||||
usb_read_bytes(itf);
|
||||
usb_write_bytes(itf);
|
||||
}
|
||||
|
||||
static inline void uart_read_bytes(uint8_t itf)
|
||||
{
|
||||
uart_data_t *ud = &UART_DATA[itf];
|
||||
const uart_id_t *ui = &UART_ID[itf];
|
||||
|
||||
if (uart_is_readable(ui->inst)) {
|
||||
mutex_enter_blocking(&ud->uart_mtx);
|
||||
|
||||
while (uart_is_readable(ui->inst) &&
|
||||
(ud->uart_pos < BUFFER_SIZE)) {
|
||||
ud->uart_buffer[ud->uart_pos] = uart_getc(ui->inst);
|
||||
ud->uart_pos++;
|
||||
}
|
||||
|
||||
mutex_exit(&ud->uart_mtx);
|
||||
}
|
||||
}
|
||||
|
||||
void uart0_irq_fn(void)
|
||||
{
|
||||
uart_read_bytes(0);
|
||||
}
|
||||
|
||||
void uart1_irq_fn(void)
|
||||
{
|
||||
uart_read_bytes(1);
|
||||
}
|
||||
|
||||
void uart_write_bytes(uint8_t itf)
|
||||
{
|
||||
uart_data_t *ud = &UART_DATA[itf];
|
||||
|
||||
if (ud->usb_pos &&
|
||||
mutex_try_enter(&ud->usb_mtx, NULL)) {
|
||||
const uart_id_t *ui = &UART_ID[itf];
|
||||
uint32_t count = 0;
|
||||
|
||||
while (uart_is_writable(ui->inst) &&
|
||||
count < ud->usb_pos) {
|
||||
uart_putc_raw(ui->inst, ud->usb_buffer[count]);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count < ud->usb_pos)
|
||||
memmove(ud->usb_buffer, &ud->usb_buffer[count],
|
||||
ud->usb_pos - count);
|
||||
ud->usb_pos -= count;
|
||||
|
||||
mutex_exit(&ud->usb_mtx);
|
||||
}
|
||||
}
|
||||
|
||||
void init_uart_data(uint8_t itf)
|
||||
{
|
||||
const uart_id_t *ui = &UART_ID[itf];
|
||||
uart_data_t *ud = &UART_DATA[itf];
|
||||
|
||||
/* Pinmux */
|
||||
gpio_set_function(ui->tx_pin, GPIO_FUNC_UART);
|
||||
gpio_set_function(ui->rx_pin, GPIO_FUNC_UART);
|
||||
|
||||
/* USB CDC LC */
|
||||
ud->usb_lc.bit_rate = DEF_BIT_RATE;
|
||||
ud->usb_lc.data_bits = DEF_DATA_BITS;
|
||||
ud->usb_lc.parity = DEF_PARITY;
|
||||
ud->usb_lc.stop_bits = DEF_STOP_BITS;
|
||||
|
||||
/* UART LC */
|
||||
ud->uart_lc.bit_rate = DEF_BIT_RATE;
|
||||
ud->uart_lc.data_bits = DEF_DATA_BITS;
|
||||
ud->uart_lc.parity = DEF_PARITY;
|
||||
ud->uart_lc.stop_bits = DEF_STOP_BITS;
|
||||
|
||||
/* Buffer */
|
||||
ud->uart_pos = 0;
|
||||
ud->usb_pos = 0;
|
||||
|
||||
/* Mutex */
|
||||
mutex_init(&ud->lc_mtx);
|
||||
mutex_init(&ud->uart_mtx);
|
||||
mutex_init(&ud->usb_mtx);
|
||||
|
||||
/* UART start */
|
||||
uart_init(ui->inst, ud->usb_lc.bit_rate);
|
||||
uart_set_hw_flow(ui->inst, false, false);
|
||||
uart_set_format(ui->inst, databits_usb2uart(ud->usb_lc.data_bits),
|
||||
stopbits_usb2uart(ud->usb_lc.stop_bits),
|
||||
parity_usb2uart(ud->usb_lc.parity));
|
||||
uart_set_fifo_enabled(ui->inst, false);
|
||||
|
||||
/* UART RX Interrupt */
|
||||
irq_set_exclusive_handler(ui->irq, ui->irq_fn);
|
||||
irq_set_enabled(ui->irq, true);
|
||||
uart_set_irq_enables(ui->inst, true, false);
|
||||
}
|
||||
|
||||
void core1_entry(void)
|
||||
{
|
||||
for (int itf = 0; itf < CFG_TUD_CDC; itf++)
|
||||
{
|
||||
init_uart_data(0);
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
for (int itf = 0; itf < CFG_TUD_CDC; itf++)
|
||||
{
|
||||
update_uart_cfg(itf);
|
||||
uart_write_bytes(itf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void usbd_serial_init(void);
|
||||
|
||||
int uart_bridge_run(void)
|
||||
{
|
||||
set_sys_clock_khz(125000, false);
|
||||
|
||||
usbd_serial_init();
|
||||
tud_init(BOARD_TUD_RHPORT);
|
||||
|
||||
multicore_reset_core1();
|
||||
multicore_launch_core1(core1_entry);
|
||||
|
||||
while (1)
|
||||
{
|
||||
tud_task();
|
||||
|
||||
for (int itf = 0; itf < CFG_TUD_CDC; itf++)
|
||||
{
|
||||
if (tud_cdc_n_connected(itf))
|
||||
{
|
||||
usb_cdc_process(itf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DESC_STR_MAX 20
|
||||
|
||||
#define USBD_VID 0x2E8A /* Raspberry Pi */
|
||||
#define USBD_PID 0x000A /* Raspberry Pi Pico SDK CDC */
|
||||
|
||||
#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + (TUD_CDC_DESC_LEN * CFG_TUD_CDC))
|
||||
#define USBD_MAX_POWER_MA 500
|
||||
|
||||
enum
|
||||
{
|
||||
USBD_ITF_CDC_0_1 = 0,
|
||||
USBD_ITF_CDC_0_2,
|
||||
#if CFG_TUD_CDC > 1
|
||||
USBD_ITF_CDC_1_1,
|
||||
USBD_ITF_CDC_1_2,
|
||||
#endif
|
||||
USBD_ITF_MAX,
|
||||
};
|
||||
|
||||
#define USBD_CDC_0_EP_CMD 0x81
|
||||
#define USBD_CDC_1_EP_CMD 0x83
|
||||
|
||||
#define USBD_CDC_0_EP_OUT 0x01
|
||||
#define USBD_CDC_1_EP_OUT 0x03
|
||||
|
||||
#define USBD_CDC_0_EP_IN 0x82
|
||||
#define USBD_CDC_1_EP_IN 0x84
|
||||
|
||||
#define USBD_CDC_CMD_MAX_SIZE 8
|
||||
#define USBD_CDC_IN_OUT_MAX_SIZE 64
|
||||
|
||||
#define USBD_STR_0 0x00
|
||||
#define USBD_STR_MANUF 0x01
|
||||
#define USBD_STR_PRODUCT 0x02
|
||||
#define USBD_STR_SERIAL 0x03
|
||||
#define USBD_STR_SERIAL_LEN 17
|
||||
#define USBD_STR_CDC 0x04
|
||||
|
||||
static const tusb_desc_device_t usbd_desc_device =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.idVendor = USBD_VID,
|
||||
.idProduct = USBD_PID,
|
||||
.bcdDevice = 0x0100,
|
||||
.iManufacturer = USBD_STR_MANUF,
|
||||
.iProduct = USBD_STR_PRODUCT,
|
||||
.iSerialNumber = USBD_STR_SERIAL,
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = {
|
||||
TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN,
|
||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA),
|
||||
|
||||
TUD_CDC_DESCRIPTOR(USBD_ITF_CDC_0_1, USBD_STR_CDC, USBD_CDC_0_EP_CMD,
|
||||
USBD_CDC_CMD_MAX_SIZE, USBD_CDC_0_EP_OUT, USBD_CDC_0_EP_IN,
|
||||
USBD_CDC_IN_OUT_MAX_SIZE),
|
||||
|
||||
#if CFG_TUD_CDC > 1
|
||||
TUD_CDC_DESCRIPTOR(USBD_ITF_CDC_1_1, USBD_STR_CDC, USBD_CDC_1_EP_CMD,
|
||||
USBD_CDC_CMD_MAX_SIZE, USBD_CDC_1_EP_OUT, USBD_CDC_1_EP_IN,
|
||||
USBD_CDC_IN_OUT_MAX_SIZE),
|
||||
#endif
|
||||
};
|
||||
|
||||
static char usbd_serial[USBD_STR_SERIAL_LEN] = "000000000000";
|
||||
|
||||
static const char *const usbd_desc_str[] = {
|
||||
[USBD_STR_MANUF] = "Raspberry Pi",
|
||||
[USBD_STR_PRODUCT] = "Pico",
|
||||
[USBD_STR_SERIAL] = usbd_serial,
|
||||
[USBD_STR_CDC] = "Board CDC",
|
||||
};
|
||||
|
||||
const uint8_t *uart_bridge_descriptor_device_cb(void)
|
||||
{
|
||||
return (const uint8_t *) &usbd_desc_device;
|
||||
}
|
||||
|
||||
const uint8_t *uart_bridge_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return usbd_desc_cfg;
|
||||
}
|
||||
|
||||
const uint16_t *uart_bridge_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
static uint16_t desc_str[DESC_STR_MAX];
|
||||
uint8_t len;
|
||||
|
||||
if (index == 0) {
|
||||
desc_str[1] = 0x0409;
|
||||
len = 1;
|
||||
} else {
|
||||
const char *str;
|
||||
char serial[USBD_STR_SERIAL_LEN];
|
||||
|
||||
if (index >= sizeof(usbd_desc_str) / sizeof(usbd_desc_str[0]))
|
||||
return NULL;
|
||||
|
||||
str = usbd_desc_str[index];
|
||||
for (len = 0; len < DESC_STR_MAX - 1 && str[len]; ++len)
|
||||
desc_str[1 + len] = str[len];
|
||||
}
|
||||
|
||||
desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * len + 2);
|
||||
|
||||
return desc_str;
|
||||
}
|
||||
|
||||
void usbd_serial_init(void)
|
||||
{
|
||||
uint8_t id[8];
|
||||
|
||||
flash_get_unique_id(id);
|
||||
|
||||
snprintf(usbd_serial, USBD_STR_SERIAL_LEN, "%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7]);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef _UART_BRIDGE_H_
|
||||
#define _UART_BRIDGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int uart_bridge_run(void);
|
||||
const uint8_t *uart_bridge_descriptor_device_cb(void);
|
||||
const uint8_t *uart_bridge_descriptor_configuration_cb(uint8_t index);
|
||||
const uint16_t *uart_bridge_descriptor_string_cb(uint8_t index, uint16_t langid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _UART_BRIDGE_H_
|
||||
155
Firmware/RP2040/src/USBDevice/DeviceDriver/WebApp/WebApp.cpp
Normal file
155
Firmware/RP2040/src/USBDevice/DeviceDriver/WebApp/WebApp.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "class/cdc/cdc_device.h"
|
||||
#include "bsp/board_api.h"
|
||||
|
||||
#include "Descriptors/CDCDev.h"
|
||||
#include "USBDevice/DeviceDriver/WebApp/WebApp.h"
|
||||
|
||||
void WebAppDevice::initialize()
|
||||
{
|
||||
class_driver_ =
|
||||
{
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "WEBAPP",
|
||||
#endif
|
||||
.init = cdcd_init,
|
||||
.reset = cdcd_reset,
|
||||
.open = cdcd_open,
|
||||
.control_xfer_cb = cdcd_control_xfer_cb,
|
||||
.xfer_cb = cdcd_xfer_cb,
|
||||
.sof = NULL
|
||||
};
|
||||
}
|
||||
|
||||
void WebAppDevice::process(const uint8_t idx, Gamepad& gamepad)
|
||||
{
|
||||
if (!tud_cdc_available() || !tud_cdc_connected())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tud_cdc_write_flush();
|
||||
tud_cdc_read(reinterpret_cast<void*>(&in_report_), sizeof(Report));
|
||||
tud_cdc_read_flush();
|
||||
|
||||
bool success = false;
|
||||
|
||||
switch (in_report_.report_id)
|
||||
{
|
||||
case ReportID::INIT_READ:
|
||||
in_report_.input_mode = static_cast<uint8_t>(driver_type_);
|
||||
in_report_.player_idx = 0;
|
||||
in_report_.report_id = ReportID::RESP_OK;
|
||||
in_report_.max_gamepads = MAX_GAMEPADS;
|
||||
|
||||
in_report_.profile.id = user_settings_.get_active_profile_id(in_report_.player_idx);
|
||||
in_report_.profile = user_settings_.get_profile_by_id(in_report_.profile.id);
|
||||
|
||||
tud_cdc_write(reinterpret_cast<const void*>(&in_report_), sizeof(Report));
|
||||
tud_cdc_write_flush();
|
||||
break;
|
||||
|
||||
case ReportID::READ_PROFILE:
|
||||
in_report_.input_mode = static_cast<uint8_t>(driver_type_);
|
||||
in_report_.profile = user_settings_.get_profile_by_id(in_report_.profile.id);
|
||||
in_report_.report_id = ReportID::RESP_OK;
|
||||
|
||||
tud_cdc_write(reinterpret_cast<const void*>(&in_report_), sizeof(Report));
|
||||
tud_cdc_write_flush();
|
||||
break;
|
||||
|
||||
case ReportID::WRITE_PROFILE:
|
||||
if (in_report_.input_mode != static_cast<uint8_t>(driver_type_) && in_report_.input_mode != 0)
|
||||
{
|
||||
success = user_settings_.store_profile_and_driver_type_safe(static_cast<DeviceDriver::Type>(in_report_.input_mode), in_report_.player_idx, in_report_.profile);
|
||||
}
|
||||
else
|
||||
{
|
||||
success = user_settings_.store_profile_safe(in_report_.player_idx, in_report_.profile);
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
in_report_.report_id = ReportID::RESP_ERROR;
|
||||
tud_cdc_write(reinterpret_cast<const void*>(&in_report_), sizeof(Report));
|
||||
tud_cdc_write_flush();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t WebAppDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
return reqlen;
|
||||
}
|
||||
|
||||
void WebAppDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
|
||||
|
||||
bool WebAppDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t * WebAppDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
size_t chr_count;
|
||||
|
||||
switch ( index )
|
||||
{
|
||||
case CDCDesc::STRID_LANGID:
|
||||
std::memcpy(&CDCDesc::_desc_str[1], CDCDesc::STRING_DESCRIPTORS[0], 2);
|
||||
chr_count = 1;
|
||||
break;
|
||||
|
||||
case CDCDesc::STRID_SERIAL:
|
||||
chr_count = board_usb_get_serial(CDCDesc::_desc_str + 1, 32);
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( !(index < sizeof(CDCDesc::STRING_DESCRIPTORS) / sizeof(CDCDesc::STRING_DESCRIPTORS[0])) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *str = CDCDesc::STRING_DESCRIPTORS[index];
|
||||
|
||||
chr_count = strlen(str);
|
||||
size_t const max_count = sizeof(CDCDesc::_desc_str) / sizeof(CDCDesc::_desc_str[0]) - 1;
|
||||
|
||||
if ( chr_count > max_count )
|
||||
{
|
||||
chr_count = max_count;
|
||||
}
|
||||
|
||||
for ( size_t i = 0; i < chr_count; i++ )
|
||||
{
|
||||
CDCDesc::_desc_str[1 + i] = str[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
CDCDesc::_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
|
||||
|
||||
return CDCDesc::_desc_str;
|
||||
}
|
||||
|
||||
const uint8_t * WebAppDevice::get_descriptor_device_cb()
|
||||
{
|
||||
return reinterpret_cast<const uint8_t*>(&CDCDesc::DEVICE_DESCRIPTORS);
|
||||
}
|
||||
|
||||
const uint8_t * WebAppDevice::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t * WebAppDevice::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return CDCDesc::CONFIGURATION_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t * WebAppDevice::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
49
Firmware/RP2040/src/USBDevice/DeviceDriver/WebApp/WebApp.h
Normal file
49
Firmware/RP2040/src/USBDevice/DeviceDriver/WebApp/WebApp.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef _WEBAAPP_DEVICE_H_
|
||||
#define _WEBAAPP_DEVICE_H_
|
||||
|
||||
#include "USBDevice/DeviceDriver/DeviceDriver.h"
|
||||
#include "UserSettings/UserSettings.h"
|
||||
#include "UserSettings/UserProfile.h"
|
||||
|
||||
class WebAppDevice : public DeviceDriver
|
||||
{
|
||||
public:
|
||||
void initialize() override;
|
||||
void process(const uint8_t idx, Gamepad& gamepad) override;
|
||||
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
|
||||
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
|
||||
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
|
||||
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
|
||||
const uint8_t* get_descriptor_device_cb() override;
|
||||
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
|
||||
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
|
||||
const uint8_t* get_descriptor_device_qualifier_cb() override;
|
||||
|
||||
private:
|
||||
struct ReportID
|
||||
{
|
||||
static constexpr uint8_t INIT_READ = 0x88;
|
||||
static constexpr uint8_t READ_PROFILE = 0x01;
|
||||
static constexpr uint8_t WRITE_PROFILE = 0x02;
|
||||
static constexpr uint8_t RESP_OK = 0x10;
|
||||
static constexpr uint8_t RESP_ERROR = 0x11;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct Report
|
||||
{
|
||||
uint8_t report_id{0};
|
||||
uint8_t input_mode{0};
|
||||
uint8_t max_gamepads{MAX_GAMEPADS};
|
||||
uint8_t player_idx{0};
|
||||
UserProfile profile{UserProfile()};
|
||||
};
|
||||
static_assert(sizeof(Report) == 50, "WebApp report size mismatch");
|
||||
#pragma pack(pop)
|
||||
|
||||
UserSettings user_settings_{UserSettings()};
|
||||
Report in_report_{Report()};
|
||||
DeviceDriver::Type driver_type_{DeviceDriver::Type::WEBAPP};
|
||||
};
|
||||
|
||||
#endif // _WEBAAPP_DEVICE_H_
|
||||
123
Firmware/RP2040/src/USBDevice/DeviceDriver/XInput/XInput.cpp
Normal file
123
Firmware/RP2040/src/USBDevice/DeviceDriver/XInput/XInput.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "USBDevice/DeviceDriver/XInput/tud_xinput/tud_xinput.h"
|
||||
#include "USBDevice/DeviceDriver/XInput/XInput.h"
|
||||
|
||||
void XInputDevice::initialize()
|
||||
{
|
||||
class_driver_ = *tud_xinput::class_driver();
|
||||
std::memset(&in_report_, 0, sizeof(XInput::InReport));
|
||||
std::memset(&prev_in_report_, 0, sizeof(XInput::InReport));
|
||||
in_report_.report_size = XInput::ENDPOINT_IN_SIZE;
|
||||
}
|
||||
|
||||
void XInputDevice::process(const uint8_t idx, Gamepad& gamepad)
|
||||
{
|
||||
std::memset(&in_report_.buttons, 0, sizeof(in_report_.buttons));
|
||||
|
||||
switch (gamepad.get_dpad_buttons())
|
||||
{
|
||||
case Gamepad::DPad::UP:
|
||||
in_report_.buttons[0] = XInput::Buttons0::DPAD_UP;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN:
|
||||
in_report_.buttons[0] = XInput::Buttons0::DPAD_DOWN;
|
||||
break;
|
||||
case Gamepad::DPad::LEFT:
|
||||
in_report_.buttons[0] = XInput::Buttons0::DPAD_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::RIGHT:
|
||||
in_report_.buttons[0] = XInput::Buttons0::DPAD_RIGHT;
|
||||
break;
|
||||
case Gamepad::DPad::UP_LEFT:
|
||||
in_report_.buttons[0] = (XInput::Buttons0::DPAD_UP | XInput::Buttons0::DPAD_LEFT);
|
||||
break;
|
||||
case Gamepad::DPad::UP_RIGHT:
|
||||
in_report_.buttons[0] = XInput::Buttons0::DPAD_UP | XInput::Buttons0::DPAD_RIGHT;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN_LEFT:
|
||||
in_report_.buttons[0] = XInput::Buttons0::DPAD_DOWN | XInput::Buttons0::DPAD_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN_RIGHT:
|
||||
in_report_.buttons[0] = XInput::Buttons0::DPAD_DOWN | XInput::Buttons0::DPAD_RIGHT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t gp_buttons = gamepad.get_buttons();
|
||||
|
||||
if (gp_buttons & Gamepad::Button::BACK) in_report_.buttons[0] |= XInput::Buttons0::BACK;
|
||||
if (gp_buttons & Gamepad::Button::START) in_report_.buttons[0] |= XInput::Buttons0::START;
|
||||
if (gp_buttons & Gamepad::Button::L3) in_report_.buttons[0] |= XInput::Buttons0::L3;
|
||||
if (gp_buttons & Gamepad::Button::R3) in_report_.buttons[0] |= XInput::Buttons0::R3;
|
||||
|
||||
if (gp_buttons & Gamepad::Button::X) in_report_.buttons[1] |= XInput::Buttons1::X;
|
||||
if (gp_buttons & Gamepad::Button::A) in_report_.buttons[1] |= XInput::Buttons1::A;
|
||||
if (gp_buttons & Gamepad::Button::Y) in_report_.buttons[1] |= XInput::Buttons1::Y;
|
||||
if (gp_buttons & Gamepad::Button::B) in_report_.buttons[1] |= XInput::Buttons1::B;
|
||||
if (gp_buttons & Gamepad::Button::LB) in_report_.buttons[1] |= XInput::Buttons1::LB;
|
||||
if (gp_buttons & Gamepad::Button::RB) in_report_.buttons[1] |= XInput::Buttons1::RB;
|
||||
if (gp_buttons & Gamepad::Button::SYS) in_report_.buttons[1] |= XInput::Buttons1::HOME;
|
||||
|
||||
in_report_.trigger_l = gamepad.get_trigger_l().uint8();
|
||||
in_report_.trigger_r = gamepad.get_trigger_r().uint8();
|
||||
|
||||
in_report_.joystick_lx = gamepad.get_joystick_lx().int16();
|
||||
in_report_.joystick_ly = gamepad.get_joystick_ly().int16(true);
|
||||
in_report_.joystick_rx = gamepad.get_joystick_rx().int16();
|
||||
in_report_.joystick_ry = gamepad.get_joystick_ry().int16(true);
|
||||
|
||||
if (std::memcmp(&prev_in_report_, &in_report_, sizeof(in_report_)) != 0 &&
|
||||
tud_xinput::send_report(reinterpret_cast<uint8_t*>(&in_report_), sizeof(XInput::InReport)))
|
||||
{
|
||||
std::memcpy(&prev_in_report_, &in_report_, sizeof(XInput::InReport));
|
||||
}
|
||||
|
||||
if (tud_xinput::receive_report(reinterpret_cast<uint8_t*>(&out_report_), sizeof(XInput::OutReport)) &&
|
||||
out_report_.report_id == XInput::OutReportID::RUMBLE)
|
||||
{
|
||||
gamepad.set_rumble_l(out_report_.rumble_l);
|
||||
gamepad.set_rumble_r(out_report_.rumble_r);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t XInputDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
std::memcpy(buffer, &in_report_, sizeof(in_report_));
|
||||
return sizeof(XInput::InReport);
|
||||
}
|
||||
|
||||
void XInputDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
|
||||
|
||||
bool XInputDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t * XInputDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
const char *value = (const char *)XInput::STRING_DESCRIPTORS[index];
|
||||
return get_string_descriptor(value, index);
|
||||
}
|
||||
|
||||
const uint8_t * XInputDevice::get_descriptor_device_cb()
|
||||
{
|
||||
return XInput::DEVICE_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t * XInputDevice::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t * XInputDevice::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return XInput::CONFIGURATION_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t * XInputDevice::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
27
Firmware/RP2040/src/USBDevice/DeviceDriver/XInput/XInput.h
Normal file
27
Firmware/RP2040/src/USBDevice/DeviceDriver/XInput/XInput.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef _XINPUT_DEVICE_H_
|
||||
#define _XINPUT_DEVICE_H_
|
||||
|
||||
#include "USBDevice/DeviceDriver/DeviceDriver.h"
|
||||
#include "Descriptors/XInput.h"
|
||||
|
||||
class XInputDevice : public DeviceDriver
|
||||
{
|
||||
public:
|
||||
void initialize() override;
|
||||
void process(const uint8_t idx, Gamepad& gamepad) override;
|
||||
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
|
||||
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
|
||||
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
|
||||
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
|
||||
const uint8_t* get_descriptor_device_cb() override;
|
||||
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
|
||||
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
|
||||
const uint8_t* get_descriptor_device_qualifier_cb() override;
|
||||
|
||||
private:
|
||||
XInput::InReport in_report_;
|
||||
XInput::InReport prev_in_report_;
|
||||
XInput::OutReport out_report_;
|
||||
};
|
||||
|
||||
#endif // _XINPUT_DEVICE_H_
|
||||
@@ -0,0 +1,137 @@
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XINPUT)
|
||||
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "Descriptors/XInput.h"
|
||||
#include "USBDevice/DeviceDriver/XInput/tud_xinput/tud_xinput.h"
|
||||
|
||||
namespace tud_xinput {
|
||||
|
||||
static constexpr uint8_t ENDPOINT_SIZE = 32;
|
||||
|
||||
uint8_t endpoint_in_ = 0xFF;
|
||||
uint8_t endpoint_out_ = 0xFF;
|
||||
std::array<uint8_t, ENDPOINT_SIZE> out_buffer_;
|
||||
|
||||
//Class Driver
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
out_buffer_.fill(0);
|
||||
}
|
||||
|
||||
static void reset(uint8_t rhport)
|
||||
{
|
||||
endpoint_in_ = 0xFF;
|
||||
endpoint_out_ = 0xFF;
|
||||
init();
|
||||
}
|
||||
|
||||
static uint16_t open(uint8_t rhport, tusb_desc_interface_t const *itf_descriptor, uint16_t max_length)
|
||||
{
|
||||
uint16_t driver_length = static_cast<uint16_t>(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 = reinterpret_cast<const tusb_desc_endpoint_t*>(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 control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool xfer_callback(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
if (ep_addr == endpoint_out_)
|
||||
{
|
||||
usbd_edpt_xfer(0, endpoint_out_, out_buffer_.data(), ENDPOINT_SIZE);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const usbd_class_driver_t tud_class_driver_ =
|
||||
{
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "XINPUT",
|
||||
#endif
|
||||
.init = init,
|
||||
.reset = reset,
|
||||
.open = open,
|
||||
.control_xfer_cb = control_xfer_cb,
|
||||
.xfer_cb = xfer_callback,
|
||||
.sof = NULL
|
||||
};
|
||||
|
||||
// Public API
|
||||
|
||||
const usbd_class_driver_t* class_driver()
|
||||
{
|
||||
return &tud_class_driver_;
|
||||
}
|
||||
|
||||
bool send_report_ready()
|
||||
{
|
||||
return (tud_ready() && endpoint_in_ != 0xFF && !usbd_edpt_busy(BOARD_TUD_RHPORT, endpoint_in_));
|
||||
}
|
||||
|
||||
bool send_report(const uint8_t *report, uint16_t len)
|
||||
{
|
||||
if (tud_suspended())
|
||||
{
|
||||
tud_remote_wakeup();
|
||||
}
|
||||
|
||||
if (send_report_ready())
|
||||
{
|
||||
usbd_edpt_claim(BOARD_TUD_RHPORT, endpoint_in_);
|
||||
usbd_edpt_xfer(BOARD_TUD_RHPORT, endpoint_in_, const_cast<uint8_t*>(report), len);
|
||||
usbd_edpt_release(BOARD_TUD_RHPORT, endpoint_in_);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool receive_report(uint8_t *report, uint16_t len)
|
||||
{
|
||||
if (len > ENDPOINT_SIZE || endpoint_out_ == 0xFF)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::memcpy(report, out_buffer_.data(), len);
|
||||
usbd_edpt_xfer(BOARD_TUD_RHPORT, endpoint_out_, out_buffer_.data(), ENDPOINT_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace TUDXInput
|
||||
|
||||
#endif // (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XINPUT)
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef _TUD_XINPUT_H_
|
||||
#define _TUD_XINPUT_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
namespace tud_xinput
|
||||
{
|
||||
bool send_report_ready();
|
||||
bool send_report(const uint8_t *report, uint16_t len);
|
||||
bool receive_report(uint8_t *report, uint16_t len);
|
||||
const usbd_class_driver_t* class_driver();
|
||||
};
|
||||
|
||||
#endif // _TUD_XINPUT_H_
|
||||
136
Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.cpp
Normal file
136
Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.h"
|
||||
#include "USBDevice/DeviceDriver/XboxOG/XboxOG_GP.h"
|
||||
|
||||
void XboxOGDevice::initialize()
|
||||
{
|
||||
tud_xid::initialize(tud_xid::Type::GAMEPAD);
|
||||
class_driver_ = *tud_xid::class_driver();
|
||||
|
||||
std::memset(&in_report_, 0, sizeof(XboxOG::GP::InReport));
|
||||
in_report_.report_len = sizeof(XboxOG::GP::InReport);
|
||||
|
||||
std::memcpy(&prev_in_report_, &in_report_, sizeof(XboxOG::GP::InReport));
|
||||
}
|
||||
|
||||
void XboxOGDevice::process(const uint8_t idx, Gamepad& gamepad)
|
||||
{
|
||||
std::memset(&in_report_.buttons, 0, 8);
|
||||
|
||||
switch (gamepad.get_dpad_buttons())
|
||||
{
|
||||
case Gamepad::DPad::UP:
|
||||
in_report_.buttons = XboxOG::GP::Buttons::DPAD_UP;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN:
|
||||
in_report_.buttons = XboxOG::GP::Buttons::DPAD_DOWN;
|
||||
break;
|
||||
case Gamepad::DPad::LEFT:
|
||||
in_report_.buttons = XboxOG::GP::Buttons::DPAD_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::RIGHT:
|
||||
in_report_.buttons = XboxOG::GP::Buttons::DPAD_RIGHT;
|
||||
break;
|
||||
case Gamepad::DPad::UP_LEFT:
|
||||
in_report_.buttons = XboxOG::GP::Buttons::DPAD_UP | XboxOG::GP::Buttons::DPAD_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::UP_RIGHT:
|
||||
in_report_.buttons = XboxOG::GP::Buttons::DPAD_UP | XboxOG::GP::Buttons::DPAD_RIGHT;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN_LEFT:
|
||||
in_report_.buttons = XboxOG::GP::Buttons::DPAD_DOWN | XboxOG::GP::Buttons::DPAD_LEFT;
|
||||
break;
|
||||
case Gamepad::DPad::DOWN_RIGHT:
|
||||
in_report_.buttons = XboxOG::GP::Buttons::DPAD_DOWN | XboxOG::GP::Buttons::DPAD_RIGHT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t gp_buttons = gamepad.get_buttons();
|
||||
|
||||
if (gp_buttons & Gamepad::Button::BACK) in_report_.buttons |= XboxOG::GP::Buttons::BACK;
|
||||
if (gp_buttons & Gamepad::Button::START) in_report_.buttons |= XboxOG::GP::Buttons::START;
|
||||
if (gp_buttons & Gamepad::Button::L3) in_report_.buttons |= XboxOG::GP::Buttons::L3;
|
||||
if (gp_buttons & Gamepad::Button::R3) in_report_.buttons |= XboxOG::GP::Buttons::R3;
|
||||
|
||||
if (gamepad.analog_enabled())
|
||||
{
|
||||
in_report_.a = gamepad.get_analog_a();
|
||||
in_report_.b = gamepad.get_analog_b();
|
||||
in_report_.x = gamepad.get_analog_x();
|
||||
in_report_.y = gamepad.get_analog_y();
|
||||
in_report_.white = gamepad.get_analog_lb();
|
||||
in_report_.black = gamepad.get_analog_rb();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gp_buttons & Gamepad::Button::X) in_report_.x = 0xFF;
|
||||
if (gp_buttons & Gamepad::Button::A) in_report_.a = 0xFF;
|
||||
if (gp_buttons & Gamepad::Button::Y) in_report_.y = 0xFF;
|
||||
if (gp_buttons & Gamepad::Button::B) in_report_.b = 0xFF;
|
||||
if (gp_buttons & Gamepad::Button::LB) in_report_.white = 0xFF;
|
||||
if (gp_buttons & Gamepad::Button::RB) in_report_.black = 0xFF;
|
||||
}
|
||||
|
||||
in_report_.trigger_l = gamepad.get_trigger_l().uint8();
|
||||
in_report_.trigger_r = gamepad.get_trigger_r().uint8();
|
||||
|
||||
in_report_.joystick_lx = gamepad.get_joystick_lx().int16();
|
||||
in_report_.joystick_ly = gamepad.get_joystick_ly().int16(true);
|
||||
in_report_.joystick_rx = gamepad.get_joystick_rx().int16();
|
||||
in_report_.joystick_ry = gamepad.get_joystick_ry().int16(true);
|
||||
|
||||
if (std::memcmp(&prev_in_report_, &in_report_, sizeof(in_report_)) != 0 &&
|
||||
tud_xid::send_report(0, reinterpret_cast<uint8_t*>(&in_report_), sizeof(XboxOG::GP::InReport)))
|
||||
{
|
||||
std::memcpy(&prev_in_report_, &in_report_, sizeof(XboxOG::GP::InReport));
|
||||
}
|
||||
|
||||
if (tud_xid::receive_report(0, reinterpret_cast<uint8_t*>(&out_report_), sizeof(XboxOG::GP::OutReport)))
|
||||
{
|
||||
gamepad.set_rumble_l(out_report_.rumble_l);
|
||||
gamepad.set_rumble_r(out_report_.rumble_r);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t XboxOGDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
std::memcpy(buffer, &in_report_, sizeof(in_report_));
|
||||
return sizeof(XboxOG::GP::InReport);
|
||||
}
|
||||
|
||||
void XboxOGDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
|
||||
|
||||
bool XboxOGDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return tud_xid::class_driver()->control_xfer_cb(rhport, stage, request);
|
||||
}
|
||||
|
||||
const uint16_t* XboxOGDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
const char *value = reinterpret_cast<const char*>(XboxOG::GP::STRING_DESCRIPTORS[index]);
|
||||
return get_string_descriptor(value, index);
|
||||
}
|
||||
|
||||
const uint8_t* XboxOGDevice::get_descriptor_device_cb()
|
||||
{
|
||||
return reinterpret_cast<const uint8_t*>(&XboxOG::GP::DEVICE_DESCRIPTORS);
|
||||
}
|
||||
|
||||
const uint8_t* XboxOGDevice::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t* XboxOGDevice::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return XboxOG::GP::CONFIGURATION_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* XboxOGDevice::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef _XBOXGOG_DEVICE_H_
|
||||
#define _XBOXGOG_DEVICE_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "USBDevice/DeviceDriver/DeviceDriver.h"
|
||||
#include "Descriptors/XboxOG.h"
|
||||
|
||||
class XboxOGDevice : public DeviceDriver
|
||||
{
|
||||
public:
|
||||
void initialize() override;
|
||||
void process(const uint8_t idx, Gamepad& gamepad) override;
|
||||
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
|
||||
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
|
||||
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
|
||||
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
|
||||
const uint8_t* get_descriptor_device_cb() override;
|
||||
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
|
||||
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
|
||||
const uint8_t* get_descriptor_device_qualifier_cb() override;
|
||||
|
||||
private:
|
||||
XboxOG::GP::InReport in_report_;
|
||||
XboxOG::GP::InReport prev_in_report_;
|
||||
XboxOG::GP::OutReport out_report_;
|
||||
};
|
||||
|
||||
#endif // _XBOXGOG_DEVICE_H_
|
||||
368
Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_SB.cpp
Normal file
368
Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_SB.cpp
Normal file
@@ -0,0 +1,368 @@
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "pico/time.h"
|
||||
|
||||
#include "Descriptors/XInput.h"
|
||||
#include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.h"
|
||||
#include "USBDevice/DeviceDriver/XboxOG/XboxOG_SB.h"
|
||||
|
||||
static constexpr std::array<XboxOGSBDevice::ButtonMap, 9> GP_MAP =
|
||||
{{
|
||||
{Gamepad::Button::START, XboxOG::SB::Buttons0::START, 0},
|
||||
{Gamepad::Button::LB, XboxOG::SB::Buttons0::RIGHTJOYFIRE, 0},
|
||||
{Gamepad::Button::R3, XboxOG::SB::Buttons0::RIGHTJOYLOCKON, 0},
|
||||
{Gamepad::Button::B, XboxOG::SB::Buttons0::RIGHTJOYLOCKON, 0},
|
||||
{Gamepad::Button::RB, XboxOG::SB::Buttons0::RIGHTJOYMAINWEAPON, 0},
|
||||
{Gamepad::Button::A, XboxOG::SB::Buttons0::RIGHTJOYMAINWEAPON, 0},
|
||||
{Gamepad::Button::SYS, XboxOG::SB::Buttons0::EJECT, 0},
|
||||
{Gamepad::Button::L3, XboxOG::SB::Buttons2::LEFTJOYSIGHTCHANGE, 2},
|
||||
{Gamepad::Button::Y, XboxOG::SB::Buttons1::CHAFF, 1}
|
||||
}};
|
||||
|
||||
static constexpr std::array<XboxOGSBDevice::ButtonMap, 19> CHATPAD_MAP =
|
||||
{{
|
||||
{XInput::Chatpad::CODE_0, XboxOG::SB::Buttons0::EJECT, 0},
|
||||
{XInput::Chatpad::CODE_D, XboxOG::SB::Buttons1::WASHING, 1},
|
||||
{XInput::Chatpad::CODE_F, XboxOG::SB::Buttons1::EXTINGUISHER, 1},
|
||||
{XInput::Chatpad::CODE_G, XboxOG::SB::Buttons1::CHAFF, 1},
|
||||
{XInput::Chatpad::CODE_X, XboxOG::SB::Buttons1::WEAPONCONMAIN, 1},
|
||||
{XInput::Chatpad::CODE_RIGHT, XboxOG::SB::Buttons1::WEAPONCONMAIN, 1},
|
||||
{XInput::Chatpad::CODE_C, XboxOG::SB::Buttons1::WEAPONCONSUB, 1},
|
||||
{XInput::Chatpad::CODE_LEFT, XboxOG::SB::Buttons1::WEAPONCONSUB, 1},
|
||||
{XInput::Chatpad::CODE_V, XboxOG::SB::Buttons1::WEAPONCONMAGAZINE, 1},
|
||||
{XInput::Chatpad::CODE_SPACE, XboxOG::SB::Buttons1::WEAPONCONMAGAZINE, 1},
|
||||
{XInput::Chatpad::CODE_U, XboxOG::SB::Buttons0::MULTIMONOPENCLOSE, 0},
|
||||
{XInput::Chatpad::CODE_J, XboxOG::SB::Buttons0::MULTIMONMODESELECT, 0},
|
||||
{XInput::Chatpad::CODE_N, XboxOG::SB::Buttons0::MAINMONZOOMIN, 0},
|
||||
{XInput::Chatpad::CODE_I, XboxOG::SB::Buttons0::MULTIMONMAPZOOMINOUT, 0},
|
||||
{XInput::Chatpad::CODE_K, XboxOG::SB::Buttons0::MULTIMONSUBMONITOR, 0},
|
||||
{XInput::Chatpad::CODE_M, XboxOG::SB::Buttons0::MAINMONZOOMOUT, 0},
|
||||
{XInput::Chatpad::CODE_ENTER, XboxOG::SB::Buttons0::START, 0},
|
||||
{XInput::Chatpad::CODE_P, XboxOG::SB::Buttons0::COCKPITHATCH, 0},
|
||||
{XInput::Chatpad::CODE_COMMA, XboxOG::SB::Buttons0::IGNITION, 0}
|
||||
}};
|
||||
|
||||
static constexpr std::array<XboxOGSBDevice::ButtonMap, 5> CHATPAD_MAP_ALT1 =
|
||||
{{
|
||||
{XInput::Chatpad::CODE_1, XboxOG::SB::Buttons1::COMM1, 1},
|
||||
{XInput::Chatpad::CODE_2, XboxOG::SB::Buttons1::COMM2, 1},
|
||||
{XInput::Chatpad::CODE_3, XboxOG::SB::Buttons1::COMM3, 1},
|
||||
{XInput::Chatpad::CODE_4, XboxOG::SB::Buttons1::COMM4, 1},
|
||||
{XInput::Chatpad::CODE_5, XboxOG::SB::Buttons2::COMM5, 2}
|
||||
}};
|
||||
|
||||
static constexpr std::array<XboxOGSBDevice::ButtonMap, 9> CHATPAD_MAP_ALT2=
|
||||
{{
|
||||
{XInput::Chatpad::CODE_1, XboxOG::SB::Buttons1::FUNCTIONF1, 1},
|
||||
{XInput::Chatpad::CODE_2, XboxOG::SB::Buttons1::FUNCTIONTANKDETACH, 1},
|
||||
{XInput::Chatpad::CODE_3, XboxOG::SB::Buttons0::FUNCTIONFSS, 0},
|
||||
{XInput::Chatpad::CODE_4, XboxOG::SB::Buttons1::FUNCTIONF2, 1},
|
||||
{XInput::Chatpad::CODE_5, XboxOG::SB::Buttons1::FUNCTIONOVERRIDE, 1},
|
||||
{XInput::Chatpad::CODE_6, XboxOG::SB::Buttons0::FUNCTIONMANIPULATOR, 0},
|
||||
{XInput::Chatpad::CODE_7, XboxOG::SB::Buttons1::FUNCTIONF3, 1},
|
||||
{XInput::Chatpad::CODE_8, XboxOG::SB::Buttons1::FUNCTIONNIGHTSCOPE, 1},
|
||||
{XInput::Chatpad::CODE_9, XboxOG::SB::Buttons0::FUNCTIONLINECOLORCHANGE, 0}
|
||||
}};
|
||||
|
||||
static constexpr std::array<XboxOGSBDevice::ButtonMap, 5> CHATPAD_TOGGLE_MAP =
|
||||
{{
|
||||
{XInput::Chatpad::CODE_Q, XboxOG::SB::Buttons2::TOGGLEOXYGENSUPPLY, 2},
|
||||
{XInput::Chatpad::CODE_A, XboxOG::SB::Buttons2::TOGGLEFILTERCONTROL, 2},
|
||||
{XInput::Chatpad::CODE_W, XboxOG::SB::Buttons2::TOGGLEVTLOCATION, 2},
|
||||
{XInput::Chatpad::CODE_S, XboxOG::SB::Buttons2::TOGGLEBUFFREMATERIAL, 2},
|
||||
{XInput::Chatpad::CODE_Z, XboxOG::SB::Buttons2::TOGGLEFUELFLOWRATE, 2}
|
||||
}};
|
||||
|
||||
void XboxOGSBDevice::initialize()
|
||||
{
|
||||
tud_xid::initialize(tud_xid::Type::STEELBATTALION);
|
||||
class_driver_ = *tud_xid::class_driver();
|
||||
|
||||
std::memset(&in_report_, 0, sizeof(XboxOG::SB::InReport));
|
||||
in_report_.bLength = sizeof(XboxOG::SB::InReport);
|
||||
in_report_.gearLever = XboxOG::SB::Gear::N;
|
||||
|
||||
std::memcpy(&prev_in_report_, &in_report_, sizeof(XboxOG::SB::InReport));
|
||||
}
|
||||
|
||||
void XboxOGSBDevice::process(const uint8_t idx, Gamepad& gamepad)
|
||||
{
|
||||
// // uint8_t xid_index = tud_xid::get_index_by_type(0, tud_xid::Type::STEELBATTALION);
|
||||
|
||||
// // if (tud_xid::send_report_ready(xid_index))
|
||||
// // {
|
||||
in_report_.dButtons[0] = 0;
|
||||
in_report_.dButtons[1] = 0;
|
||||
in_report_.dButtons[2] &= XboxOG::SB::BUTTONS2_TOGGLE_MID;
|
||||
|
||||
uint16_t gp_buttons = gamepad.get_buttons();
|
||||
|
||||
for (const auto& map : GP_MAP)
|
||||
{
|
||||
if (gp_buttons & map.gp_mask)
|
||||
{
|
||||
in_report_.dButtons[map.button_offset] |= map.sb_mask;
|
||||
}
|
||||
}
|
||||
|
||||
Gamepad::Chatpad gp_chatpad = gamepad.get_chatpad();
|
||||
|
||||
for (const auto& map : CHATPAD_MAP)
|
||||
{
|
||||
if (chatpad_pressed(gp_chatpad, map.gp_mask))
|
||||
{
|
||||
in_report_.dButtons[map.button_offset] |= map.sb_mask;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& map : CHATPAD_TOGGLE_MAP)
|
||||
{
|
||||
if (chatpad_pressed(gp_chatpad, map.gp_mask))
|
||||
{
|
||||
in_report_.dButtons[map.button_offset] |= map.sb_mask;
|
||||
}
|
||||
}
|
||||
|
||||
if (gp_buttons & Gamepad::Button::X)
|
||||
{
|
||||
if (out_report_.Chaff_Extinguisher & 0x0F)
|
||||
{
|
||||
in_report_.dButtons[1] |= XboxOG::SB::Buttons1::EXTINGUISHER;
|
||||
}
|
||||
if (out_report_.Comm1_MagazineChange & 0x0F)
|
||||
{
|
||||
in_report_.dButtons[1] |= XboxOG::SB::Buttons1::WEAPONCONMAGAZINE;
|
||||
}
|
||||
if (out_report_.Washing_LineColorChange & 0xF0)
|
||||
{
|
||||
in_report_.dButtons[1] |= XboxOG::SB::Buttons1::WASHING;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t gp_dpad = gamepad.get_dpad_buttons();
|
||||
|
||||
if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_MESSENGER) || gp_buttons & Gamepad::Button::BACK)
|
||||
{
|
||||
for (const auto& map : CHATPAD_MAP_ALT1)
|
||||
{
|
||||
if (chatpad_pressed(gp_chatpad, map.gp_mask))
|
||||
{
|
||||
in_report_.dButtons[map.button_offset] |= map.sb_mask;
|
||||
}
|
||||
}
|
||||
// for (uint8_t i = 0; i < sizeof(CHATPAD_MAP_ALT1) / sizeof(CHATPAD_MAP_ALT1[0]); i++)
|
||||
// {
|
||||
// if (chatpad_pressed(gp_chatpad, CHATPAD_MAP_ALT1[i].gp_mask))
|
||||
// {
|
||||
// in_report_.dButtons[CHATPAD_MAP_ALT1[i].button_offset] |= CHATPAD_MAP_ALT1[i].sb_mask;
|
||||
// }
|
||||
// }
|
||||
if (gp_dpad & Gamepad::DPad::UP || gp_dpad & Gamepad::DPad::RIGHT)
|
||||
{
|
||||
in_report_.tunerDial += (prev_in_report_.tunerDial < 15) ? 1 : -15;
|
||||
}
|
||||
if (gp_dpad & Gamepad::DPad::DOWN || gp_dpad & Gamepad::DPad::LEFT)
|
||||
{
|
||||
in_report_.tunerDial -= (prev_in_report_.tunerDial > 0) ? 1 : -15;
|
||||
}
|
||||
}
|
||||
else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_ORANGE))
|
||||
{
|
||||
for (const auto& map : CHATPAD_MAP_ALT2)
|
||||
{
|
||||
if (chatpad_pressed(gp_chatpad, map.gp_mask))
|
||||
{
|
||||
in_report_.dButtons[map.button_offset] |= map.sb_mask;
|
||||
}
|
||||
}
|
||||
// for (uint8_t i = 0; i < sizeof(CHATPAD_MAP_ALT2) / sizeof(CHATPAD_MAP_ALT2[0]); i++)
|
||||
// {
|
||||
// if (chatpad_pressed(gp_chatpad, CHATPAD_MAP_ALT2[i].gp_mask))
|
||||
// {
|
||||
// in_report_.dButtons[CHATPAD_MAP_ALT2[i].button_offset] |= CHATPAD_MAP_ALT2[i].sb_mask;
|
||||
// }
|
||||
// }
|
||||
if (!(gp_dpad & Gamepad::DPad::LEFT) && !(gp_dpad & Gamepad::DPad::RIGHT))
|
||||
{
|
||||
if (gp_dpad & Gamepad::DPad::UP)
|
||||
{
|
||||
in_report_.gearLever += (prev_in_report_.gearLever < XboxOG::SB::Gear::G5) ? 1 : 0;
|
||||
}
|
||||
if (gp_dpad & Gamepad::DPad::DOWN)
|
||||
{
|
||||
in_report_.gearLever -= (prev_in_report_.gearLever > XboxOG::SB::Gear::R) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_SHIFT))
|
||||
{
|
||||
if (in_report_.dButtons[2] & XboxOG::SB::BUTTONS2_TOGGLE_MID)
|
||||
{
|
||||
in_report_.dButtons[2] &= ~XboxOG::SB::BUTTONS2_TOGGLE_MID;
|
||||
}
|
||||
else
|
||||
{
|
||||
in_report_.dButtons[2] |= XboxOG::SB::BUTTONS2_TOGGLE_MID;
|
||||
}
|
||||
}
|
||||
|
||||
in_report_.leftPedal = gamepad.get_trigger_l().uint16();
|
||||
in_report_.rightPedal = gamepad.get_trigger_r().uint16();
|
||||
in_report_.middlePedal = chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_BACK) ? 0xFF00 : 0x0000;
|
||||
in_report_.rotationLever = chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_MESSENGER) ? 0 :
|
||||
(gp_buttons & Gamepad::Button::BACK) ? 0 :
|
||||
(gp_dpad & Gamepad::DPad::LEFT) ? INT16_MIN :
|
||||
(gp_dpad & Gamepad::DPad::RIGHT) ? INT16_MAX : 0;
|
||||
|
||||
in_report_.sightChangeX = gamepad.get_joystick_lx().int16(true);
|
||||
in_report_.sightChangeY = gamepad.get_joystick_ly().int16();
|
||||
|
||||
int32_t axis_value = static_cast<int32_t>(gamepad.get_joystick_rx().int16(true));
|
||||
|
||||
if (axis_value > XboxOG::SB::DEFAULT_DEADZONE)
|
||||
{
|
||||
vmouse_x_ += axis_value / sensitivity_;
|
||||
}
|
||||
|
||||
axis_value = static_cast<int32_t>(gamepad.get_joystick_ry().int16(true));
|
||||
|
||||
if (axis_value > XboxOG::SB::DEFAULT_DEADZONE)
|
||||
{
|
||||
vmouse_y_ -= axis_value / sensitivity_;
|
||||
}
|
||||
|
||||
if (vmouse_x_ < 0) vmouse_x_ = 0;
|
||||
if (vmouse_x_ > UINT16_MAX) vmouse_x_ = UINT16_MAX;
|
||||
if (vmouse_y_ > UINT16_MAX) vmouse_y_ = UINT16_MAX;
|
||||
if (vmouse_y_ < 0) vmouse_y_ = 0;
|
||||
|
||||
if (gp_buttons & Gamepad::Button::L3)
|
||||
{
|
||||
if ((time_us_32() / 1000) - aim_reset_timer_ > 500)
|
||||
{
|
||||
vmouse_x_ = XboxOG::SB::AIMING_MID;
|
||||
vmouse_y_ = XboxOG::SB::AIMING_MID;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
aim_reset_timer_ = time_us_32() / 1000;
|
||||
}
|
||||
|
||||
in_report_.aimingX = static_cast<uint16_t>(vmouse_x_);
|
||||
in_report_.aimingY = static_cast<uint16_t>(vmouse_y_);
|
||||
|
||||
if (tud_suspended())
|
||||
{
|
||||
tud_remote_wakeup();
|
||||
}
|
||||
|
||||
if (std::memcmp(&prev_in_report_, &in_report_, sizeof(XboxOG::SB::InReport)) != 0 &&
|
||||
tud_xid::send_report(0, reinterpret_cast<uint8_t*>(&in_report_), sizeof(XboxOG::SB::InReport)))
|
||||
{
|
||||
std::memcpy(&prev_in_report_, &in_report_, sizeof(XboxOG::SB::InReport));
|
||||
}
|
||||
// }
|
||||
|
||||
// Gamepad::Chatpad gp_chatpad = gamepad.chatpad();
|
||||
// gp_chatpad = gamepad.get_chatpad();
|
||||
|
||||
// if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_ORANGE))
|
||||
// {
|
||||
// uint16_t new_sense = 0;
|
||||
|
||||
// if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_9))
|
||||
// {
|
||||
// new_sense = 200;
|
||||
// }
|
||||
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_8))
|
||||
// {
|
||||
// new_sense = 250;
|
||||
// }
|
||||
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_7))
|
||||
// {
|
||||
// new_sense = 300;
|
||||
// }
|
||||
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_6))
|
||||
// {
|
||||
// new_sense = 350;
|
||||
// }
|
||||
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_5))
|
||||
// {
|
||||
// new_sense = 400;
|
||||
// }
|
||||
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_4))
|
||||
// {
|
||||
// new_sense = 650;
|
||||
// }
|
||||
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_3))
|
||||
// {
|
||||
// new_sense = 800;
|
||||
// }
|
||||
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_2))
|
||||
// {
|
||||
// new_sense = 1000;
|
||||
// }
|
||||
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_1))
|
||||
// {
|
||||
// new_sense = 1200;
|
||||
// }
|
||||
|
||||
// if (new_sense != 0 && new_sense != sensitivity_)
|
||||
// {
|
||||
// sensitivity_ = new_sense;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (tud_xid::receive_report(0, reinterpret_cast<uint8_t*>(&out_report_), sizeof(XboxOG::SB::OutReport)) &&
|
||||
// out_report_.bLength == sizeof(XboxOG::SB::OutReport))
|
||||
// {
|
||||
// uint8_t rumble = out_report_.Chaff_Extinguisher;
|
||||
// rumble |= out_report_.Chaff_Extinguisher << 4;
|
||||
// rumble |= out_report_.Comm1_MagazineChange << 4;
|
||||
// rumble |= out_report_.CockpitHatch_EmergencyEject << 4;
|
||||
|
||||
// gamepad.set_rumble_l(rumble);
|
||||
// gamepad.set_rumble_r(rumble);
|
||||
// }
|
||||
}
|
||||
|
||||
uint16_t XboxOGSBDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
std::memcpy(buffer, &in_report_, sizeof(in_report_));
|
||||
return sizeof(XboxOG::SB::InReport);
|
||||
}
|
||||
|
||||
void XboxOGSBDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
|
||||
|
||||
bool XboxOGSBDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return tud_xid::class_driver()->control_xfer_cb(rhport, stage, request);
|
||||
}
|
||||
|
||||
const uint16_t* XboxOGSBDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
const char *value = reinterpret_cast<const char*>(XboxOG::SB::STRING_DESCRIPTORS[index]);
|
||||
return get_string_descriptor(value, index);
|
||||
}
|
||||
|
||||
const uint8_t* XboxOGSBDevice::get_descriptor_device_cb()
|
||||
{
|
||||
return reinterpret_cast<const uint8_t*>(&XboxOG::GP::DEVICE_DESCRIPTORS);
|
||||
}
|
||||
|
||||
const uint8_t* XboxOGSBDevice::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t* XboxOGSBDevice::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return XboxOG::SB::CONFIGURATION_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* XboxOGSBDevice::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef _XBOXGOG_SB_DEVICE_H_
|
||||
#define _XBOXGOG_SB_DEVICE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <numeric>
|
||||
|
||||
#include "Gamepad.h"
|
||||
#include "USBDevice/DeviceDriver/DeviceDriver.h"
|
||||
#include "Descriptors/XboxOG.h"
|
||||
|
||||
class XboxOGSBDevice : public DeviceDriver
|
||||
{
|
||||
public:
|
||||
struct ButtonMap
|
||||
{
|
||||
uint16_t gp_mask;
|
||||
uint16_t sb_mask;
|
||||
uint8_t button_offset;
|
||||
};
|
||||
|
||||
void initialize() override;
|
||||
void process(const uint8_t idx, Gamepad& gamepad) override;
|
||||
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
|
||||
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
|
||||
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
|
||||
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
|
||||
const uint8_t* get_descriptor_device_cb() override;
|
||||
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
|
||||
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
|
||||
const uint8_t* get_descriptor_device_qualifier_cb() override;
|
||||
|
||||
private:
|
||||
int32_t vmouse_x_ = XboxOG::SB::AIMING_MID;
|
||||
int32_t vmouse_y_ = XboxOG::SB::AIMING_MID;
|
||||
uint16_t sensitivity_ = XboxOG::SB::DEFAULT_SENSE;
|
||||
uint32_t aim_reset_timer_ = 0;
|
||||
|
||||
XboxOG::SB::InReport in_report_;
|
||||
XboxOG::SB::InReport prev_in_report_;
|
||||
XboxOG::SB::OutReport out_report_;
|
||||
|
||||
static inline bool chatpad_pressed(const Gamepad::Chatpad& chatpad_array, const uint16_t keycode)
|
||||
{
|
||||
if (std::accumulate(chatpad_array.data(), chatpad_array.data() + sizeof(Gamepad::Chatpad), 0) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (keycode < 17 && (chatpad_array[0] & keycode))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (keycode < 17)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (chatpad_array[1] == keycode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (chatpad_array[2] == keycode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _XBOXGOG_SB_DEVICE_H_
|
||||
117
Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_XR.cpp
Normal file
117
Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/XboxOG_XR.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.h"
|
||||
#include "USBDevice/DeviceDriver/XboxOG/XboxOG_XR.h"
|
||||
|
||||
void XboxOGXRDevice::initialize()
|
||||
{
|
||||
tud_xid::initialize(tud_xid::Type::XREMOTE);
|
||||
class_driver_ = *tud_xid::class_driver();
|
||||
|
||||
std::memset(&in_report_, 0, sizeof(XboxOG::XR::InReport));
|
||||
in_report_.bLength = sizeof(XboxOG::XR::InReport);
|
||||
}
|
||||
|
||||
void XboxOGXRDevice::process(const uint8_t idx, Gamepad& gamepad)
|
||||
{
|
||||
if (!tud_xid::xremote_rom_available())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t index = tud_xid::get_index_by_type(0, tud_xid::Type::XREMOTE);
|
||||
uint32_t time_elapsed = to_ms_since_boot(get_absolute_time()) - ms_timer_;
|
||||
|
||||
if (tud_suspended())
|
||||
{
|
||||
tud_remote_wakeup();
|
||||
}
|
||||
|
||||
if (!tud_xid::send_report_ready(index) || time_elapsed < 64)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
in_report_.buttonCode = 0;
|
||||
|
||||
uint16_t gp_buttons = gamepad.get_buttons();
|
||||
uint8_t gp_dpad = gamepad.get_dpad_buttons();
|
||||
|
||||
if (gp_dpad & Gamepad::DPad::UP) in_report_.buttonCode |= XboxOG::XR::ButtonCode::UP ;
|
||||
if (gp_dpad & Gamepad::DPad::DOWN) in_report_.buttonCode |= XboxOG::XR::ButtonCode::DOWN ;
|
||||
if (gp_dpad & Gamepad::DPad::LEFT) in_report_.buttonCode |= XboxOG::XR::ButtonCode::LEFT ;
|
||||
if (gp_dpad & Gamepad::DPad::RIGHT) in_report_.buttonCode |= XboxOG::XR::ButtonCode::RIGHT;
|
||||
|
||||
if (gp_buttons & Gamepad::Button::SYS) in_report_.buttonCode |= XboxOG::XR::ButtonCode::DISPLAY;
|
||||
if (gp_buttons & Gamepad::Button::START) in_report_.buttonCode |= XboxOG::XR::ButtonCode::PLAY ;
|
||||
if (gp_buttons & Gamepad::Button::BACK) in_report_.buttonCode |= XboxOG::XR::ButtonCode::STOP ;
|
||||
|
||||
if (gp_buttons & Gamepad::Button::L3 && !(gp_buttons & Gamepad::Button::R3)) in_report_.buttonCode |= XboxOG::XR::ButtonCode::TITLE;
|
||||
if (gp_buttons & Gamepad::Button::R3 && !(gp_buttons & Gamepad::Button::L3)) in_report_.buttonCode |= XboxOG::XR::ButtonCode::MENU;
|
||||
if (gp_buttons & (Gamepad::Button::L3 | Gamepad::Button::R3)) in_report_.buttonCode |= XboxOG::XR::ButtonCode::INFO;
|
||||
|
||||
if (gp_buttons & Gamepad::Button::A) in_report_.buttonCode |= XboxOG::XR::ButtonCode::SELECT ;
|
||||
if (gp_buttons & Gamepad::Button::Y) in_report_.buttonCode |= XboxOG::XR::ButtonCode::PAUSE ;
|
||||
if (gp_buttons & Gamepad::Button::X) in_report_.buttonCode |= XboxOG::XR::ButtonCode::DISPLAY;
|
||||
if (gp_buttons & Gamepad::Button::B) in_report_.buttonCode |= XboxOG::XR::ButtonCode::BACK ;
|
||||
|
||||
if (gp_buttons & Gamepad::Button::LB && !(gp_buttons & Gamepad::Button::RB)) in_report_.buttonCode |= XboxOG::XR::ButtonCode::SKIP_MINUS;
|
||||
if (gp_buttons & Gamepad::Button::RB && !(gp_buttons & Gamepad::Button::LB)) in_report_.buttonCode |= XboxOG::XR::ButtonCode::SKIP_PLUS ;
|
||||
if (gp_buttons & (Gamepad::Button::LB | Gamepad::Button::RB)) in_report_.buttonCode |= XboxOG::XR::ButtonCode::DISPLAY;
|
||||
|
||||
if (gamepad.get_trigger_l().uint8() >= 100) in_report_.buttonCode |= XboxOG::XR::ButtonCode::REVERSE;
|
||||
if (gamepad.get_trigger_r().uint8() >= 100) in_report_.buttonCode |= XboxOG::XR::ButtonCode::FORWARD;
|
||||
|
||||
if (in_report_.buttonCode == 0x0000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
in_report_.timeElapsed = static_cast<uint16_t>(time_elapsed);
|
||||
|
||||
if (tud_xid::send_report(index, reinterpret_cast<uint8_t*>(&in_report_), sizeof(XboxOG::XR::InReport)))
|
||||
{
|
||||
ms_timer_ = to_ms_since_boot(get_absolute_time());
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t XboxOGXRDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
std::memcpy(buffer, &in_report_, sizeof(in_report_));
|
||||
return sizeof(XboxOG::XR::InReport);
|
||||
}
|
||||
|
||||
void XboxOGXRDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
|
||||
|
||||
bool XboxOGXRDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return tud_xid::class_driver()->control_xfer_cb(rhport, stage, request);
|
||||
}
|
||||
|
||||
const uint16_t* XboxOGXRDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
const char *value = reinterpret_cast<const char*>(XboxOG::GP::STRING_DESCRIPTORS[index]);
|
||||
return get_string_descriptor(value, index);
|
||||
}
|
||||
|
||||
const uint8_t* XboxOGXRDevice::get_descriptor_device_cb()
|
||||
{
|
||||
return reinterpret_cast<const uint8_t*>(&XboxOG::XR::DEVICE_DESCRIPTORS);
|
||||
}
|
||||
|
||||
const uint8_t* XboxOGXRDevice::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t* XboxOGXRDevice::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return XboxOG::XR::CONFIGURATION_DESCRIPTORS;
|
||||
}
|
||||
|
||||
const uint8_t* XboxOGXRDevice::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef _XBOXGOG_XREMOTE_DEVICE_H_
|
||||
#define _XBOXGOG_XREMOTE_DEVICE_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "USBDevice/DeviceDriver/DeviceDriver.h"
|
||||
#include "Descriptors/XboxOG.h"
|
||||
|
||||
class XboxOGXRDevice : public DeviceDriver
|
||||
{
|
||||
public:
|
||||
void initialize() override;
|
||||
void process(const uint8_t idx, Gamepad& gamepad) override;
|
||||
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
|
||||
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
|
||||
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
|
||||
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
|
||||
const uint8_t* get_descriptor_device_cb() override;
|
||||
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
|
||||
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
|
||||
const uint8_t* get_descriptor_device_qualifier_cb() override;
|
||||
|
||||
private:
|
||||
XboxOG::XR::InReport in_report_;
|
||||
uint32_t ms_timer_;
|
||||
};
|
||||
|
||||
#endif // _XBOXGOG_XREMOTE_DEVICE_H_
|
||||
@@ -0,0 +1,970 @@
|
||||
// #include "tusb_option.h"
|
||||
|
||||
// #if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XID)
|
||||
|
||||
// #include <cstring>
|
||||
|
||||
// #include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.h"
|
||||
// #include "Descriptors/XboxOG.h"
|
||||
|
||||
// #ifndef CFG_TUD_XID_XREMOTE_ENABLE
|
||||
// #define CFG_TUD_XID_XREMOTE_ENABLE 0
|
||||
// #endif
|
||||
|
||||
// #ifdef XREMOTE_ROM_AVAILABLE
|
||||
// #define XREMOTE_ENABLED (CFG_TUD_XID_XREMOTE_ENABLE ? 1 : 0)
|
||||
// #include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid_xremote_rom.h"
|
||||
// #else
|
||||
// #define XREMOTE_ENABLED 0
|
||||
// #endif
|
||||
|
||||
// namespace tud_xid {
|
||||
|
||||
// static constexpr uint8_t ENDPOINT_SIZE = 32;
|
||||
// static constexpr uint8_t INTERFACE_MULT = XREMOTE_ENABLED + 1;
|
||||
// static constexpr uint8_t INTERFACE_CLASS = 0x58;
|
||||
// static constexpr uint8_t INTERFACE_SUBCLASS = 0x42;
|
||||
|
||||
// enum class RequestType { SET_REPORT, GET_REPORT, GET_DESC, GET_CAPABILITIES_IN, GET_CAPABILITIES_OUT, UNKNOWN };
|
||||
|
||||
// struct Interface
|
||||
// {
|
||||
// Type type{Type::GAMEPAD};
|
||||
|
||||
// uint8_t itf_num{0xFF};
|
||||
|
||||
// uint8_t ep_in{0xFF};
|
||||
// uint8_t ep_out{0xFF};
|
||||
|
||||
// uint8_t ep_in_size{0xFF};
|
||||
// uint8_t ep_out_size{0xFF};
|
||||
|
||||
// std::array<uint8_t, ENDPOINT_SIZE> ep_out_buffer;
|
||||
// std::array<uint8_t, ENDPOINT_SIZE> gp_in_buffer;
|
||||
// std::array<uint8_t, ENDPOINT_SIZE> gp_out_buffer;
|
||||
|
||||
// Interface()
|
||||
// {
|
||||
// ep_out_buffer.fill(0);
|
||||
// gp_in_buffer.fill(0);
|
||||
// gp_out_buffer.fill(0);
|
||||
// }
|
||||
// };
|
||||
|
||||
// static std::array<Interface, CFG_TUD_XID * INTERFACE_MULT> interfaces_;
|
||||
// static Type xid_type_{Type::GAMEPAD};
|
||||
|
||||
// static inline RequestType get_request_type(const tusb_control_request_t *request)
|
||||
// {
|
||||
// if (request->bmRequestType == XboxOG::GET_REPORT_REQ_TYPE && request->bRequest == XboxOG::GET_REPORT_REQ && request->wValue == XboxOG::GET_REPORT_VALUE)
|
||||
// {
|
||||
// return RequestType::GET_REPORT;
|
||||
// }
|
||||
// if (request->bmRequestType == XboxOG::SET_REPORT_REQ_TYPE && request->bRequest == XboxOG::SET_REPORT_REQ && request->wValue == XboxOG::SET_REPORT_VALUE && request->wLength == static_cast<uint16_t>(0x06))
|
||||
// {
|
||||
// return RequestType::SET_REPORT;
|
||||
// }
|
||||
// if (request->bmRequestType == XboxOG::GET_DESC_REQ_TYPE && request->bRequest == XboxOG::GET_DESC_REQ && request->wValue == XboxOG::GET_DESC_VALUE)
|
||||
// {
|
||||
// return RequestType::GET_DESC;
|
||||
// }
|
||||
// if (request->bmRequestType == XboxOG::GET_CAP_REQ_TYPE && request->bRequest == XboxOG::GET_CAP_REQ)
|
||||
// {
|
||||
// if (request->wValue == XboxOG::GET_CAP_VALUE_IN)
|
||||
// {
|
||||
// return RequestType::GET_CAPABILITIES_IN;
|
||||
// }
|
||||
// else if (request->wValue == XboxOG::GET_CAP_VALUE_OUT)
|
||||
// {
|
||||
// return RequestType::GET_CAPABILITIES_OUT;
|
||||
// }
|
||||
// }
|
||||
// return RequestType::UNKNOWN;
|
||||
// }
|
||||
|
||||
// static inline uint8_t get_idx_by_itf(uint8_t itf)
|
||||
// {
|
||||
// for (uint8_t i = 0; i < interfaces_.size(); i++)
|
||||
// {
|
||||
// if (interfaces_[i].itf_num == itf)
|
||||
// {
|
||||
// return i;
|
||||
// }
|
||||
// if (interfaces_[i].type == Type::XREMOTE)
|
||||
// {
|
||||
// if (itf == interfaces_[i].itf_num + 1)
|
||||
// {
|
||||
// return i;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return 0xFF;
|
||||
// }
|
||||
|
||||
// static inline uint8_t get_idx_by_edpt(uint8_t edpt)
|
||||
// {
|
||||
// for (uint8_t i = 0; i < interfaces_.size(); i++)
|
||||
// {
|
||||
// if (interfaces_[i].ep_in == edpt || interfaces_[i].ep_out == edpt)
|
||||
// {
|
||||
// return i;
|
||||
// }
|
||||
// }
|
||||
// return 0xFF;
|
||||
// }
|
||||
|
||||
// uint8_t get_index_by_type(uint8_t type_idx, Type type)
|
||||
// {
|
||||
// uint8_t type_idx_ = 0;
|
||||
// for (uint8_t i = 0; i < interfaces_.size(); i++)
|
||||
// {
|
||||
// if (interfaces_[i].type == type)
|
||||
// {
|
||||
// if (type_idx_ == type_idx)
|
||||
// {
|
||||
// return i;
|
||||
// }
|
||||
// type_idx_++;
|
||||
// }
|
||||
// }
|
||||
// return 0xFF;
|
||||
// }
|
||||
|
||||
// static inline Interface* find_available_interface()
|
||||
// {
|
||||
// for (Interface &itf : interfaces_)
|
||||
// {
|
||||
// if (itf.itf_num == 0xFF)
|
||||
// {
|
||||
// return &itf;
|
||||
// }
|
||||
// }
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
// const uint8_t *xremote_get_rom()
|
||||
// {
|
||||
// #if XREMOTE_ENABLED
|
||||
// return XRemote::ROM;
|
||||
// #else
|
||||
// return nullptr;
|
||||
// #endif
|
||||
// }
|
||||
|
||||
// //Class driver
|
||||
|
||||
// static void xid_init()
|
||||
// {
|
||||
// for (Interface &itf : interfaces_)
|
||||
// {
|
||||
// itf = Interface();
|
||||
// itf.type = xid_type_;
|
||||
// }
|
||||
// }
|
||||
|
||||
// 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 == INTERFACE_CLASS, 0);
|
||||
// TU_VERIFY(itf_desc->bInterfaceSubClass == INTERFACE_SUBCLASS, 0);
|
||||
|
||||
// Interface *interface = find_available_interface();
|
||||
// TU_ASSERT(interface != nullptr, 0);
|
||||
|
||||
// uint16_t driver_len = 0;
|
||||
|
||||
// switch (interface->type)
|
||||
// {
|
||||
// case Type::GAMEPAD:
|
||||
// driver_len = XboxOG::GP::DRIVER_LEN;
|
||||
// break;
|
||||
// case Type::STEELBATTALION:
|
||||
// driver_len = XboxOG::SB::DRIVER_LEN;
|
||||
// break;
|
||||
// case Type::XREMOTE:
|
||||
// driver_len = XboxOG::XR::DRIVER_LEN;
|
||||
// break;
|
||||
// }
|
||||
|
||||
// TU_ASSERT(max_len >= driver_len, 0);
|
||||
|
||||
// interface->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) ? (interface->ep_in = ep_desc->bEndpointAddress) :
|
||||
// (interface->ep_out = ep_desc->bEndpointAddress);
|
||||
// }
|
||||
|
||||
// TU_VERIFY(itf_desc->bNumEndpoints >= 2, driver_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) ? (interface->ep_in = ep_desc->bEndpointAddress) :
|
||||
// (interface->ep_out = ep_desc->bEndpointAddress);
|
||||
// }
|
||||
|
||||
// return driver_len;
|
||||
// }
|
||||
|
||||
// static bool xid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
// {
|
||||
// uint8_t index = get_idx_by_edpt(ep_addr);
|
||||
|
||||
// TU_VERIFY(result == XFER_RESULT_SUCCESS, true);
|
||||
// TU_VERIFY(index != 0xFF, true);
|
||||
// TU_VERIFY(xferred_bytes < ENDPOINT_SIZE, true);
|
||||
|
||||
// if (ep_addr == interfaces_[index].ep_out)
|
||||
// {
|
||||
// std::memcpy(interfaces_[index].gp_out_buffer.data(), interfaces_[index].ep_out_buffer.data(), std::min(static_cast<uint8_t>(xferred_bytes), ENDPOINT_SIZE));
|
||||
// }
|
||||
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// bool xremote_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, Interface *interface)
|
||||
// {
|
||||
// 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");
|
||||
// const uint8_t *rom = xremote_get_rom();
|
||||
// if (rom == nullptr)
|
||||
// {
|
||||
// return false; //STALL
|
||||
// }
|
||||
// tud_control_xfer(rhport, request, const_cast<uint8_t*>(&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)
|
||||
// {
|
||||
// const uint8_t *rom = xremote_get_rom();
|
||||
// if (rom == nullptr)
|
||||
// {
|
||||
// return false; //STALL
|
||||
// }
|
||||
// tud_control_xfer(rhport, request, const_cast<uint8_t*>(&rom[request->wValue * 1024]), request->wLength);
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// 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_idx_by_itf(static_cast<uint8_t>(request->wIndex));
|
||||
// TU_VERIFY(index != 0xFF, false);
|
||||
|
||||
// void* desc_buffer;
|
||||
// uint16_t desc_buffer_len;
|
||||
|
||||
// switch (get_request_type(request))
|
||||
// {
|
||||
// case RequestType::GET_REPORT:
|
||||
// if (stage == CONTROL_STAGE_SETUP)
|
||||
// {
|
||||
// tud_control_xfer(rhport, request, interfaces_[index].gp_in_buffer.data(), std::min(static_cast<uint8_t>(request->wLength), ENDPOINT_SIZE));
|
||||
// }
|
||||
// return true;
|
||||
|
||||
// case RequestType::SET_REPORT:
|
||||
// if (stage == CONTROL_STAGE_SETUP) //Getting rumble report
|
||||
// {
|
||||
// tud_control_xfer(rhport, request, interfaces_[index].ep_out_buffer.data(), std::min(static_cast<uint8_t>(request->wLength), ENDPOINT_SIZE));
|
||||
// }
|
||||
// else if (stage == CONTROL_STAGE_ACK) //Got rumble report
|
||||
// {
|
||||
// std::memcpy(interfaces_[index].gp_out_buffer.data(), interfaces_[index].ep_out_buffer.data(), std::min(static_cast<uint8_t>(request->wLength), ENDPOINT_SIZE));
|
||||
// }
|
||||
// return true;
|
||||
|
||||
// case RequestType::GET_DESC:
|
||||
// if (stage == CONTROL_STAGE_SETUP)
|
||||
// {
|
||||
// switch (interfaces_[index].type)
|
||||
// {
|
||||
// case Type::GAMEPAD:
|
||||
// desc_buffer = (void*)XboxOG::GP::XID_DEVICE_DESCRIPTORS;
|
||||
// desc_buffer_len = sizeof(XboxOG::GP::XID_DEVICE_DESCRIPTORS);
|
||||
// break;
|
||||
// case Type::STEELBATTALION:
|
||||
// desc_buffer = (void*)XboxOG::SB::XID_DEVICE_DESCRIPTORS;
|
||||
// desc_buffer_len = sizeof(XboxOG::SB::XID_DEVICE_DESCRIPTORS);
|
||||
// break;
|
||||
// case Type::XREMOTE:
|
||||
// desc_buffer = (void*)XboxOG::XR::XID_DEVICE_DESCRIPTORS;
|
||||
// desc_buffer_len = sizeof(XboxOG::XR::XID_DEVICE_DESCRIPTORS);
|
||||
// break;
|
||||
// default:
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// tud_control_xfer(rhport, request, desc_buffer, desc_buffer_len);
|
||||
// }
|
||||
// return true;
|
||||
|
||||
// case RequestType::GET_CAPABILITIES_IN:
|
||||
// if (stage == CONTROL_STAGE_SETUP)
|
||||
// {
|
||||
// switch (interfaces_[index].type)
|
||||
// {
|
||||
// case Type::GAMEPAD:
|
||||
// desc_buffer = (void*)XboxOG::GP::XID_CAPABILITIES_IN;
|
||||
// desc_buffer_len = sizeof(XboxOG::GP::XID_CAPABILITIES_IN);
|
||||
// break;
|
||||
// case Type::STEELBATTALION:
|
||||
// desc_buffer = (void*)XboxOG::SB::XID_CAPABILITIES_IN;
|
||||
// desc_buffer_len = sizeof(XboxOG::SB::XID_CAPABILITIES_IN);
|
||||
// break;
|
||||
// default:
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// tud_control_xfer(rhport, request, desc_buffer, desc_buffer_len);
|
||||
// }
|
||||
// return true;
|
||||
|
||||
// case RequestType::GET_CAPABILITIES_OUT:
|
||||
// if (stage == CONTROL_STAGE_SETUP)
|
||||
// {
|
||||
// switch (interfaces_[index].type)
|
||||
// {
|
||||
// case Type::GAMEPAD:
|
||||
// desc_buffer = (void*)XboxOG::GP::XID_CAPABILITIES_OUT;
|
||||
// desc_buffer_len = sizeof(XboxOG::GP::XID_CAPABILITIES_OUT);
|
||||
// break;
|
||||
// case Type::STEELBATTALION:
|
||||
// desc_buffer = (void*)XboxOG::SB::XID_CAPABILITIES_OUT;
|
||||
// desc_buffer_len = sizeof(XboxOG::SB::XID_CAPABILITIES_OUT);
|
||||
// break;
|
||||
// default:
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// tud_control_xfer(rhport, request, desc_buffer, desc_buffer_len);
|
||||
// }
|
||||
// return true;
|
||||
|
||||
// default:
|
||||
// if (interfaces_[index].type != Type::XREMOTE)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// return xremote_control_xfer(rhport, stage, request, &interfaces_[index]);
|
||||
// }
|
||||
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// static const usbd_class_driver_t tud_xid_class_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
|
||||
// };
|
||||
|
||||
// //Public API
|
||||
|
||||
// //Call first before device stack is initialized with tud_init(), will default to Duke/Gamepad otherwise
|
||||
// void initialize(Type xid_type)
|
||||
// {
|
||||
// xid_type_ = xid_type;
|
||||
// }
|
||||
|
||||
// const usbd_class_driver_t* class_driver()
|
||||
// {
|
||||
// return &tud_xid_class_driver_;
|
||||
// }
|
||||
|
||||
// bool send_report_ready(uint8_t index)
|
||||
// {
|
||||
// TU_VERIFY(index < interfaces_.size(), false);
|
||||
// TU_VERIFY(interfaces_[index].ep_in != 0xFF, false);
|
||||
// return (tud_ready() && !usbd_edpt_busy(BOARD_TUD_RHPORT, interfaces_[index].ep_in));
|
||||
// }
|
||||
|
||||
// bool send_report(uint8_t index, const uint8_t* report, uint16_t len)
|
||||
// {
|
||||
// TU_VERIFY(len < ENDPOINT_SIZE, false);
|
||||
// TU_VERIFY(send_report_ready(index), false);
|
||||
|
||||
// if (tud_suspended())
|
||||
// {
|
||||
// tud_remote_wakeup();
|
||||
// }
|
||||
|
||||
// std::memcpy(interfaces_[index].gp_in_buffer.data(), report, len);
|
||||
|
||||
// return usbd_edpt_xfer(BOARD_TUD_RHPORT, interfaces_[index].ep_in, interfaces_[index].gp_in_buffer.data(), len);
|
||||
// }
|
||||
|
||||
// bool receive_report(uint8_t index, uint8_t *report, uint16_t len)
|
||||
// {
|
||||
// TU_VERIFY(index < interfaces_.size(), false);
|
||||
// TU_VERIFY(interfaces_[index].ep_out != 0xFF, false);
|
||||
// TU_VERIFY(len < ENDPOINT_SIZE, false);
|
||||
|
||||
// std::memcpy(report, interfaces_[index].gp_out_buffer.data(), len);
|
||||
|
||||
// if (tud_ready() && !usbd_edpt_busy(BOARD_TUD_RHPORT, interfaces_[index].ep_out))
|
||||
// {
|
||||
// usbd_edpt_xfer(BOARD_TUD_RHPORT, interfaces_[index].ep_out, interfaces_[index].ep_out_buffer.data(), len);
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// bool xremote_rom_available()
|
||||
// {
|
||||
// return (xremote_get_rom() != nullptr);
|
||||
// }
|
||||
|
||||
// } // namespace TUDXID
|
||||
|
||||
// #endif // TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XID
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XID)
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.h"
|
||||
#include "Descriptors/XboxOG.h"
|
||||
|
||||
#ifndef CFG_TUD_XID_XREMOTE_ENABLE
|
||||
#define CFG_TUD_XID_XREMOTE_ENABLE 0
|
||||
#endif
|
||||
|
||||
#ifdef XREMOTE_ROM_AVAILABLE
|
||||
#define XREMOTE_ENABLED (CFG_TUD_XID_XREMOTE_ENABLE ? 1 : 0)
|
||||
#include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid_xremote_rom.h"
|
||||
#else
|
||||
#define XREMOTE_ENABLED 0
|
||||
#endif
|
||||
|
||||
namespace tud_xid {
|
||||
|
||||
static constexpr uint8_t ENDPOINT_SIZE = 32;
|
||||
static constexpr uint8_t INTERFACE_MULT = XREMOTE_ENABLED + 1;
|
||||
static constexpr uint8_t INTERFACE_CLASS = 0x58;
|
||||
static constexpr uint8_t INTERFACE_SUBCLASS = 0x42;
|
||||
|
||||
enum class RequestType { SET_REPORT, GET_REPORT, GET_DESC, GET_CAPABILITIES_IN, GET_CAPABILITIES_OUT, UNKNOWN };
|
||||
|
||||
struct Interface
|
||||
{
|
||||
Type type{Type::GAMEPAD};
|
||||
|
||||
uint8_t itf_num{0xFF};
|
||||
|
||||
uint8_t ep_in{0xFF};
|
||||
uint8_t ep_out{0xFF};
|
||||
|
||||
uint16_t ep_in_size{ENDPOINT_SIZE};
|
||||
uint16_t ep_out_size{ENDPOINT_SIZE};
|
||||
|
||||
std::array<uint8_t, ENDPOINT_SIZE> ep_in_buffer;
|
||||
std::array<uint8_t, ENDPOINT_SIZE> ep_out_buffer;
|
||||
|
||||
Interface()
|
||||
{
|
||||
ep_out_buffer.fill(0);
|
||||
ep_in_buffer.fill(0);
|
||||
}
|
||||
};
|
||||
|
||||
static std::array<Interface, CFG_TUD_XID * INTERFACE_MULT> interfaces_;
|
||||
static Type xid_type_{Type::GAMEPAD};
|
||||
|
||||
static inline RequestType get_request_type(const tusb_control_request_t *request)
|
||||
{
|
||||
if (request->bmRequestType == XboxOG::GET_REPORT_REQ_TYPE && request->bRequest == XboxOG::GET_REPORT_REQ && request->wValue == XboxOG::GET_REPORT_VALUE)
|
||||
{
|
||||
return RequestType::GET_REPORT;
|
||||
}
|
||||
if (request->bmRequestType == XboxOG::SET_REPORT_REQ_TYPE && request->bRequest == XboxOG::SET_REPORT_REQ && request->wValue == XboxOG::SET_REPORT_VALUE && request->wLength == static_cast<uint16_t>(0x06))
|
||||
{
|
||||
return RequestType::SET_REPORT;
|
||||
}
|
||||
if (request->bmRequestType == XboxOG::GET_DESC_REQ_TYPE && request->bRequest == XboxOG::GET_DESC_REQ && request->wValue == XboxOG::GET_DESC_VALUE)
|
||||
{
|
||||
return RequestType::GET_DESC;
|
||||
}
|
||||
if (request->bmRequestType == XboxOG::GET_CAP_REQ_TYPE && request->bRequest == XboxOG::GET_CAP_REQ)
|
||||
{
|
||||
if (request->wValue == XboxOG::GET_CAP_VALUE_IN)
|
||||
{
|
||||
return RequestType::GET_CAPABILITIES_IN;
|
||||
}
|
||||
else if (request->wValue == XboxOG::GET_CAP_VALUE_OUT)
|
||||
{
|
||||
return RequestType::GET_CAPABILITIES_OUT;
|
||||
}
|
||||
}
|
||||
return RequestType::UNKNOWN;
|
||||
}
|
||||
|
||||
static inline uint8_t get_idx_by_itf(uint8_t itf)
|
||||
{
|
||||
for (uint8_t i = 0; i < interfaces_.size(); i++)
|
||||
{
|
||||
if (interfaces_[i].itf_num == itf)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
if (interfaces_[i].type == Type::XREMOTE)
|
||||
{
|
||||
if (itf == interfaces_[i].itf_num + 1)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static inline uint8_t get_idx_by_edpt(uint8_t edpt)
|
||||
{
|
||||
for (uint8_t i = 0; i < interfaces_.size(); i++)
|
||||
{
|
||||
if (interfaces_[i].ep_in == edpt || interfaces_[i].ep_out == edpt)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
uint8_t get_index_by_type(uint8_t type_idx, Type type)
|
||||
{
|
||||
uint8_t type_idx_ = 0;
|
||||
for (uint8_t i = 0; i < interfaces_.size(); i++)
|
||||
{
|
||||
if (interfaces_[i].type == type)
|
||||
{
|
||||
if (type_idx_ == type_idx)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
type_idx_++;
|
||||
}
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static inline Interface* find_available_interface()
|
||||
{
|
||||
for (Interface &itf : interfaces_)
|
||||
{
|
||||
if (itf.itf_num == 0xFF)
|
||||
{
|
||||
return &itf;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t *xremote_get_rom()
|
||||
{
|
||||
#if XREMOTE_ENABLED
|
||||
return XRemote::ROM;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
//Class driver
|
||||
|
||||
static void xid_init()
|
||||
{
|
||||
for (Interface &itf : interfaces_)
|
||||
{
|
||||
itf = Interface();
|
||||
itf.type = xid_type_;
|
||||
}
|
||||
}
|
||||
|
||||
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 == INTERFACE_CLASS, 0);
|
||||
TU_VERIFY(itf_desc->bInterfaceSubClass == INTERFACE_SUBCLASS, 0);
|
||||
|
||||
Interface *interface = find_available_interface();
|
||||
TU_ASSERT(interface != nullptr, 0);
|
||||
|
||||
uint16_t driver_len = 0;
|
||||
|
||||
switch (interface->type)
|
||||
{
|
||||
case Type::GAMEPAD:
|
||||
driver_len = XboxOG::GP::DRIVER_LEN;
|
||||
break;
|
||||
case Type::STEELBATTALION:
|
||||
driver_len = XboxOG::SB::DRIVER_LEN;
|
||||
break;
|
||||
case Type::XREMOTE:
|
||||
driver_len = XboxOG::XR::DRIVER_LEN;
|
||||
break;
|
||||
}
|
||||
|
||||
TU_ASSERT(max_len >= driver_len, 0);
|
||||
|
||||
const uint8_t *desc_ep = reinterpret_cast<const uint8_t*>(itf_desc);
|
||||
int endpoint = 0;
|
||||
int pos = 0;
|
||||
|
||||
while (endpoint < itf_desc->bNumEndpoints && pos < max_len)
|
||||
{
|
||||
if (tu_desc_type(desc_ep) != TUSB_DESC_ENDPOINT)
|
||||
{
|
||||
pos += tu_desc_len(desc_ep);
|
||||
desc_ep = tu_desc_next(desc_ep);
|
||||
continue;
|
||||
}
|
||||
|
||||
const tusb_desc_endpoint_t *ep_desc = reinterpret_cast<const tusb_desc_endpoint_t*>(desc_ep);
|
||||
TU_VERIFY(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType, 0);
|
||||
TU_VERIFY(usbd_edpt_open(rhport, ep_desc), 0);
|
||||
|
||||
if (tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN)
|
||||
{
|
||||
interface->ep_in = ep_desc->bEndpointAddress;
|
||||
interface->ep_in_size = ep_desc->wMaxPacketSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
interface->ep_out = ep_desc->bEndpointAddress;
|
||||
interface->ep_out_size = ep_desc->wMaxPacketSize;
|
||||
}
|
||||
|
||||
interface->itf_num = itf_desc->bInterfaceNumber;
|
||||
|
||||
pos += tu_desc_len(desc_ep);
|
||||
desc_ep = tu_desc_next(desc_ep);
|
||||
endpoint++;
|
||||
}
|
||||
|
||||
// interface->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)
|
||||
// {
|
||||
// TU_VERIFY(usbd_edpt_open(rhport, ep_desc), 0);
|
||||
// if (tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN)
|
||||
// {
|
||||
// interface->ep_in = ep_desc->bEndpointAddress;
|
||||
// interface->ep_in_size = ep_desc->wMaxPacketSize;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// interface->ep_out = ep_desc->bEndpointAddress;
|
||||
// interface->ep_out_size = ep_desc->wMaxPacketSize;
|
||||
// }
|
||||
// }
|
||||
|
||||
// TU_VERIFY(itf_desc->bNumEndpoints >= 2, driver_len);
|
||||
// ep_desc = (tusb_desc_endpoint_t *)tu_desc_next(ep_desc);
|
||||
|
||||
// if (tu_desc_type(ep_desc) == TUSB_DESC_ENDPOINT)
|
||||
// {
|
||||
// TU_VERIFY(usbd_edpt_open(rhport, ep_desc), driver_len);
|
||||
// if (tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN)
|
||||
// {
|
||||
// interface->ep_in = ep_desc->bEndpointAddress;
|
||||
// interface->ep_in_size = ep_desc->wMaxPacketSize;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// interface->ep_out = ep_desc->bEndpointAddress;
|
||||
// interface->ep_out_size = ep_desc->wMaxPacketSize;
|
||||
// }
|
||||
// }
|
||||
|
||||
return driver_len;
|
||||
}
|
||||
|
||||
static bool xid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
// uint8_t index = get_idx_by_edpt(ep_addr);
|
||||
|
||||
// TU_VERIFY(result == XFER_RESULT_SUCCESS, true);
|
||||
// TU_VERIFY(index != 0xFF, true);
|
||||
// TU_VERIFY(xferred_bytes < ENDPOINT_SIZE, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool xremote_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, Interface *interface)
|
||||
{
|
||||
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");
|
||||
const uint8_t *rom = xremote_get_rom();
|
||||
if (rom == nullptr)
|
||||
{
|
||||
return false; //STALL
|
||||
}
|
||||
tud_control_xfer(rhport, request, const_cast<uint8_t*>(&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)
|
||||
{
|
||||
const uint8_t *rom = xremote_get_rom();
|
||||
if (rom == nullptr)
|
||||
{
|
||||
return false; //STALL
|
||||
}
|
||||
tud_control_xfer(rhport, request, const_cast<uint8_t*>(&rom[request->wValue * 1024]), request->wLength);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool steelbattalion_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, Interface *p_xid);
|
||||
bool duke_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, Interface *p_xid);
|
||||
|
||||
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_idx_by_itf(static_cast<uint8_t>(request->wIndex));
|
||||
TU_VERIFY(index != 0xFF, false);
|
||||
|
||||
Interface& interface = interfaces_[index];
|
||||
|
||||
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, interface.ep_in_buffer.data(), std::min(request->wLength, interface.ep_in_size));
|
||||
}
|
||||
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, interface.ep_out_buffer.data(), std::min(request->wLength, interface.ep_out_size));
|
||||
}
|
||||
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(interface.out, interface.ep_out_buff, min(request->wLength, sizeof(interface.out)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (interface.type)
|
||||
{
|
||||
case Type::GAMEPAD:
|
||||
ret = duke_control_xfer(rhport, stage, request, &interface);
|
||||
break;
|
||||
case Type::STEELBATTALION:
|
||||
ret = steelbattalion_control_xfer(rhport, stage, request, &interface);
|
||||
break;
|
||||
case Type::XREMOTE:
|
||||
ret = xremote_control_xfer(rhport, stage, request, &interface);
|
||||
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 tud_xid_class_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
|
||||
};
|
||||
|
||||
//Public API
|
||||
|
||||
//Call first before device stack is initialized with tud_init(), will default to Duke/Gamepad otherwise
|
||||
void initialize(Type xid_type)
|
||||
{
|
||||
xid_type_ = xid_type;
|
||||
}
|
||||
|
||||
const usbd_class_driver_t* class_driver()
|
||||
{
|
||||
return &tud_xid_class_driver_;
|
||||
}
|
||||
|
||||
bool send_report_ready(uint8_t index)
|
||||
{
|
||||
TU_VERIFY(index < interfaces_.size(), false);
|
||||
TU_VERIFY(interfaces_[index].ep_in != 0xFF, false);
|
||||
return (tud_ready() && !usbd_edpt_busy(BOARD_TUD_RHPORT, interfaces_[index].ep_in));
|
||||
}
|
||||
|
||||
bool send_report(uint8_t index, const uint8_t* report, uint16_t len)
|
||||
{
|
||||
TU_VERIFY(len < ENDPOINT_SIZE, false);
|
||||
TU_VERIFY(send_report_ready(index), false);
|
||||
|
||||
if (tud_suspended())
|
||||
{
|
||||
tud_remote_wakeup();
|
||||
}
|
||||
|
||||
uint16_t size = std::min(len, static_cast<uint16_t>(interfaces_[index].ep_in_size));
|
||||
|
||||
std::memcpy(interfaces_[index].ep_in_buffer.data(), report, size);
|
||||
|
||||
return usbd_edpt_xfer(BOARD_TUD_RHPORT, interfaces_[index].ep_in, interfaces_[index].ep_in_buffer.data(), size);
|
||||
}
|
||||
|
||||
bool receive_report(uint8_t index, uint8_t *report, uint16_t len)
|
||||
{
|
||||
TU_VERIFY(index < interfaces_.size());
|
||||
TU_VERIFY(interfaces_[index].ep_out != 0xFF);
|
||||
TU_VERIFY(len < ENDPOINT_SIZE);
|
||||
|
||||
uint16_t size = std::min(len, static_cast<uint16_t>(interfaces_[index].ep_out_size));
|
||||
|
||||
if (tud_ready() && !usbd_edpt_busy(BOARD_TUD_RHPORT, interfaces_[index].ep_out))
|
||||
{
|
||||
usbd_edpt_xfer(BOARD_TUD_RHPORT, interfaces_[index].ep_out, interfaces_[index].ep_out_buffer.data(), size);
|
||||
}
|
||||
|
||||
std::memcpy(report, interfaces_[index].ep_out_buffer.data(), size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool xremote_rom_available()
|
||||
{
|
||||
return (xremote_get_rom() != nullptr);
|
||||
}
|
||||
|
||||
bool steelbattalion_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, Interface *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 *)XboxOG::SB::XID_DEVICE_DESCRIPTORS, sizeof(XboxOG::SB::XID_DEVICE_DESCRIPTORS));
|
||||
}
|
||||
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 *)XboxOG::SB::XID_CAPABILITIES_IN, sizeof(XboxOG::SB::XID_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 *)XboxOG::SB::XID_CAPABILITIES_OUT, sizeof(XboxOG::SB::XID_CAPABILITIES_OUT));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool duke_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, Interface *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 *)XboxOG::GP::XID_DEVICE_DESCRIPTORS, sizeof(XboxOG::GP::XID_DEVICE_DESCRIPTORS));
|
||||
}
|
||||
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 *)XboxOG::GP::XID_CAPABILITIES_IN, sizeof(XboxOG::GP::XID_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 *)XboxOG::SB::XID_CAPABILITIES_OUT, sizeof(XboxOG::SB::XID_CAPABILITIES_OUT));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace TUDXID
|
||||
|
||||
#endif // TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XID
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
|
||||
TinyUSB XID Device driver based on https://github.com/Ryzee119/ogx360_t4
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Ryan Wendland
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _TUD_XID_H_
|
||||
#define _TUD_XID_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
namespace tud_xid
|
||||
{
|
||||
enum class Type { GAMEPAD, STEELBATTALION, XREMOTE };
|
||||
|
||||
void initialize(tud_xid::Type xid_type);
|
||||
|
||||
const usbd_class_driver_t* class_driver();
|
||||
|
||||
uint8_t get_index_by_type(uint8_t type_index, tud_xid::Type xid_type);
|
||||
bool receive_report(uint8_t idx, uint8_t* buffer, uint16_t len);
|
||||
bool send_report(uint8_t idx, const uint8_t* buffer, uint16_t len);
|
||||
bool send_report_ready(uint8_t idx);
|
||||
bool xremote_rom_available();
|
||||
|
||||
} // namespace TUDXID
|
||||
|
||||
#endif // _TUD_XID_H_
|
||||
60
Firmware/RP2040/src/USBDevice/DeviceManager.cpp
Normal file
60
Firmware/RP2040/src/USBDevice/DeviceManager.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "tusb.h"
|
||||
|
||||
#include "board_config.h"
|
||||
#include "USBDevice/DeviceDriver/PSClassic/PSClassic.h"
|
||||
#include "USBDevice/DeviceDriver/XInput/XInput.h"
|
||||
#include "USBDevice/DeviceDriver/Switch/Switch.h"
|
||||
#include "USBDevice/DeviceDriver/DInput/DInput.h"
|
||||
#include "USBDevice/DeviceDriver/PS3/PS3.h"
|
||||
#include "USBDevice/DeviceDriver/XboxOG/XboxOG_GP.h"
|
||||
#include "USBDevice/DeviceDriver/XboxOG/XboxOG_SB.h"
|
||||
#include "USBDevice/DeviceDriver/XboxOG/XboxOG_XR.h"
|
||||
#include "USBDevice/DeviceDriver/WebApp/WebApp.h"
|
||||
#include "USBDevice/DeviceManager.h"
|
||||
|
||||
#if defined(CONFIG_EN_UART_BRIDGE)
|
||||
#include "USBDevice/DeviceDriver/UARTBridge/UARTBridge.h"
|
||||
#endif // defined(CONFIG_EN_UART_BRIDGE)
|
||||
|
||||
void DeviceManager::initialize_driver(DeviceDriver::Type driver_type)
|
||||
{
|
||||
switch (driver_type)
|
||||
{
|
||||
case DeviceDriver::Type::DINPUT:
|
||||
device_driver_ = new DInputDevice();
|
||||
break;
|
||||
case DeviceDriver::Type::PS3:
|
||||
device_driver_ = new PS3Device();
|
||||
break;
|
||||
case DeviceDriver::Type::PSCLASSIC:
|
||||
device_driver_ = new PSClassicDevice();
|
||||
break;
|
||||
case DeviceDriver::Type::SWITCH:
|
||||
device_driver_ = new SwitchDevice();
|
||||
break;
|
||||
case DeviceDriver::Type::XINPUT:
|
||||
device_driver_ = new XInputDevice();
|
||||
break;
|
||||
case DeviceDriver::Type::XBOXOG:
|
||||
device_driver_ = new XboxOGDevice();
|
||||
break;
|
||||
case DeviceDriver::Type::XBOXOG_SB:
|
||||
device_driver_ = new XboxOGSBDevice();
|
||||
break;
|
||||
case DeviceDriver::Type::XBOXOG_XR:
|
||||
device_driver_ = new XboxOGXRDevice();
|
||||
break;
|
||||
case DeviceDriver::Type::WEBAPP:
|
||||
device_driver_ = new WebAppDevice();
|
||||
break;
|
||||
#if defined(CONFIG_EN_UART_BRIDGE)
|
||||
case DeviceDriver::Type::UART_BRIDGE:
|
||||
device_driver_ = new UARTBridgeDevice();
|
||||
break;
|
||||
#endif //defined(CONFIG_EN_UART_BRIDGE)
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
device_driver_->initialize();
|
||||
}
|
||||
31
Firmware/RP2040/src/USBDevice/DeviceManager.h
Normal file
31
Firmware/RP2040/src/USBDevice/DeviceManager.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef _DEVICE_MANAGER_H_
|
||||
#define _DEVICE_MANAGER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "USBDevice/DeviceDriver/DeviceDriver.h"
|
||||
|
||||
class DeviceManager
|
||||
{
|
||||
public:
|
||||
DeviceManager(DeviceManager const&) = delete;
|
||||
void operator=(DeviceManager const&) = delete;
|
||||
|
||||
static DeviceManager& get_instance()
|
||||
{
|
||||
static DeviceManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
//Must be called before any other method
|
||||
void initialize_driver(DeviceDriver::Type driver_type);
|
||||
|
||||
DeviceDriver* get_driver() { return device_driver_; }
|
||||
|
||||
private:
|
||||
DeviceManager() {}
|
||||
~DeviceManager() { delete device_driver_; }
|
||||
DeviceDriver* device_driver_{nullptr};
|
||||
};
|
||||
|
||||
#endif // _DEVICE_MANAGER_H_
|
||||
54
Firmware/RP2040/src/USBDevice/tud_callbacks.cpp
Normal file
54
Firmware/RP2040/src/USBDevice/tud_callbacks.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "class/hid/hid_device.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "USBDevice/DeviceManager.h"
|
||||
|
||||
const usbd_class_driver_t *usbd_app_driver_get_cb(uint8_t *driver_count)
|
||||
{
|
||||
*driver_count = 1;
|
||||
return DeviceManager::get_instance().get_driver()->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 DeviceManager::get_instance().get_driver()->get_report_cb(itf, report_id, report_type, buffer, reqlen);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
DeviceManager::get_instance().get_driver()->set_report_cb(itf, report_id, report_type, buffer, bufsize);
|
||||
tud_hid_report(report_id, buffer, bufsize);
|
||||
}
|
||||
|
||||
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return DeviceManager::get_instance().get_driver()->vendor_control_xfer_cb(rhport, stage, request);
|
||||
}
|
||||
|
||||
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
return DeviceManager::get_instance().get_driver()->get_descriptor_string_cb(index, langid);
|
||||
}
|
||||
|
||||
uint8_t const *tud_descriptor_device_cb()
|
||||
{
|
||||
return DeviceManager::get_instance().get_driver()->get_descriptor_device_cb();
|
||||
}
|
||||
|
||||
uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return DeviceManager::get_instance().get_driver()->get_hid_descriptor_report_cb(itf);
|
||||
}
|
||||
|
||||
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return DeviceManager::get_instance().get_driver()->get_descriptor_configuration_cb(index);
|
||||
}
|
||||
|
||||
uint8_t const* tud_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return DeviceManager::get_instance().get_driver()->get_descriptor_device_qualifier_cb();
|
||||
}
|
||||
170
Firmware/RP2040/src/USBHost/HIDParser/HIDJoystick.cpp
Normal file
170
Firmware/RP2040/src/USBHost/HIDParser/HIDJoystick.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
#include "USBHost/HIDParser/HIDJoystick.h"
|
||||
#include "USBHost/HIDParser/HIDUtils.h"
|
||||
#include <cstring>
|
||||
|
||||
/* ----------------------------------------------- */
|
||||
|
||||
static int32_t mapValue(int32_t value, int32_t in_min, int32_t in_max, int32_t out_min, int32_t out_max)
|
||||
{
|
||||
return (value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------- */
|
||||
|
||||
HIDJoystickData::HIDJoystickData() : index(0xFF),
|
||||
support(0),
|
||||
X(0),
|
||||
Y(0),
|
||||
Z(0),
|
||||
Rx(0),
|
||||
Ry(0),
|
||||
Rz(0),
|
||||
Slider(0),
|
||||
Dial(0),
|
||||
hat_switch(HIDJoystickHatSwitch::NEUTRAL),
|
||||
button_count(0)
|
||||
{
|
||||
memset(buttons, 0, sizeof(buttons));
|
||||
}
|
||||
|
||||
/* ----------------------------------------------- */
|
||||
|
||||
HIDJoystickData::~HIDJoystickData()
|
||||
{
|
||||
}
|
||||
|
||||
/* ----------------------------------------------- */
|
||||
|
||||
HIDJoystick::HIDJoystick(const std::shared_ptr<HIDReportDescriptor> &descriptor)
|
||||
{
|
||||
this->m_reports = descriptor->GetReports();
|
||||
}
|
||||
|
||||
/* ----------------------------------------------- */
|
||||
|
||||
HIDJoystick::~HIDJoystick()
|
||||
{
|
||||
}
|
||||
|
||||
/* ----------------------------------------------- */
|
||||
|
||||
bool HIDJoystick::isValid()
|
||||
{
|
||||
return getCount() > 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------- */
|
||||
|
||||
uint8_t HIDJoystick::getCount()
|
||||
{
|
||||
uint8_t count = 0;
|
||||
|
||||
for (auto report : this->m_reports)
|
||||
{
|
||||
if (report.report_type == HIDIOReportType::Joystick || report.report_type == HIDIOReportType::GamePad)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------- */
|
||||
|
||||
bool HIDJoystick::parseData(uint8_t *data, uint16_t datalen, HIDJoystickData *joystick_data)
|
||||
{
|
||||
bool found = false;
|
||||
uint8_t joystick_count = 0;
|
||||
|
||||
for (uint32_t i = 0; i < this->m_reports.size(); i++)
|
||||
{
|
||||
auto report = this->m_reports[i];
|
||||
|
||||
if (report.report_type != HIDIOReportType::Joystick && report.report_type != HIDIOReportType::GamePad)
|
||||
continue;
|
||||
|
||||
joystick_count += 1;
|
||||
|
||||
for (auto ioblock : report.inputs)
|
||||
{
|
||||
uint32_t bitOffset = 0;
|
||||
|
||||
for (auto input : ioblock.data)
|
||||
{
|
||||
uint32_t value = HIDUtils::readBitsLE(data, bitOffset, input.size);
|
||||
bitOffset += input.size;
|
||||
|
||||
if (bitOffset > (datalen * (uint32_t)8))
|
||||
return false; // Out of range
|
||||
|
||||
if (input.type == HIDIOType::ReportId)
|
||||
{
|
||||
if (value != input.id)
|
||||
break; // Not the correct report id
|
||||
}
|
||||
|
||||
found = true;
|
||||
joystick_data->index = joystick_count - 1;
|
||||
|
||||
if (input.type == HIDIOType::Button)
|
||||
{
|
||||
if (input.id >= MAX_BUTTONS)
|
||||
return false;
|
||||
|
||||
joystick_data->buttons[input.id] = value;
|
||||
if (joystick_data->button_count < input.id)
|
||||
joystick_data->button_count = input.id;
|
||||
}
|
||||
else if (input.type == HIDIOType::X)
|
||||
{
|
||||
joystick_data->support |= JOYSTICK_SUPPORT_X;
|
||||
joystick_data->X = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
|
||||
}
|
||||
else if (input.type == HIDIOType::Y)
|
||||
{
|
||||
joystick_data->support |= JOYSTICK_SUPPORT_Y;
|
||||
joystick_data->Y = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
|
||||
}
|
||||
else if (input.type == HIDIOType::Z)
|
||||
{
|
||||
joystick_data->support |= JOYSTICK_SUPPORT_Z;
|
||||
joystick_data->Z = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
|
||||
}
|
||||
else if (input.type == HIDIOType::Rx)
|
||||
{
|
||||
joystick_data->support |= JOYSTICK_SUPPORT_Rx;
|
||||
joystick_data->Rx = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
|
||||
}
|
||||
else if (input.type == HIDIOType::Ry)
|
||||
{
|
||||
joystick_data->support |= JOYSTICK_SUPPORT_Ry;
|
||||
joystick_data->Ry = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
|
||||
}
|
||||
else if (input.type == HIDIOType::Rz)
|
||||
{
|
||||
joystick_data->support |= JOYSTICK_SUPPORT_Rz;
|
||||
joystick_data->Rz = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
|
||||
}
|
||||
else if (input.type == HIDIOType::Slider)
|
||||
{
|
||||
joystick_data->support |= JOYSTICK_SUPPORT_Slider;
|
||||
joystick_data->Slider = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
|
||||
}
|
||||
else if (input.type == HIDIOType::Dial)
|
||||
{
|
||||
joystick_data->support |= JOYSTICK_SUPPORT_Dial;
|
||||
joystick_data->Dial = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
|
||||
}
|
||||
else if (input.type == HIDIOType::HatSwitch)
|
||||
{
|
||||
joystick_data->support |= JOYSTICK_SUPPORT_HatSwitch;
|
||||
joystick_data->hat_switch = (HIDJoystickHatSwitch)value;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
68
Firmware/RP2040/src/USBHost/HIDParser/HIDJoystick.h
Normal file
68
Firmware/RP2040/src/USBHost/HIDParser/HIDJoystick.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "USBHost/HIDParser/HIDReportDescriptor.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#define MAX_BUTTONS 32
|
||||
|
||||
enum class HIDJoystickHatSwitch
|
||||
{
|
||||
UP = 0,
|
||||
UP_RIGHT = 1,
|
||||
RIGHT = 2,
|
||||
DOWN_RIGHT = 3,
|
||||
DOWN = 4,
|
||||
DOWN_LEFT = 5,
|
||||
LEFT = 6,
|
||||
UP_LEFT = 7,
|
||||
NEUTRAL = 8
|
||||
};
|
||||
|
||||
#define JOYSTICK_SUPPORT_X 0x0001
|
||||
#define JOYSTICK_SUPPORT_Y 0x0002
|
||||
#define JOYSTICK_SUPPORT_Z 0x0004
|
||||
#define JOYSTICK_SUPPORT_Rx 0x0008
|
||||
#define JOYSTICK_SUPPORT_Ry 0x0010
|
||||
#define JOYSTICK_SUPPORT_Rz 0x0020
|
||||
#define JOYSTICK_SUPPORT_Slider 0x0040
|
||||
#define JOYSTICK_SUPPORT_Dial 0x0080
|
||||
#define JOYSTICK_SUPPORT_HatSwitch 0x0100
|
||||
|
||||
class HIDJoystickData
|
||||
{
|
||||
public:
|
||||
HIDJoystickData();
|
||||
~HIDJoystickData();
|
||||
|
||||
uint8_t index;
|
||||
|
||||
uint16_t support;
|
||||
|
||||
int16_t X; //-32768 to 32767
|
||||
int16_t Y; //-32768 to 32767
|
||||
int16_t Z; //-32768 to 32767
|
||||
int16_t Rx; //-32768 to 32767
|
||||
int16_t Ry; //-32768 to 32767
|
||||
int16_t Rz; //-32768 to 32767
|
||||
int16_t Slider; //-32768 to 32767
|
||||
int16_t Dial; //-32768 to 32767
|
||||
|
||||
HIDJoystickHatSwitch hat_switch;
|
||||
|
||||
uint8_t button_count;
|
||||
uint8_t buttons[MAX_BUTTONS];
|
||||
};
|
||||
|
||||
class HIDJoystick
|
||||
{
|
||||
public:
|
||||
HIDJoystick(const std::shared_ptr<HIDReportDescriptor> &descriptor);
|
||||
~HIDJoystick();
|
||||
|
||||
bool isValid();
|
||||
uint8_t getCount();
|
||||
|
||||
bool parseData(uint8_t *data, uint16_t datalen, HIDJoystickData *joystick_data);
|
||||
|
||||
private:
|
||||
std::vector<HIDIOReport> m_reports;
|
||||
};
|
||||
141
Firmware/RP2040/src/USBHost/HIDParser/HIDReportDescriptor.cpp
Normal file
141
Firmware/RP2040/src/USBHost/HIDParser/HIDReportDescriptor.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "USBHost/HIDParser/HIDReportDescriptor.h"
|
||||
#include "USBHost/HIDParser/HIDReportDescriptorElements.h"
|
||||
#include "USBHost/HIDParser/HIDReportDescriptorUsages.h"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
// https://github.com/pasztorpisti/hid-report-parser/blob/master/src/hid_report_parser.cpp
|
||||
// https://docs.kernel.org/hid/hidintro.html
|
||||
|
||||
HIDInputOutput::HIDInputOutput(HIDIOType type, uint32_t size, uint32_t id) : type(type),
|
||||
sub_type(0),
|
||||
size(size),
|
||||
id(id),
|
||||
logical_min(0),
|
||||
logical_max(0),
|
||||
physical_min(0),
|
||||
physical_max(0),
|
||||
unit(0),
|
||||
unit_exponent(0)
|
||||
{
|
||||
}
|
||||
|
||||
HIDInputOutput::~HIDInputOutput()
|
||||
{
|
||||
}
|
||||
|
||||
HIDInputOutput::HIDInputOutput(const HIDUsage &usage, uint32_t idx) : type(HIDIOType::Unknown),
|
||||
sub_type(0),
|
||||
size(usage.property.size),
|
||||
id(0),
|
||||
logical_min(usage.property.logical_min),
|
||||
logical_max(usage.property.logical_max),
|
||||
physical_min(usage.property.physical_min),
|
||||
physical_max(usage.property.physical_max),
|
||||
unit(usage.property.unit),
|
||||
unit_exponent(usage.property.unit_exponent)
|
||||
{
|
||||
if (usage.type == HIDUsageType::GenericDesktop)
|
||||
{
|
||||
if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::X)
|
||||
this->type = HIDIOType::X;
|
||||
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Y)
|
||||
this->type = HIDIOType::Y;
|
||||
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Z)
|
||||
this->type = HIDIOType::Z;
|
||||
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Rx)
|
||||
this->type = HIDIOType::Rx;
|
||||
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Ry)
|
||||
this->type = HIDIOType::Ry;
|
||||
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Rz)
|
||||
this->type = HIDIOType::Rz;
|
||||
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Slider)
|
||||
this->type = HIDIOType::Slider;
|
||||
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Dial)
|
||||
this->type = HIDIOType::Dial;
|
||||
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::HatSwitch)
|
||||
this->type = HIDIOType::HatSwitch;
|
||||
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Wheel)
|
||||
this->type = HIDIOType::Wheel;
|
||||
}
|
||||
else if (usage.type == HIDUsageType::Button)
|
||||
this->type = HIDIOType::Button;
|
||||
else if (usage.type == HIDUsageType::ReportId)
|
||||
this->type = HIDIOType::ReportId;
|
||||
else if (usage.type == HIDUsageType::Padding)
|
||||
this->type = HIDIOType::Padding;
|
||||
else if (usage.type == HIDUsageType::VendorDefined)
|
||||
{
|
||||
this->type = HIDIOType::VendorDefined;
|
||||
this->sub_type = usage.sub_type;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->type = HIDIOType::Unknown;
|
||||
this->sub_type = usage.sub_type;
|
||||
}
|
||||
this->id = usage.usage_min + idx;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
HIDReportDescriptor::HIDReportDescriptor()
|
||||
{
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
HIDReportDescriptor::HIDReportDescriptor(const uint8_t *hid_report_data, uint16_t hid_report_data_len)
|
||||
{
|
||||
parse(hid_report_data, hid_report_data_len);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
HIDReportDescriptor::~HIDReportDescriptor()
|
||||
{
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
void HIDReportDescriptor::parse(const uint8_t *hid_report_data, uint16_t hid_report_data_len)
|
||||
{
|
||||
HIDReportDescriptorElements hid_report_elements = HIDReportDescriptorElements(hid_report_data, hid_report_data_len);
|
||||
std::vector<HIDReport> hid_report_usage = HIDReportDescriptorUsages::parse(hid_report_elements);
|
||||
for (auto report : hid_report_usage)
|
||||
{
|
||||
m_reports.push_back(HIDIOReport((HIDIOReportType)report.usages[0].sub_type));
|
||||
|
||||
for (size_t k = 1; k < report.usages.size(); k++)
|
||||
{
|
||||
std::vector<HIDIOBlock> *ioblocks = NULL;
|
||||
HIDUsage &usage = report.usages[k];
|
||||
|
||||
if (usage.io_type == HIDUsageIOType::Input)
|
||||
ioblocks = &m_reports.back().inputs;
|
||||
else if (usage.io_type == HIDUsageIOType::Output)
|
||||
ioblocks = &m_reports.back().outputs;
|
||||
else if (usage.io_type == HIDUsageIOType::Feature)
|
||||
ioblocks = &m_reports.back().features;
|
||||
else
|
||||
continue;
|
||||
|
||||
for (uint32_t i = 0; i < usage.property.count; i++)
|
||||
{
|
||||
HIDInputOutput io(usage, i);
|
||||
|
||||
//We need to create a new block everytime we meet a ReportId and if there is no block
|
||||
if (io.type == HIDIOType::ReportId || ioblocks->size() == 0)
|
||||
ioblocks->push_back(HIDIOBlock());
|
||||
|
||||
ioblocks->back().data.push_back(io);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(m_reports.size() > 0);
|
||||
}
|
||||
101
Firmware/RP2040/src/USBHost/HIDParser/HIDReportDescriptor.h
Normal file
101
Firmware/RP2040/src/USBHost/HIDParser/HIDReportDescriptor.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
enum class HIDIOType
|
||||
{
|
||||
Unknown = 0x00,
|
||||
ReportId,
|
||||
VendorDefined,
|
||||
Padding,
|
||||
Button,
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
Rx,
|
||||
Ry,
|
||||
Rz,
|
||||
Slider,
|
||||
Dial,
|
||||
HatSwitch,
|
||||
Wheel
|
||||
};
|
||||
class HIDUsage;
|
||||
|
||||
class HIDInputOutput
|
||||
{
|
||||
public:
|
||||
HIDInputOutput(HIDIOType type = HIDIOType::Unknown, uint32_t size=0, uint32_t id=0);
|
||||
~HIDInputOutput();
|
||||
HIDInputOutput(const HIDUsage &usage, uint32_t idx);
|
||||
|
||||
HIDIOType type; //Type (Button, X, Y, Hat switch, Padding, etc.)
|
||||
uint32_t sub_type; //Sub type (Usefull for vendor defined and non handled types)
|
||||
uint32_t size; //Size of the data in bits
|
||||
uint32_t id; //Index of the input in the report
|
||||
|
||||
int32_t logical_min;
|
||||
int32_t logical_max;
|
||||
int32_t physical_min;
|
||||
int32_t physical_max;
|
||||
uint32_t unit;
|
||||
uint32_t unit_exponent;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
//https://usb.org/sites/default/files/hut1_2.pdf p31
|
||||
typedef enum class HIDIOReportType
|
||||
{
|
||||
Unknown = 0x00,
|
||||
Pointer = 0x01,
|
||||
Mouse = 0x02,
|
||||
Joystick = 0x04,
|
||||
GamePad = 0x05,
|
||||
Keyboard = 0x06,
|
||||
Keypad = 0x07,
|
||||
MultiAxis = 0x08,
|
||||
Tablet = 0x09,
|
||||
|
||||
MAX = 0x2F
|
||||
} HIDIOReportType;
|
||||
|
||||
class HIDIOBlock
|
||||
{
|
||||
public:
|
||||
HIDIOBlock() {}
|
||||
~HIDIOBlock() {}
|
||||
|
||||
std::vector<HIDInputOutput> data;
|
||||
};
|
||||
|
||||
class HIDIOReport
|
||||
{
|
||||
public:
|
||||
HIDIOReport(HIDIOReportType report_type = HIDIOReportType::Unknown) :
|
||||
report_type(report_type)
|
||||
{}
|
||||
|
||||
HIDIOReportType report_type;
|
||||
std::vector<HIDIOBlock> inputs;
|
||||
std::vector<HIDIOBlock> outputs;
|
||||
std::vector<HIDIOBlock> features;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
class HIDReportDescriptor
|
||||
{
|
||||
public:
|
||||
HIDReportDescriptor();
|
||||
HIDReportDescriptor(const uint8_t *hid_report_data, uint16_t hid_report_data_size);
|
||||
~HIDReportDescriptor();
|
||||
|
||||
std::vector<HIDIOReport> GetReports() const { return m_reports; }
|
||||
|
||||
private:
|
||||
void parse(const uint8_t *hid_report_data, uint16_t hid_report_data_len);
|
||||
|
||||
std::vector<HIDIOReport> m_reports;
|
||||
};
|
||||
@@ -0,0 +1,153 @@
|
||||
#include "USBHost/HIDParser/HIDReportDescriptorElements.h"
|
||||
#include <cstring>
|
||||
|
||||
// https://docs.kernel.org/hid/hidreport-parsing.html
|
||||
#define HID_FUNC_TYPE_MASK 0xFC
|
||||
#define HID_TYPE_MASK 0x0C
|
||||
#define HID_LENGTH_MASK 0x03
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDElement::HIDElement() : type(HIDElementType::HID_UNKNOWN),
|
||||
data_size(0)
|
||||
{
|
||||
memset(data, 0x00, sizeof(data));
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDElement::~HIDElement()
|
||||
{
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDElement::HIDElement(HIDElementType type, const uint8_t *data, uint8_t data_size)
|
||||
{
|
||||
this->type = type;
|
||||
memset(this->data, 0x00, sizeof(this->data));
|
||||
memcpy(this->data, data, data_size);
|
||||
this->data_size = data_size;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
uint32_t HIDElement::GetSize() const
|
||||
{
|
||||
return data_size;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDElementType HIDElement::GetType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
uint32_t HIDElement::GetValueUint32() const
|
||||
{
|
||||
return data[0] | ((uint32_t)data[1] << 8) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
int32_t HIDElement::GetValueInt32() const
|
||||
{
|
||||
if (this->data_size == 1)
|
||||
return (int8_t)data[0];
|
||||
else if (this->data_size == 2)
|
||||
return (int16_t)(data[0] | ((uint16_t)data[1] << 8));
|
||||
else if (this->data_size == 4)
|
||||
return (int32_t)(data[0] | ((uint32_t)data[1] << 8) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDReportDescriptorElements::HIDReportDescriptorElements(const uint8_t *hid_report_data, uint16_t hid_report_data_len):
|
||||
hid_report_data(hid_report_data),
|
||||
hid_report_data_len(hid_report_data_len)
|
||||
{
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDReportDescriptorElements::~HIDReportDescriptorElements()
|
||||
{
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDReportDescriptorElements::Iterator HIDReportDescriptorElements::begin() const
|
||||
{
|
||||
return HIDReportDescriptorElements::Iterator(hid_report_data, hid_report_data_len);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDReportDescriptorElements::Iterator HIDReportDescriptorElements::end() const
|
||||
{
|
||||
return HIDReportDescriptorElements::Iterator(hid_report_data, hid_report_data_len, hid_report_data_len);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDReportDescriptorElements::Iterator::Iterator(const uint8_t* hid_report_data, uint16_t hid_report_data_len, uint16_t offset) :
|
||||
hid_report_data(hid_report_data),
|
||||
hid_report_data_len(hid_report_data_len),
|
||||
offset(offset)
|
||||
{
|
||||
if (offset < hid_report_data_len)
|
||||
parse_current_element();
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDElement& HIDReportDescriptorElements::Iterator::operator*()
|
||||
{
|
||||
return current_element;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDElement* HIDReportDescriptorElements::Iterator::operator->()
|
||||
{
|
||||
return ¤t_element;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDReportDescriptorElements::Iterator& HIDReportDescriptorElements::Iterator::operator++()
|
||||
{
|
||||
offset += 1 + current_element_length;
|
||||
|
||||
if (offset < hid_report_data_len)
|
||||
parse_current_element();
|
||||
else
|
||||
offset = hid_report_data_len; // End condition
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
bool HIDReportDescriptorElements::Iterator::operator!=(const Iterator& other) const
|
||||
{
|
||||
return offset != other.offset;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
void HIDReportDescriptorElements::Iterator::parse_current_element()
|
||||
{
|
||||
uint8_t type = hid_report_data[offset];
|
||||
uint8_t datalen = type & HID_LENGTH_MASK;
|
||||
if (datalen == 3)
|
||||
datalen = 4;
|
||||
|
||||
current_element = HIDElement((HIDElementType)(type & HID_FUNC_TYPE_MASK), &hid_report_data[offset + 1], datalen);
|
||||
current_element_length = datalen;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
typedef enum class HIDElementType
|
||||
{
|
||||
HID_UNKNOWN = 0x00,
|
||||
HID_INPUT = 0x80,
|
||||
HID_OUTPUT = 0x90,
|
||||
HID_FEATURE = 0xb0,
|
||||
HID_COLLECTION = 0xa0,
|
||||
HID_END_COLLECTION = 0xc0,
|
||||
HID_USAGE_PAGE = 0x04,
|
||||
HID_LOGICAL_MINIMUM = 0x14,
|
||||
HID_LOGICAL_MAXIMUM = 0x24,
|
||||
HID_PHYSICAL_MINIMUM = 0x34,
|
||||
HID_PHYSICAL_MAXIMUM = 0x44,
|
||||
HID_UNIT_EXPONENT = 0x54,
|
||||
HID_UNIT = 0x64,
|
||||
HID_REPORT_SIZE = 0x74,
|
||||
HID_REPORT_ID = 0x84,
|
||||
HID_REPORT_COUNT = 0x94,
|
||||
HID_PUSH = 0xa4,
|
||||
HID_POP = 0xb4,
|
||||
HID_USAGE = 0x08,
|
||||
HID_USAGE_MINIMUM = 0x18,
|
||||
HID_USAGE_MAXIMUM = 0x28,
|
||||
HID_DESIGNATOR_INDEX = 0x38,
|
||||
HID_DESIGNATOR_MINIMUM = 0x48,
|
||||
HID_DESIGNATOR_MAXIMUM = 0x58,
|
||||
HID_STRING_INDEX = 0x78,
|
||||
HID_STRING_MINIMUM = 0x88,
|
||||
HID_STRING_MAXIMUM = 0x98,
|
||||
HID_DELIMITER = 0xa8
|
||||
} HIDElementType;
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
class HIDElement
|
||||
{
|
||||
public:
|
||||
HIDElement();
|
||||
HIDElement(HIDElementType type, const uint8_t *data, uint8_t data_size);
|
||||
~HIDElement();
|
||||
|
||||
uint32_t GetSize() const;
|
||||
HIDElementType GetType() const;
|
||||
uint32_t GetValueUint32() const;
|
||||
int32_t GetValueInt32() const;
|
||||
|
||||
private:
|
||||
HIDElementType type;
|
||||
uint8_t data[4];
|
||||
uint8_t data_size;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
class HIDReportDescriptorElements
|
||||
{
|
||||
public:
|
||||
HIDReportDescriptorElements(const uint8_t *hid_report_data, uint16_t hid_report_data_len);
|
||||
~HIDReportDescriptorElements();
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(const uint8_t* hid_report_data, uint16_t hid_report_data_len, uint16_t offset = 0);
|
||||
HIDElement& operator*();
|
||||
HIDElement* operator->();
|
||||
Iterator& operator++();
|
||||
|
||||
bool operator!=(const Iterator& other) const;
|
||||
|
||||
private:
|
||||
void parse_current_element() ;
|
||||
|
||||
const uint8_t* hid_report_data;
|
||||
uint16_t hid_report_data_len;
|
||||
uint16_t offset;
|
||||
HIDElement current_element;
|
||||
uint8_t current_element_length;
|
||||
};
|
||||
|
||||
Iterator begin() const;
|
||||
Iterator end() const;
|
||||
|
||||
private:
|
||||
const uint8_t *hid_report_data;
|
||||
uint16_t hid_report_data_len;
|
||||
};
|
||||
@@ -0,0 +1,269 @@
|
||||
#include "USBHost/HIDParser/HIDReportDescriptorUsages.h"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
//---------------USAGE_PAGE-----------------
|
||||
#define USAGE_PAGE_GenericDesktop 0x01
|
||||
#define USAGE_PAGE_Simulation 0x02
|
||||
#define USAGE_PAGE_VR 0x03
|
||||
#define USAGE_PAGE_Sport 0x04
|
||||
#define USAGE_PAGE_Game 0x05
|
||||
#define USAGE_PAGE_GenericDevice 0x06
|
||||
#define USAGE_PAGE_Keyboard 0x07
|
||||
#define USAGE_PAGE_LEDs 0x08
|
||||
#define USAGE_PAGE_Button 0x09
|
||||
#define USAGE_PAGE_Ordinal 0x0A
|
||||
#define USAGE_PAGE_Telephony 0x0B
|
||||
#define USAGE_PAGE_Consumer 0x0C
|
||||
#define USAGE_PAGE_VendorDefined 0xFF00
|
||||
|
||||
//---------------USAGE-----------------
|
||||
#define USAGE_X 0x30
|
||||
#define USAGE_Y 0x31
|
||||
#define USAGE_Z 0x32
|
||||
#define USAGE_Rx 0x33
|
||||
#define USAGE_Ry 0x34
|
||||
#define USAGE_Rz 0x35
|
||||
#define USAGE_Slider 0x36
|
||||
#define USAGE_Dial 0x37
|
||||
#define USAGE_Wheel 0x38
|
||||
#define USAGE_Hat_switch 0x39
|
||||
|
||||
//---------------INPUT-----------------
|
||||
#define INPUT_Const 0x01
|
||||
#define INPUT_Var 0x02
|
||||
#define INPUT_Rel 0x04
|
||||
#define INPUT_Wrap 0x08
|
||||
#define INPUT_NLin 0x10
|
||||
#define INPUT_NPrf 0x20
|
||||
#define INPUT_Null 0x40
|
||||
#define INPUT_Vol 0x80
|
||||
|
||||
//---------------COLLECTION-----------------
|
||||
#define HID_COLLECTION_PHYSICAL 0x00
|
||||
#define HID_COLLECTION_APPLICATION 0x01
|
||||
#define HID_COLLECTION_LOGICAL 0x02
|
||||
#define HID_COLLECTION_REPORT 0x03
|
||||
#define HID_COLLECTION_NAMED_ARRAY 0x04
|
||||
#define HID_COLLECTION_USAGE_SWITCH 0x05
|
||||
#define HID_COLLECTION_USAGE_MODIFIER 0x06
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDUsage::HIDUsage(HIDUsageType type, uint32_t sub_type, HIDUsageIOType io_type, HIDProperty property) : type(type),
|
||||
sub_type(sub_type),
|
||||
usage_min(0),
|
||||
usage_max(0),
|
||||
io_type(io_type),
|
||||
property(property)
|
||||
{
|
||||
if (sub_type != 0)
|
||||
{
|
||||
usage_min = sub_type;
|
||||
usage_max = sub_type;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDUsage::~HIDUsage()
|
||||
{
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDProperty::HIDProperty(uint32_t size, uint32_t count) : logical_min(0),
|
||||
logical_max(0),
|
||||
physical_min(0),
|
||||
physical_max(0),
|
||||
unit(0),
|
||||
unit_exponent(0),
|
||||
size(size),
|
||||
count(count)
|
||||
{
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDProperty::~HIDProperty()
|
||||
{
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
bool HIDProperty::is_valid()
|
||||
{
|
||||
return size != 0 && count != 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
HIDUsageType convert_usage_page(uint32_t usage_page)
|
||||
{
|
||||
switch (usage_page)
|
||||
{
|
||||
case USAGE_PAGE_Button:
|
||||
return HIDUsageType::Button;
|
||||
case USAGE_PAGE_GenericDesktop:
|
||||
return HIDUsageType::GenericDesktop;
|
||||
case USAGE_PAGE_VendorDefined:
|
||||
return HIDUsageType::VendorDefined;
|
||||
default:
|
||||
return HIDUsageType::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
std::vector<HIDReport> HIDReportDescriptorUsages::parse(const HIDReportDescriptorElements &elements)
|
||||
{
|
||||
HIDProperty current_property;
|
||||
std::vector<HIDUsage> current_usages;
|
||||
HIDUsageType current_usage_page_type = HIDUsageType::Unknown;
|
||||
std::vector<HIDReport> report;
|
||||
uint8_t current_report_id = 0;
|
||||
|
||||
for (const HIDElement &element : elements)
|
||||
{
|
||||
switch (element.GetType())
|
||||
{
|
||||
case HIDElementType::HID_USAGE_PAGE:
|
||||
current_usage_page_type = convert_usage_page(element.GetValueUint32());
|
||||
break;
|
||||
|
||||
case HIDElementType::HID_USAGE:
|
||||
current_usages.push_back(HIDUsage(current_usage_page_type, element.GetValueUint32()));
|
||||
break;
|
||||
|
||||
case HIDElementType::HID_USAGE_MAXIMUM:
|
||||
case HIDElementType::HID_USAGE_MINIMUM:
|
||||
{
|
||||
if (current_usages.size() == 0)
|
||||
current_usages.push_back(HIDUsage(current_usage_page_type));
|
||||
|
||||
for (HIDUsage &usage : current_usages)
|
||||
{
|
||||
if (element.GetType() == HIDElementType::HID_USAGE_MINIMUM)
|
||||
usage.usage_min = element.GetValueUint32();
|
||||
else if (element.GetType() == HIDElementType::HID_USAGE_MAXIMUM)
|
||||
usage.usage_max = element.GetValueUint32();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case HIDElementType::HID_REPORT_ID:
|
||||
current_report_id = element.GetValueUint32();
|
||||
break;
|
||||
|
||||
case HIDElementType::HID_LOGICAL_MINIMUM:
|
||||
current_property.logical_min = element.GetValueInt32();
|
||||
current_property.logical_min_unsigned = element.GetValueUint32();
|
||||
break;
|
||||
|
||||
case HIDElementType::HID_LOGICAL_MAXIMUM:
|
||||
current_property.logical_max = element.GetValueInt32();
|
||||
current_property.logical_max_unsigned = element.GetValueUint32();
|
||||
break;
|
||||
|
||||
case HIDElementType::HID_PHYSICAL_MINIMUM:
|
||||
current_property.physical_min = element.GetValueInt32();
|
||||
current_property.physical_min_unsigned = element.GetValueUint32();
|
||||
break;
|
||||
|
||||
case HIDElementType::HID_PHYSICAL_MAXIMUM:
|
||||
current_property.physical_max = element.GetValueInt32();
|
||||
current_property.physical_max_unsigned = element.GetValueUint32();
|
||||
break;
|
||||
|
||||
case HIDElementType::HID_UNIT_EXPONENT:
|
||||
current_property.unit_exponent = element.GetValueUint32();
|
||||
break;
|
||||
|
||||
case HIDElementType::HID_UNIT:
|
||||
current_property.unit = element.GetValueUint32();
|
||||
break;
|
||||
|
||||
case HIDElementType::HID_REPORT_SIZE:
|
||||
current_property.size = element.GetValueUint32();
|
||||
break;
|
||||
|
||||
case HIDElementType::HID_REPORT_COUNT:
|
||||
current_property.count = element.GetValueUint32();
|
||||
break;
|
||||
|
||||
case HIDElementType::HID_INPUT:
|
||||
case HIDElementType::HID_OUTPUT:
|
||||
case HIDElementType::HID_FEATURE:
|
||||
{
|
||||
if (current_usages.size() == 0)
|
||||
current_usages.push_back(HIDUsage(HIDUsageType::Padding));
|
||||
|
||||
HIDUsageIOType io_type = HIDUsageIOType::None;
|
||||
if (element.GetType() == HIDElementType::HID_INPUT)
|
||||
io_type = HIDUsageIOType::Input;
|
||||
else if (element.GetType() == HIDElementType::HID_OUTPUT)
|
||||
io_type = HIDUsageIOType::Output;
|
||||
else if (element.GetType() == HIDElementType::HID_FEATURE)
|
||||
io_type = HIDUsageIOType::Feature;
|
||||
|
||||
for (HIDUsage &usage : current_usages)
|
||||
{
|
||||
//Fix bug on few controllers, that provide incorrect "Unsigned" values
|
||||
if (current_property.logical_max < current_property.logical_min)
|
||||
current_property.logical_max = (int32_t)current_property.logical_max_unsigned;
|
||||
|
||||
if (current_property.physical_max < current_property.physical_min)
|
||||
current_property.physical_max = (int32_t)current_property.physical_max_unsigned;
|
||||
|
||||
usage.io_type = io_type;
|
||||
usage.property = current_property;
|
||||
usage.property.count = (current_property.count / (uint32_t)current_usages.size());
|
||||
}
|
||||
|
||||
if (current_report_id != 0)
|
||||
{
|
||||
current_usages.insert(current_usages.begin(), HIDUsage(HIDUsageType::ReportId, current_report_id, io_type, HIDProperty(8, 1)));
|
||||
current_report_id = 0;
|
||||
}
|
||||
|
||||
report.back().usages.insert(report.back().usages.end(), current_usages.begin(), current_usages.end());
|
||||
current_usages.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
// For now collections are ignored
|
||||
case HIDElementType::HID_COLLECTION:
|
||||
{
|
||||
if (element.GetValueUint32() == HID_COLLECTION_APPLICATION)
|
||||
report.push_back(HIDReport());
|
||||
|
||||
report.back().usages.insert(report.back().usages.end(), current_usages.begin(), current_usages.end());
|
||||
current_usages.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
case HIDElementType::HID_END_COLLECTION:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case HIDElementType::HID_UNKNOWN:
|
||||
case HIDElementType::HID_PUSH:
|
||||
case HIDElementType::HID_POP:
|
||||
case HIDElementType::HID_DELIMITER:
|
||||
case HIDElementType::HID_DESIGNATOR_INDEX:
|
||||
case HIDElementType::HID_DESIGNATOR_MINIMUM:
|
||||
case HIDElementType::HID_DESIGNATOR_MAXIMUM:
|
||||
case HIDElementType::HID_STRING_INDEX:
|
||||
case HIDElementType::HID_STRING_MINIMUM:
|
||||
case HIDElementType::HID_STRING_MAXIMUM:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return report;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
#include "USBHost/HIDParser/HIDReportDescriptorElements.h"
|
||||
#include <vector>
|
||||
|
||||
enum class HIDUsageIOType
|
||||
{
|
||||
None = 0x00,
|
||||
Input,
|
||||
Output,
|
||||
Feature
|
||||
};
|
||||
|
||||
enum class HIDUsageType
|
||||
{
|
||||
Unknown = 0x00,
|
||||
ReportId,
|
||||
Padding,
|
||||
Button,
|
||||
GenericDesktop,
|
||||
VendorDefined
|
||||
};
|
||||
|
||||
enum class HIDUsageGenericDesktopSubType
|
||||
{
|
||||
Pointer = 0x01,
|
||||
Mouse = 0x02,
|
||||
Joystick = 0x04,
|
||||
GamePad = 0x05,
|
||||
Keyboard = 0x06,
|
||||
Keypad = 0x07,
|
||||
MultiAxisController = 0x08,
|
||||
|
||||
ReportTypeEnd = 0x1F,
|
||||
|
||||
X = 0x30,
|
||||
Y = 0x31,
|
||||
Z = 0x32,
|
||||
Rx = 0x33,
|
||||
Ry = 0x34,
|
||||
Rz = 0x35,
|
||||
Slider = 0x36,
|
||||
Dial = 0x37,
|
||||
Wheel = 0x38,
|
||||
HatSwitch = 0x39,
|
||||
};
|
||||
|
||||
class HIDProperty
|
||||
{
|
||||
public:
|
||||
HIDProperty(uint32_t size=0, uint32_t count=0);
|
||||
virtual ~HIDProperty();
|
||||
|
||||
virtual bool is_valid();
|
||||
|
||||
int32_t logical_min;
|
||||
uint32_t logical_min_unsigned;
|
||||
|
||||
int32_t logical_max;
|
||||
uint32_t logical_max_unsigned;
|
||||
|
||||
int32_t physical_min;
|
||||
uint32_t physical_min_unsigned;
|
||||
|
||||
int32_t physical_max;
|
||||
uint32_t physical_max_unsigned;
|
||||
|
||||
uint32_t unit;
|
||||
uint32_t unit_exponent;
|
||||
uint32_t size; //Size of the data in bits
|
||||
uint32_t count; //Number of data items
|
||||
};
|
||||
|
||||
class HIDUsage
|
||||
{
|
||||
public:
|
||||
/// @brief
|
||||
/// @param type
|
||||
/// @param sub_type will depend on the type, for example, if type is GenericDesktop, sub_type will be HIDUsageGenericDesktopSubType etc.
|
||||
/// @param property
|
||||
HIDUsage(HIDUsageType type, uint32_t sub_type = 0, HIDUsageIOType io_type = HIDUsageIOType::None, HIDProperty property = HIDProperty());
|
||||
~HIDUsage();
|
||||
|
||||
HIDUsageType type; //Input type (Button, X, Y, Hat switch, Padding, etc.)
|
||||
uint32_t sub_type; //Sub type (Button number, etc.)
|
||||
uint32_t usage_min;
|
||||
uint32_t usage_max;
|
||||
HIDUsageIOType io_type;
|
||||
HIDProperty property;
|
||||
};
|
||||
|
||||
class HIDReport
|
||||
{
|
||||
public:
|
||||
std::vector<HIDUsage> usages;
|
||||
};
|
||||
|
||||
class HIDReportDescriptorUsages
|
||||
{
|
||||
public:
|
||||
static std::vector<HIDReport> parse(const HIDReportDescriptorElements &elements);
|
||||
};
|
||||
26
Firmware/RP2040/src/USBHost/HIDParser/HIDUtils.cpp
Normal file
26
Firmware/RP2040/src/USBHost/HIDParser/HIDUtils.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "USBHost/HIDParser/HIDUtils.h"
|
||||
|
||||
uint32_t HIDUtils::readBitsLE(uint8_t *buffer, uint32_t bitOffset, uint32_t bitLength) {
|
||||
// Calculate the starting byte index and bit index within that byte
|
||||
uint32_t byteIndex = bitOffset / 8;
|
||||
uint32_t bitIndex = bitOffset % 8; // Little endian, LSB is at index 0
|
||||
|
||||
uint32_t result = 0;
|
||||
|
||||
for (uint32_t i = 0; i < bitLength; ++i) {
|
||||
// Check if we need to move to the next byte
|
||||
if (bitIndex > 7) {
|
||||
++byteIndex;
|
||||
bitIndex = 0;
|
||||
}
|
||||
|
||||
// Get the bit at the current position and add it to the result
|
||||
uint8_t bit = (buffer[byteIndex] >> bitIndex) & 0x01;
|
||||
result |= (bit << i);
|
||||
|
||||
// Move to the next bit
|
||||
++bitIndex;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
11
Firmware/RP2040/src/USBHost/HIDParser/HIDUtils.h
Normal file
11
Firmware/RP2040/src/USBHost/HIDParser/HIDUtils.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
class HIDUtils
|
||||
{
|
||||
public:
|
||||
HIDUtils() {}
|
||||
~HIDUtils() {}
|
||||
|
||||
static uint32_t readBitsLE(uint8_t *buffer, uint32_t bitOffset, uint32_t bitLength);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user