Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea14d683ad | ||
|
|
8ef8d8daf6 | ||
|
|
62a22d26de | ||
|
|
658eb1dd39 | ||
|
|
0693a873b7 | ||
|
|
007cd7fb72 | ||
|
|
90a931a118 | ||
|
|
43bb4408af | ||
|
|
12654e351c | ||
|
|
9cf3f0cbb0 | ||
|
|
380bbe7cf7 | ||
|
|
b3bcbff50a | ||
|
|
7cb01051c3 | ||
|
|
c4ebea1d01 | ||
|
|
de3f25d183 | ||
|
|
e4269d441d | ||
|
|
10203c814b | ||
|
|
404997686f | ||
|
|
8e53aa5d6b | ||
|
|
5599f2d834 | ||
|
|
6ed1026d72 | ||
|
|
9d8d5dfc63 | ||
|
|
ae2777172d | ||
|
|
ee8d5e7036 | ||
|
|
9a8a5313b2 | ||
|
|
49edbb62e6 | ||
|
|
a5bfa4f9d2 | ||
|
|
ddd1e912c9 | ||
|
|
aabb00f2b5 | ||
|
|
f863f84976 | ||
|
|
da14684b90 | ||
|
|
0608838767 | ||
|
|
0d9d586767 | ||
|
|
ea69e607cb | ||
|
|
3eb0b56080 |
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
|
||||
...
|
||||
|
||||
15
.github/FUNDING.yml
vendored
Normal file
15
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: wiredopposite
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
thanks_dev: # Replace with a single thanks.dev username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
19
.gitignore
vendored
19
.gitignore
vendored
@@ -1,6 +1,13 @@
|
||||
build
|
||||
.vscode
|
||||
release
|
||||
generated
|
||||
tools
|
||||
.ignore
|
||||
Firmware/.vscode
|
||||
Firmware/RP2040/build
|
||||
Firmware/RP2040/.ignore
|
||||
Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid_xremote_rom.h
|
||||
Firmware/RP2040/src/BLEServer/att_delayed_response.h
|
||||
Firmware/ESP32/main/BLEServer/att_delayed_response.h
|
||||
Firmware/ESP32/.ignore
|
||||
Firmware/ESP32/build
|
||||
Firmware/ESP32/components/btstack
|
||||
Firmware/ESP32/sdkconfig.old
|
||||
Firmware/external/pico-sdk
|
||||
Firmware/external/picotool
|
||||
Tools/dvd-dongle-rom.bin
|
||||
31
.gitmodules
vendored
31
.gitmodules
vendored
@@ -1,12 +1,21 @@
|
||||
[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 "Tools/dump-dvd-kit"]
|
||||
path = Tools/dump-dvd-kit
|
||||
url = https://github.com/XboxDev/dump-dvd-kit.git
|
||||
[submodule "Firmware/external/Pico-PIO-USB"]
|
||||
path = Firmware/external/Pico-PIO-USB
|
||||
url = https://github.com/wiredopposite/Pico-PIO-USB.git
|
||||
[submodule "WebApp"]
|
||||
path = WebApp
|
||||
url = https://github.com/wiredopposite/OGX-Mini-WebApp.git
|
||||
[submodule "Firmware/external/libfixmath"]
|
||||
path = Firmware/external/libfixmath
|
||||
url = https://github.com/PetteriAimonen/libfixmath.git
|
||||
[submodule "Firmware/ESP32_Blueretro"]
|
||||
path = Firmware/ESP32_Blueretro
|
||||
url = https://github.com/wiredopposite/BlueRetro.git
|
||||
|
||||
20
.vscode/c_cpp_properties.json
vendored
Normal file
20
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Pico",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/Firmware/external/pico-sdk/**"
|
||||
],
|
||||
"forcedInclude": [
|
||||
"${workspaceFolder}/Firmware/external/pico-sdk/src/common/pico_base_headers/include/pico.h",
|
||||
"${workspaceFolder}/Firmware/RP2040/build/generated/pico_base/pico/config_autogen.h"
|
||||
],
|
||||
"defines": [],
|
||||
"compileCommands": "${workspaceFolder}/Firmware/RP2040/build/compile_commands.json",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++14",
|
||||
"intelliSenseMode": "linux-gcc-arm"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
64
.vscode/settings.json
vendored
Normal file
64
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
|
||||
"cmake.sourceDirectory": "${workspaceFolder}/Firmware/RP2040",
|
||||
"cmake.buildDirectory": "${workspaceFolder}/Firmware/RP2040/build",
|
||||
"cmake.configureArgs": [
|
||||
"-DOGXM_BOARD=EXTERNAL_4CH_I2C",
|
||||
"-DMAX_GAMEPADS=4"
|
||||
],
|
||||
"files.associations": {
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"bit": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"cctype": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"compare": "cpp",
|
||||
"concepts": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"list": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"random": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"new": "cpp",
|
||||
"numbers": "cpp",
|
||||
"ostream": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"xmemory": "cpp",
|
||||
"xtr1common": "cpp",
|
||||
"xutility": "cpp"
|
||||
},
|
||||
}
|
||||
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
|
||||
}
|
||||
106
Firmware/ESP32/.vscode/settings.json
vendored
Normal file
106
Firmware/ESP32/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"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",
|
||||
"idf.espIdfPathWin": "C:\\Programming\\esp\\v5.1\\esp-idf",
|
||||
"idf.toolsPathWin": "C:\\Programming\\.espressif"
|
||||
}
|
||||
40
Firmware/ESP32/CMakeLists.txt
Normal file
40
Firmware/ESP32/CMakeLists.txt
Normal file
@@ -0,0 +1,40 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
set(EXTERNAL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../external)
|
||||
set(EXTERNAL_CMAKE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../cmake)
|
||||
set(BTSTACK_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/components/btstack)
|
||||
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../FWDefines.cmake)
|
||||
include(${EXTERNAL_CMAKE_DIR}/init_submodules.cmake)
|
||||
include(${EXTERNAL_CMAKE_DIR}/patch_libs.cmake)
|
||||
include(${EXTERNAL_CMAKE_DIR}/generate_gatt_header.cmake)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/integrate_btstack.cmake)
|
||||
|
||||
init_git_submodules(${EXTERNAL_DIR}
|
||||
${EXTERNAL_DIR}/bluepad32
|
||||
${EXTERNAL_DIR}/libfixmath
|
||||
)
|
||||
apply_lib_patches(${EXTERNAL_DIR})
|
||||
integrate_btstack(${EXTERNAL_DIR})
|
||||
generate_gatt_header(
|
||||
${BTSTACK_ROOT}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../RP2040/src/BLEServer/att_delayed_response.gatt
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/main/BLEServer/att_delayed_response.h
|
||||
)
|
||||
|
||||
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("${FW_NAME}-${FW_VERSION}-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)
|
||||
27
Firmware/ESP32/components/libfixmath/CMakeLists.txt
Normal file
27
Firmware/ESP32/components/libfixmath/CMakeLists.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(LIBFIXMATH_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../external/libfixmath)
|
||||
|
||||
if (NOT EXISTS ${LIBFIXMATH_ROOT})
|
||||
message(FATAL_ERROR "External directory not found: ${LIBFIXMATH_ROOT}")
|
||||
else()
|
||||
message(STATUS "Found libfixmath at ${LIBFIXMATH_ROOT}")
|
||||
endif()
|
||||
|
||||
file(GLOB SRCS ${LIBFIXMATH_ROOT}/libfixmath/*.c)
|
||||
|
||||
idf_component_register(
|
||||
SRCS
|
||||
${SRCS}
|
||||
INCLUDE_DIRS
|
||||
${LIBFIXMATH_ROOT}
|
||||
${LIBFIXMATH_ROOT}/libfixmath
|
||||
)
|
||||
|
||||
target_compile_definitions(${COMPONENT_LIB} PRIVATE
|
||||
FIXMATH_FAST_SIN
|
||||
FIXMATH_NO_64BIT
|
||||
FIXMATH_NO_CACHE
|
||||
FIXMATH_NO_HARD_DIVISION
|
||||
FIXMATH_NO_OVERFLOW
|
||||
)
|
||||
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
|
||||
334
Firmware/ESP32/main/BLEServer/BLEServer.cpp
Normal file
334
Firmware/ESP32/main/BLEServer/BLEServer.cpp
Normal file
@@ -0,0 +1,334 @@
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_system.h>
|
||||
|
||||
#include "att_server.h"
|
||||
#include "btstack.h"
|
||||
|
||||
#include "BTManager/BTManager.h"
|
||||
#include "Gamepad/Gamepad.h"
|
||||
#include "BLEServer/BLEServer.h"
|
||||
#include "BLEServer/att_delayed_response.h"
|
||||
#include "UserSettings/UserProfile.h"
|
||||
#include "UserSettings/UserSettings.h"
|
||||
|
||||
namespace BLEServer {
|
||||
|
||||
constexpr uint16_t PACKET_LEN_MAX = 20;
|
||||
constexpr size_t GAMEPAD_LEN = 23;
|
||||
|
||||
namespace Handle
|
||||
{
|
||||
constexpr uint16_t FW_VERSION = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789020_01_VALUE_HANDLE;
|
||||
constexpr uint16_t FW_NAME = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789021_01_VALUE_HANDLE;
|
||||
|
||||
constexpr uint16_t SETUP_READ = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789030_01_VALUE_HANDLE;
|
||||
constexpr uint16_t SETUP_WRITE = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789031_01_VALUE_HANDLE;
|
||||
constexpr uint16_t GET_SETUP = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789032_01_VALUE_HANDLE;
|
||||
|
||||
constexpr uint16_t PROFILE = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789040_01_VALUE_HANDLE;
|
||||
|
||||
constexpr uint16_t GAMEPAD = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789050_01_VALUE_HANDLE;
|
||||
}
|
||||
|
||||
namespace ADV
|
||||
{
|
||||
// Flags general discoverable, BR/EDR not supported
|
||||
static const uint8_t FLAGS[] = { 0x02, 0x01, 0x06 };
|
||||
static const uint8_t NAME_TYPE = 0x09;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct Data
|
||||
{
|
||||
uint8_t flags[sizeof(FLAGS)];
|
||||
uint8_t name_len;
|
||||
uint8_t name_type;
|
||||
uint8_t name[sizeof(FIRMWARE_NAME) - 1];
|
||||
|
||||
Data()
|
||||
{
|
||||
std::memcpy(flags, FLAGS, sizeof(flags));
|
||||
name_len = sizeof(FIRMWARE_NAME);
|
||||
name_type = NAME_TYPE;
|
||||
std::string fw_name = FIRMWARE_NAME;
|
||||
std::memcpy(name, fw_name.c_str(), std::min(sizeof(name), fw_name.size()));
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Data) == 5 + sizeof(FIRMWARE_NAME) - 1, "BLEServer::ADV::Data struct size mismatch");
|
||||
#pragma pack(pop)
|
||||
}
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct SetupPacket
|
||||
{
|
||||
DeviceDriverType device_type{DeviceDriverType::NONE};
|
||||
uint8_t max_gamepads{MAX_GAMEPADS};
|
||||
uint8_t player_idx{0};
|
||||
uint8_t profile_id{0};
|
||||
};
|
||||
static_assert(sizeof(SetupPacket) == 4, "BLEServer::SetupPacket struct size mismatch");
|
||||
#pragma pack(pop)
|
||||
|
||||
class ProfileReader
|
||||
{
|
||||
public:
|
||||
ProfileReader() = default;
|
||||
~ProfileReader() = default;
|
||||
|
||||
void set_setup_packet(const SetupPacket& setup_packet)
|
||||
{
|
||||
setup_packet_ = setup_packet;
|
||||
current_offset_ = 0;
|
||||
}
|
||||
const SetupPacket& get_setup_packet() const
|
||||
{
|
||||
return setup_packet_;
|
||||
}
|
||||
uint16_t get_xfer_len()
|
||||
{
|
||||
return static_cast<uint16_t>(std::min(static_cast<size_t>(PACKET_LEN_MAX), sizeof(UserProfile) - current_offset_));
|
||||
}
|
||||
uint16_t get_profile_data(uint8_t* buffer, uint16_t buffer_len)
|
||||
{
|
||||
size_t copy_len = get_xfer_len();
|
||||
if (!buffer || buffer_len < copy_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (current_offset_ == 0 && !set_profile())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::memcpy(buffer, reinterpret_cast<uint8_t*>(&profile_) + current_offset_, copy_len);
|
||||
|
||||
current_offset_ += copy_len;
|
||||
if (current_offset_ >= sizeof(UserProfile))
|
||||
{
|
||||
current_offset_ = 0;
|
||||
OGXM_LOG("ProfileReader: Read complete for profile ID: %i\n", profile_.id);
|
||||
}
|
||||
return copy_len;
|
||||
}
|
||||
|
||||
private:
|
||||
SetupPacket setup_packet_;
|
||||
UserProfile profile_;
|
||||
size_t current_offset_ = 0;
|
||||
|
||||
bool set_profile()
|
||||
{
|
||||
if (setup_packet_.profile_id == 0xFF)
|
||||
{
|
||||
if (setup_packet_.player_idx >= UserSettings::MAX_PROFILES)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
profile_ = UserSettings::get_instance().get_profile_by_index(setup_packet_.player_idx);
|
||||
OGXM_LOG("ProfileReader: Reading profile for player %d\n", setup_packet_.player_idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setup_packet_.profile_id > UserSettings::MAX_PROFILES)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
profile_ = UserSettings::get_instance().get_profile_by_id(setup_packet_.profile_id);
|
||||
OGXM_LOG("ProfileReader: Reading profile with ID %d\n", setup_packet_.profile_id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ProfileWriter
|
||||
{
|
||||
public:
|
||||
ProfileWriter() = default;
|
||||
~ProfileWriter() = default;
|
||||
|
||||
void set_setup_packet(const SetupPacket& setup_packet)
|
||||
{
|
||||
setup_packet_ = setup_packet;
|
||||
current_offset_ = 0;
|
||||
}
|
||||
const SetupPacket& get_setup_packet() const
|
||||
{
|
||||
return setup_packet_;
|
||||
}
|
||||
uint16_t get_xfer_len()
|
||||
{
|
||||
return static_cast<uint16_t>(std::min(static_cast<size_t>(PACKET_LEN_MAX), sizeof(UserProfile) - current_offset_));
|
||||
}
|
||||
size_t set_profile_data(const uint8_t* buffer, uint16_t buffer_len)
|
||||
{
|
||||
size_t copy_len = get_xfer_len();
|
||||
if (!buffer || buffer_len < copy_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::memcpy(reinterpret_cast<uint8_t*>(&profile_) + current_offset_, buffer, copy_len);
|
||||
|
||||
current_offset_ += copy_len;
|
||||
size_t ret = current_offset_;
|
||||
|
||||
if (current_offset_ >= sizeof(UserProfile))
|
||||
{
|
||||
current_offset_ = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
bool commit_profile()
|
||||
{
|
||||
if (setup_packet_.device_type != DeviceDriverType::NONE)
|
||||
{
|
||||
UserSettings::get_instance().store_profile_and_driver_type(setup_packet_.device_type, setup_packet_.player_idx, profile_);
|
||||
}
|
||||
else
|
||||
{
|
||||
UserSettings::get_instance().store_profile(setup_packet_.player_idx, profile_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
SetupPacket setup_packet_;
|
||||
UserProfile profile_;
|
||||
size_t current_offset_ = 0;
|
||||
};
|
||||
|
||||
ProfileReader profile_reader_;
|
||||
ProfileWriter profile_writer_;
|
||||
|
||||
static int verify_write(const uint16_t buffer_size, const uint16_t expected_size)
|
||||
{
|
||||
if (buffer_size != expected_size)
|
||||
{
|
||||
return ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint16_t att_read_callback( hci_con_handle_t connection_handle,
|
||||
uint16_t att_handle,
|
||||
uint16_t offset,
|
||||
uint8_t *buffer,
|
||||
uint16_t buffer_size)
|
||||
{
|
||||
std::string fw_version;
|
||||
std::string fw_name;
|
||||
|
||||
switch (att_handle)
|
||||
{
|
||||
case Handle::FW_VERSION:
|
||||
fw_version = FIRMWARE_VERSION;
|
||||
if (buffer)
|
||||
{
|
||||
std::memcpy(buffer, reinterpret_cast<const uint8_t*>(fw_version.c_str()), fw_version.size());
|
||||
}
|
||||
return static_cast<uint16_t>(fw_version.size());
|
||||
|
||||
case Handle::FW_NAME:
|
||||
fw_name = FIRMWARE_NAME;
|
||||
if (buffer)
|
||||
{
|
||||
std::memcpy(buffer, reinterpret_cast<const uint8_t*>(fw_name.c_str()), fw_name.size());;
|
||||
}
|
||||
return static_cast<uint16_t>(fw_name.size());
|
||||
|
||||
case Handle::GET_SETUP:
|
||||
if (buffer)
|
||||
{
|
||||
buffer[0] = static_cast<uint8_t>(UserSettings::get_instance().get_current_driver());
|
||||
buffer[1] = MAX_GAMEPADS;
|
||||
buffer[2] = 0;
|
||||
buffer[3] = UserSettings::get_instance().get_active_profile_id(0);
|
||||
}
|
||||
return static_cast<uint16_t>(sizeof(SetupPacket));
|
||||
|
||||
case Handle::PROFILE:
|
||||
if (buffer)
|
||||
{
|
||||
return profile_reader_.get_profile_data(buffer, buffer_size);
|
||||
}
|
||||
return profile_reader_.get_xfer_len();
|
||||
|
||||
case Handle::GAMEPAD:
|
||||
if (buffer)
|
||||
{
|
||||
I2CDriver::PacketIn packet_in = BTManager::get_instance().get_packet_in(0);
|
||||
std::memcpy(buffer, &packet_in.dpad, 13);
|
||||
}
|
||||
return static_cast<uint16_t>(13);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int att_write_callback( hci_con_handle_t connection_handle,
|
||||
uint16_t att_handle,
|
||||
uint16_t transaction_mode,
|
||||
uint16_t offset,
|
||||
uint8_t *buffer,
|
||||
uint16_t buffer_size)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (att_handle)
|
||||
{
|
||||
case Handle::SETUP_READ:
|
||||
if ((ret = verify_write(buffer_size, sizeof(SetupPacket))) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
profile_reader_.set_setup_packet(*reinterpret_cast<SetupPacket*>(buffer));
|
||||
break;
|
||||
|
||||
case Handle::SETUP_WRITE:
|
||||
if ((ret = verify_write(buffer_size, sizeof(SetupPacket))) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
profile_writer_.set_setup_packet(*reinterpret_cast<SetupPacket*>(buffer));
|
||||
break;
|
||||
|
||||
case Handle::PROFILE:
|
||||
if ((ret = verify_write(buffer_size, profile_writer_.get_xfer_len())) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (profile_writer_.set_profile_data(buffer, buffer_size) == sizeof(UserProfile))
|
||||
{
|
||||
profile_writer_.commit_profile();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void init_server()
|
||||
{
|
||||
att_server_init(profile_data, att_read_callback, att_write_callback);
|
||||
|
||||
uint16_t adv_int_min = 0x0030;
|
||||
uint16_t adv_int_max = 0x0030;
|
||||
uint8_t adv_type = 0;
|
||||
|
||||
bd_addr_t null_addr;
|
||||
std::memset(null_addr, 0, sizeof(null_addr));
|
||||
|
||||
static ADV::Data adv_data = ADV::Data();
|
||||
|
||||
gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
|
||||
gap_advertisements_set_data(static_cast<uint8_t>(sizeof(adv_data)), reinterpret_cast<uint8_t*>(&adv_data));
|
||||
gap_advertisements_enable(1);
|
||||
}
|
||||
|
||||
} // namespace BLEServer
|
||||
13
Firmware/ESP32/main/BLEServer/BLEServer.h
Normal file
13
Firmware/ESP32/main/BLEServer/BLEServer.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef BLE_SERVER_H
|
||||
#define BLE_SERVER_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Gamepad/Gamepad.h"
|
||||
|
||||
namespace BLEServer
|
||||
{
|
||||
void init_server();
|
||||
}
|
||||
|
||||
#endif // BLE_SERVER_H
|
||||
257
Firmware/ESP32/main/BTManager/BTManager.cpp
Normal file
257
Firmware/ESP32/main/BTManager/BTManager.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
#include "btstack_port_esp32.h"
|
||||
#include "btstack_run_loop.h"
|
||||
#include "btstack_stdio_esp32.h"
|
||||
#include "uni.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "Board/ogxm_log.h"
|
||||
#include "Board/board_api.h"
|
||||
#include "BTManager/BTManager.h"
|
||||
#include "BLEServer/BLEServer.h"
|
||||
|
||||
void BTManager::run_task()
|
||||
{
|
||||
board_api::init_board();
|
||||
UserSettings::get_instance().initialize_flash();
|
||||
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
|
||||
{
|
||||
UserProfile profile = UserSettings::get_instance().get_profile_by_index(i);
|
||||
devices_[i].mapper.set_profile(profile);
|
||||
}
|
||||
|
||||
i2c_driver_.initialize_i2c(
|
||||
static_cast<i2c_port_t>(CONFIG_I2C_PORT),
|
||||
static_cast<gpio_num_t>(CONFIG_I2C_SDA_PIN),
|
||||
static_cast<gpio_num_t>(CONFIG_I2C_SCL_PIN),
|
||||
CONFIG_I2C_BAUDRATE
|
||||
);
|
||||
|
||||
xTaskCreatePinnedToCore(
|
||||
[](void* parameter)
|
||||
{
|
||||
get_instance().i2c_driver_.run_tasks();
|
||||
},
|
||||
"i2c",
|
||||
2048 * 2,
|
||||
nullptr,
|
||||
configMAX_PRIORITIES-8,
|
||||
nullptr,
|
||||
1
|
||||
);
|
||||
|
||||
btstack_init();
|
||||
|
||||
uni_platform_set_custom(get_bp32_driver());
|
||||
uni_init(0, nullptr);
|
||||
|
||||
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_timer_source_t driver_update_timer;
|
||||
driver_update_timer.process = driver_update_timer_cb;
|
||||
driver_update_timer.context = nullptr;
|
||||
btstack_run_loop_set_timer(&driver_update_timer, UserSettings::GP_CHECK_DELAY_MS);
|
||||
btstack_run_loop_add_timer(&driver_update_timer);
|
||||
|
||||
BLEServer::init_server();
|
||||
|
||||
//Doesn't return
|
||||
btstack_run_loop_execute();
|
||||
}
|
||||
|
||||
bool BTManager::is_connected(uint8_t index)
|
||||
{
|
||||
if (index >= devices_.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return devices_[index].connected.load();
|
||||
}
|
||||
|
||||
bool BTManager::any_connected()
|
||||
{
|
||||
for (auto& device : devices_)
|
||||
{
|
||||
if (device.connected.load())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BTManager::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(get_instance().any_connected() ? true : (led_state ? true : false));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint8_t i = 0; i < board_api::NUM_LEDS; ++i)
|
||||
{
|
||||
board_api::set_led(i, get_instance().is_connected(i) ? true : (led_state ? true : false));
|
||||
}
|
||||
}
|
||||
|
||||
btstack_run_loop_set_timer(ts, LED_TIME_MS);
|
||||
btstack_run_loop_add_timer(ts);
|
||||
}
|
||||
|
||||
void BTManager::send_driver_type(DeviceDriverType driver_type)
|
||||
{
|
||||
if constexpr (I2CDriver::MULTI_SLAVE)
|
||||
{
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
|
||||
{
|
||||
I2CDriver::PacketIn packet_in = devices_[i].packet_in;
|
||||
packet_in.packet_id = I2CDriver::PacketID::SET_DRIVER;
|
||||
packet_in.index = i;
|
||||
packet_in.device_driver = driver_type;
|
||||
i2c_driver_.write_packet(i + 1, packet_in);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
I2CDriver::PacketIn packet_in = devices_[0].packet_in;
|
||||
packet_in.packet_id = I2CDriver::PacketID::SET_DRIVER;
|
||||
packet_in.index = 0;
|
||||
packet_in.device_driver = driver_type;
|
||||
i2c_driver_.write_packet(0x01, packet_in);
|
||||
}
|
||||
}
|
||||
|
||||
uni_hid_device_t* BTManager::get_connected_bp32_device(uint8_t index)
|
||||
{
|
||||
uni_hid_device_t* bp_device = nullptr;
|
||||
if (!(bp_device = uni_hid_device_get_instance_for_idx(index)) ||
|
||||
!uni_bt_conn_is_connected(&bp_device->conn) ||
|
||||
!bp_device->report_parser.play_dual_rumble)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return bp_device;
|
||||
}
|
||||
|
||||
void BTManager::driver_update_timer_cb(btstack_timer_source *ts)
|
||||
{
|
||||
BTManager& bt_manager = get_instance();
|
||||
I2CDriver::PacketIn& packet_in = bt_manager.devices_.front().packet_in;
|
||||
|
||||
if (get_connected_bp32_device(0) &&
|
||||
UserSettings::get_instance().check_for_driver_change(packet_in))
|
||||
{
|
||||
OGXM_LOG("BP32: Driver change detected\n");
|
||||
UserSettings::get_instance().store_driver_type(UserSettings::get_instance().get_current_driver());
|
||||
}
|
||||
|
||||
//Notify pico of current driver type regardless of change
|
||||
bt_manager.send_driver_type(UserSettings::get_instance().get_current_driver());
|
||||
|
||||
btstack_run_loop_set_timer(ts, UserSettings::GP_CHECK_DELAY_MS);
|
||||
btstack_run_loop_add_timer(ts);
|
||||
}
|
||||
|
||||
void BTManager::send_feedback_cb(void* context)
|
||||
{
|
||||
FBContext* fb_context = reinterpret_cast<FBContext*>(context);
|
||||
uni_hid_device_t* bp_device = nullptr;
|
||||
|
||||
if (!(bp_device = get_connected_bp32_device(fb_context->index)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
I2CDriver::PacketOut packet_out = fb_context->packet_out->load();
|
||||
|
||||
if (packet_out.rumble_l || packet_out.rumble_r)
|
||||
{
|
||||
bp_device->report_parser.play_dual_rumble(
|
||||
bp_device,
|
||||
0,
|
||||
FEEDBACK_TIME_MS,
|
||||
packet_out.rumble_l,
|
||||
packet_out.rumble_r
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//This will have to be changed once full support for multiple devices is added
|
||||
void BTManager::feedback_timer_cb(btstack_timer_source *ts)
|
||||
{
|
||||
static FBContext fb_contexts[MAX_GAMEPADS];
|
||||
BTManager& bt_manager = get_instance();
|
||||
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
|
||||
{
|
||||
if (!get_connected_bp32_device(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FBContext& fb_context = fb_contexts[i];
|
||||
fb_context.index = i;
|
||||
fb_context.packet_out = &bt_manager.devices_[i].packet_out;
|
||||
fb_context.cb_reg.callback = send_feedback_cb;
|
||||
fb_context.cb_reg.context = reinterpret_cast<void*>(&fb_context);
|
||||
|
||||
//Register a read on i2c thread, with callback to send feedback on btstack thread
|
||||
bt_manager.i2c_driver_.read_packet(I2CDriver::MULTI_SLAVE ? i + 1 : 0x01,
|
||||
[&fb_context](const I2CDriver::PacketOut& packet_out)
|
||||
{
|
||||
fb_context.packet_out->store(packet_out);
|
||||
btstack_run_loop_execute_on_main_thread(&fb_context.cb_reg);
|
||||
});
|
||||
}
|
||||
|
||||
btstack_run_loop_set_timer(ts, FEEDBACK_TIME_MS);
|
||||
btstack_run_loop_add_timer(ts);
|
||||
}
|
||||
|
||||
void BTManager::manage_connection(uint8_t index, bool connected)
|
||||
{
|
||||
devices_[index].connected.store(connected);
|
||||
if (connected)
|
||||
{
|
||||
if (!fb_timer_running_)
|
||||
{
|
||||
fb_timer_running_ = true;
|
||||
fb_timer_.process = feedback_timer_cb;
|
||||
fb_timer_.context = nullptr;
|
||||
btstack_run_loop_set_timer(&fb_timer_, FEEDBACK_TIME_MS);
|
||||
btstack_run_loop_add_timer(&fb_timer_);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!any_connected())
|
||||
{
|
||||
if (fb_timer_running_)
|
||||
{
|
||||
fb_timer_running_ = false;
|
||||
btstack_run_loop_remove_timer(&fb_timer_);
|
||||
}
|
||||
}
|
||||
|
||||
I2CDriver::PacketIn packet_in = I2CDriver::PacketIn();
|
||||
packet_in.packet_id = I2CDriver::PacketID::SET_PAD;
|
||||
packet_in.index = index;
|
||||
i2c_driver_.write_packet(I2CDriver::MULTI_SLAVE ? packet_in.index + 1 : 0x01, packet_in);
|
||||
}
|
||||
}
|
||||
|
||||
I2CDriver::PacketIn BTManager::get_packet_in(uint8_t index)
|
||||
{
|
||||
if (index >= devices_.size())
|
||||
{
|
||||
return I2CDriver::PacketIn();
|
||||
}
|
||||
return devices_[index].packet_in;
|
||||
}
|
||||
83
Firmware/ESP32/main/BTManager/BTManager.h
Normal file
83
Firmware/ESP32/main/BTManager/BTManager.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef _BT_MANAGER_H_
|
||||
#define _BT_MANAGER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include "I2CDriver/I2CDriver.h"
|
||||
#include "Gamepad/Gamepad.h"
|
||||
|
||||
class BTManager
|
||||
{
|
||||
public:
|
||||
static BTManager& get_instance()
|
||||
{
|
||||
static BTManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void run_task();
|
||||
bool any_connected();
|
||||
bool is_connected(uint8_t index);
|
||||
I2CDriver::PacketIn get_packet_in(uint8_t index);
|
||||
|
||||
private:
|
||||
BTManager() = default;
|
||||
~BTManager() = default;
|
||||
BTManager(const BTManager&) = delete;
|
||||
BTManager& operator=(const BTManager&) = delete;
|
||||
|
||||
static constexpr uint32_t FEEDBACK_TIME_MS = 200;
|
||||
static constexpr uint32_t LED_TIME_MS = 500;
|
||||
|
||||
struct Device
|
||||
{
|
||||
std::atomic<bool> connected{false};
|
||||
GamepadMapper mapper;
|
||||
I2CDriver::PacketIn packet_in;
|
||||
std::atomic<I2CDriver::PacketOut> packet_out; //Can be updated from i2c thread
|
||||
};
|
||||
|
||||
struct FBContext
|
||||
{
|
||||
uint8_t index;
|
||||
std::atomic<I2CDriver::PacketOut>* packet_out;
|
||||
btstack_context_callback_registration_t cb_reg;
|
||||
};
|
||||
|
||||
std::array<Device, MAX_GAMEPADS> devices_;
|
||||
I2CDriver i2c_driver_;
|
||||
|
||||
btstack_timer_source_t fb_timer_;
|
||||
bool fb_timer_running_ = false;
|
||||
|
||||
void send_driver_type(DeviceDriverType driver_type);
|
||||
void manage_connection(uint8_t index, bool connected);
|
||||
|
||||
static uni_hid_device_t* get_connected_bp32_device(uint8_t index);
|
||||
static void check_led_cb(btstack_timer_source *ts);
|
||||
static void send_feedback_cb(void* context);
|
||||
static void feedback_timer_cb(btstack_timer_source *ts);
|
||||
static void driver_update_timer_cb(btstack_timer_source *ts);
|
||||
|
||||
//Bluepad32 driver
|
||||
|
||||
void init(int argc, const char** arg_V);
|
||||
void init_complete_cb(void);
|
||||
uni_error_t device_discovered_cb(bd_addr_t addr, const char* name, uint16_t cod, uint8_t rssi);
|
||||
void device_connected_cb(uni_hid_device_t* device);
|
||||
void device_disconnected_cb(uni_hid_device_t* device);
|
||||
uni_error_t device_ready_cb(uni_hid_device_t* device);
|
||||
void controller_data_cb(uni_hid_device_t* bp_device, uni_controller_t* controller);
|
||||
const uni_property_t* get_property_cb(uni_property_idx_t idx);
|
||||
void oob_event_cb(uni_platform_oob_event_t event, void* data);
|
||||
|
||||
static uni_platform* get_bp32_driver();
|
||||
|
||||
}; // class BTManager
|
||||
|
||||
#endif // _BT_MANAGER_H_
|
||||
194
Firmware/ESP32/main/BTManager/BTManager_BP32.cpp
Normal file
194
Firmware/ESP32/main/BTManager/BTManager_BP32.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
#include "btstack_port_esp32.h"
|
||||
#include "btstack_run_loop.h"
|
||||
#include "btstack_stdio_esp32.h"
|
||||
#include "uni.h"
|
||||
|
||||
#include "Board/ogxm_log.h"
|
||||
#include "BTManager/BTManager.h"
|
||||
#include "BLEServer/BLEServer.h"
|
||||
|
||||
void BTManager::init(int argc, const char** arg_V)
|
||||
{
|
||||
OGXM_LOG("BP32: Initializing Bluepad32\n");
|
||||
}
|
||||
|
||||
void BTManager::init_complete_cb(void)
|
||||
{
|
||||
uni_bt_enable_new_connections_unsafe(true);
|
||||
// uni_bt_del_keys_unsafe();
|
||||
uni_property_dump_all();
|
||||
}
|
||||
|
||||
uni_error_t BTManager::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;
|
||||
}
|
||||
|
||||
void BTManager::device_connected_cb(uni_hid_device_t* device)
|
||||
{
|
||||
OGXM_LOG("BP32: Device connected, addr: %p, index: %i\n", device, uni_hid_device_get_idx_for_instance(device));
|
||||
}
|
||||
|
||||
void BTManager::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;
|
||||
}
|
||||
manage_connection(static_cast<uint8_t>(idx), false);
|
||||
}
|
||||
|
||||
uni_error_t BTManager::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_IGNORE_DEVICE;
|
||||
}
|
||||
manage_connection(static_cast<uint8_t>(idx), true);
|
||||
return UNI_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
void BTManager::controller_data_cb(uni_hid_device_t* bp_device, uni_controller_t* controller)
|
||||
{
|
||||
static uni_gamepad_t prev_uni_gps[MAX_GAMEPADS] = {};
|
||||
|
||||
if (controller->klass != UNI_CONTROLLER_CLASS_GAMEPAD)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = uni_hid_device_get_idx_for_instance(bp_device);
|
||||
uni_gamepad_t *uni_gp = &controller->gamepad;
|
||||
|
||||
if (idx < 0 || idx >= MAX_GAMEPADS || std::memcmp(uni_gp, &prev_uni_gps[idx], sizeof(uni_gamepad_t)) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
I2CDriver::PacketIn& packet_in = devices_[idx].packet_in;
|
||||
GamepadMapper& mapper = devices_[idx].mapper;
|
||||
|
||||
packet_in = I2CDriver::PacketIn();
|
||||
packet_in.packet_id = I2CDriver::PacketID::SET_PAD;
|
||||
packet_in.index = static_cast<uint8_t>(idx);
|
||||
|
||||
switch (uni_gp->dpad)
|
||||
{
|
||||
case DPAD_UP:
|
||||
packet_in.dpad = mapper.DPAD_UP;
|
||||
break;
|
||||
case DPAD_DOWN:
|
||||
packet_in.dpad = mapper.DPAD_DOWN;
|
||||
break;
|
||||
case DPAD_LEFT:
|
||||
packet_in.dpad = mapper.DPAD_LEFT;
|
||||
break;
|
||||
case DPAD_RIGHT:
|
||||
packet_in.dpad = mapper.DPAD_RIGHT;
|
||||
break;
|
||||
case (DPAD_UP | DPAD_RIGHT):
|
||||
packet_in.dpad = mapper.DPAD_UP_RIGHT;
|
||||
break;
|
||||
case (DPAD_DOWN | DPAD_RIGHT):
|
||||
packet_in.dpad = mapper.DPAD_DOWN_RIGHT;
|
||||
break;
|
||||
case (DPAD_DOWN | DPAD_LEFT):
|
||||
packet_in.dpad = mapper.DPAD_DOWN_LEFT;
|
||||
break;
|
||||
case (DPAD_UP | DPAD_LEFT):
|
||||
packet_in.dpad = mapper.DPAD_UP_LEFT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (uni_gp->buttons & BUTTON_A) packet_in.buttons |= mapper.BUTTON_A;
|
||||
if (uni_gp->buttons & BUTTON_B) packet_in.buttons |= mapper.BUTTON_B;
|
||||
if (uni_gp->buttons & BUTTON_X) packet_in.buttons |= mapper.BUTTON_X;
|
||||
if (uni_gp->buttons & BUTTON_Y) packet_in.buttons |= mapper.BUTTON_Y;
|
||||
if (uni_gp->buttons & BUTTON_SHOULDER_L) packet_in.buttons |= mapper.BUTTON_LB;
|
||||
if (uni_gp->buttons & BUTTON_SHOULDER_R) packet_in.buttons |= mapper.BUTTON_RB;
|
||||
if (uni_gp->buttons & BUTTON_THUMB_L) packet_in.buttons |= mapper.BUTTON_L3;
|
||||
if (uni_gp->buttons & BUTTON_THUMB_R) packet_in.buttons |= mapper.BUTTON_R3;
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_BACK) packet_in.buttons |= mapper.BUTTON_BACK;
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_START) packet_in.buttons |= mapper.BUTTON_START;
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_SYSTEM) packet_in.buttons |= mapper.BUTTON_SYS;
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_CAPTURE) packet_in.buttons |= mapper.BUTTON_MISC;
|
||||
|
||||
packet_in.trigger_l = mapper.scale_trigger_l<10>(static_cast<uint16_t>(uni_gp->brake));
|
||||
packet_in.trigger_r = mapper.scale_trigger_r<10>(static_cast<uint16_t>(uni_gp->throttle));
|
||||
|
||||
// auto joy_l = mapper.scale_joystick_l<10>(uni_gp->axis_x, uni_gp->axis_y);
|
||||
// auto joy_r = mapper.scale_joystick_r<10>(uni_gp->axis_rx, uni_gp->axis_ry);
|
||||
|
||||
// packet_in.joystick_lx = joy_l.first;
|
||||
// packet_in.joystick_ly = joy_l.second;
|
||||
// packet_in.joystick_rx = joy_r.first;
|
||||
// packet_in.joystick_ry = joy_r.second;
|
||||
|
||||
std::tie(packet_in.joystick_lx, packet_in.joystick_ly) = mapper.scale_joystick_l<10>(uni_gp->axis_x, uni_gp->axis_y);
|
||||
std::tie(packet_in.joystick_rx, packet_in.joystick_ry) = mapper.scale_joystick_r<10>(uni_gp->axis_rx, uni_gp->axis_ry);
|
||||
|
||||
i2c_driver_.write_packet(I2CDriver::MULTI_SLAVE ? packet_in.index + 1 : 0x01, packet_in);
|
||||
|
||||
std::memcpy(&prev_uni_gps[idx], uni_gp, sizeof(uni_gamepad_t));
|
||||
}
|
||||
|
||||
const uni_property_t* BTManager::get_property_cb(uni_property_idx_t idx)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BTManager::oob_event_cb(uni_platform_oob_event_t event, void* data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uni_platform* BTManager::get_bp32_driver()
|
||||
{
|
||||
static uni_platform driver = {
|
||||
.name = "OGXMiniW",
|
||||
.init =
|
||||
[](int argc, const char** argv)
|
||||
{ get_instance().init(argc, argv); },
|
||||
|
||||
.on_init_complete =
|
||||
[](void)
|
||||
{ get_instance().init_complete_cb(); },
|
||||
|
||||
.on_device_discovered =
|
||||
[](bd_addr_t addr, const char* name, uint16_t cod, uint8_t rssi)
|
||||
{ return get_instance().device_discovered_cb(addr, name, cod, rssi); },
|
||||
|
||||
.on_device_connected =
|
||||
[](uni_hid_device_t* device)
|
||||
{ get_instance().device_connected_cb(device); },
|
||||
|
||||
.on_device_disconnected =
|
||||
[](uni_hid_device_t* device)
|
||||
{ get_instance().device_disconnected_cb(device); },
|
||||
|
||||
.on_device_ready =
|
||||
[](uni_hid_device_t* device)
|
||||
{ return get_instance().device_ready_cb(device); },
|
||||
|
||||
.on_controller_data =
|
||||
[](uni_hid_device_t* device, uni_controller_t* controller)
|
||||
{ get_instance().controller_data_cb(device, controller); },
|
||||
|
||||
.get_property =
|
||||
[](uni_property_idx_t idx)
|
||||
{ return get_instance().get_property_cb(idx); },
|
||||
|
||||
.on_oob_event =
|
||||
[](uni_platform_oob_event_t event, void* data)
|
||||
{ get_instance().oob_event_cb(event, data); },
|
||||
};
|
||||
return &driver;
|
||||
}
|
||||
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_board()
|
||||
{
|
||||
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
|
||||
43
Firmware/ESP32/main/Board/board_api.h
Normal file
43
Firmware/ESP32/main/Board/board_api.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#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_board();
|
||||
void set_led(uint8_t index, bool state);
|
||||
void set_led(bool state);
|
||||
}
|
||||
|
||||
#endif // _BOARD_API_H_
|
||||
67
Firmware/ESP32/main/Board/ogxm_log.h
Normal file
67
Firmware/ESP32/main/Board/ogxm_log.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef _OGXM_LOG_H_
|
||||
#define _OGXM_LOG_H_
|
||||
|
||||
#include <esp_log.h>
|
||||
#if ESP_LOG_LEVEL >= ESP_LOG_INFO
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "uni.h"
|
||||
|
||||
namespace OGXM
|
||||
{
|
||||
static inline void log(const std::string& message)
|
||||
{
|
||||
logi(message.c_str());
|
||||
}
|
||||
|
||||
static inline void log(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
uni_logv(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static inline void log_hex(const std::string& message, const uint8_t* data, const size_t len)
|
||||
{
|
||||
log(message);
|
||||
|
||||
std::ostringstream hex_stream;
|
||||
hex_stream << std::hex << std::setfill('0');
|
||||
int char_num = 0;
|
||||
for (uint16_t i = 0; i < len; ++i)
|
||||
{
|
||||
hex_stream << std::setw(2) << static_cast<int>(data[i]) << " ";
|
||||
char_num++;
|
||||
if (char_num == 16)
|
||||
{
|
||||
hex_stream << "\n";
|
||||
char_num = 0;
|
||||
}
|
||||
}
|
||||
if (char_num != 0)
|
||||
{
|
||||
hex_stream << "\n";
|
||||
}
|
||||
|
||||
log(hex_stream.str());
|
||||
}
|
||||
|
||||
} // namespace OGXM
|
||||
|
||||
#define OGXM_LOG OGXM::log
|
||||
#define OGXM_LOG_HEX OGXM::log_hex
|
||||
|
||||
#else // ESP_LOG_LEVEL >= ESP_LOG_INFO
|
||||
|
||||
#define OGXM_LOG(...)
|
||||
#define OGXM_LOG_HEX(...)
|
||||
|
||||
#endif // ESP_LOG_LEVEL >= ESP_LOG_INFO
|
||||
|
||||
#endif // _OGXM_LOG_H_
|
||||
29
Firmware/ESP32/main/CMakeLists.txt
Normal file
29
Firmware/ESP32/main/CMakeLists.txt
Normal file
@@ -0,0 +1,29 @@
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../FWDefines.cmake)
|
||||
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"main.c"
|
||||
"main.cpp"
|
||||
"Board/board_api.cpp"
|
||||
"BTManager/BTManager.cpp"
|
||||
"BTManager/BTManager_BP32.cpp"
|
||||
"BLEServer/BLEServer.cpp"
|
||||
"I2CDriver/I2CDriver.cpp"
|
||||
"UserSettings/UserSettings.cpp"
|
||||
"UserSettings/UserProfile.cpp"
|
||||
"UserSettings/TriggerSettings.cpp"
|
||||
"UserSettings/JoystickSettings.cpp"
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
REQUIRES
|
||||
bluepad32
|
||||
btstack
|
||||
driver
|
||||
nvs_flash
|
||||
libfixmath
|
||||
)
|
||||
|
||||
target_compile_definitions(${COMPONENT_LIB} PRIVATE
|
||||
FIRMWARE_NAME=\"${FW_NAME}\"
|
||||
FIRMWARE_VERSION=\"${FW_VERSION}\"
|
||||
)
|
||||
450
Firmware/ESP32/main/Gamepad/Gamepad.h
Normal file
450
Firmware/ESP32/main/Gamepad/Gamepad.h
Normal file
@@ -0,0 +1,450 @@
|
||||
#ifndef GAMEPAD_H
|
||||
#define GAMEPAD_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "Gamepad/Range.h"
|
||||
#include "Gamepad/fix16ext.h"
|
||||
#include "UserSettings/UserProfile.h"
|
||||
#include "UserSettings/UserSettings.h"
|
||||
#include "Board/ogxm_log.h"
|
||||
|
||||
#define MAX_GAMEPADS CONFIG_BLUEPAD32_MAX_DEVICES
|
||||
static_assert( MAX_GAMEPADS > 0 &&
|
||||
MAX_GAMEPADS <= 4,
|
||||
"MAX_GAMEPADS must be between 1 and 4");
|
||||
|
||||
namespace Gamepad
|
||||
{
|
||||
static constexpr uint8_t DPAD_UP = 0x01;
|
||||
static constexpr uint8_t DPAD_DOWN = 0x02;
|
||||
static constexpr uint8_t DPAD_LEFT = 0x04;
|
||||
static constexpr uint8_t DPAD_RIGHT = 0x08;
|
||||
static constexpr uint8_t DPAD_UP_LEFT = DPAD_UP | DPAD_LEFT;
|
||||
static constexpr uint8_t DPAD_UP_RIGHT = DPAD_UP | DPAD_RIGHT;
|
||||
static constexpr uint8_t DPAD_DOWN_LEFT = DPAD_DOWN | DPAD_LEFT;
|
||||
static constexpr uint8_t DPAD_DOWN_RIGHT = DPAD_DOWN | DPAD_RIGHT;
|
||||
static constexpr uint8_t DPAD_NONE = 0x00;
|
||||
|
||||
static constexpr uint16_t BUTTON_A = 0x0001;
|
||||
static constexpr uint16_t BUTTON_B = 0x0002;
|
||||
static constexpr uint16_t BUTTON_X = 0x0004;
|
||||
static constexpr uint16_t BUTTON_Y = 0x0008;
|
||||
static constexpr uint16_t BUTTON_L3 = 0x0010;
|
||||
static constexpr uint16_t BUTTON_R3 = 0x0020;
|
||||
static constexpr uint16_t BUTTON_BACK = 0x0040;
|
||||
static constexpr uint16_t BUTTON_START = 0x0080;
|
||||
static constexpr uint16_t BUTTON_LB = 0x0100;
|
||||
static constexpr uint16_t BUTTON_RB = 0x0200;
|
||||
static constexpr uint16_t BUTTON_SYS = 0x0400;
|
||||
static constexpr uint16_t BUTTON_MISC = 0x0800;
|
||||
|
||||
static constexpr uint8_t ANALOG_OFF_UP = 0;
|
||||
static constexpr uint8_t ANALOG_OFF_DOWN = 1;
|
||||
static constexpr uint8_t ANALOG_OFF_LEFT = 2;
|
||||
static constexpr uint8_t ANALOG_OFF_RIGHT = 3;
|
||||
static constexpr uint8_t ANALOG_OFF_A = 4;
|
||||
static constexpr uint8_t ANALOG_OFF_B = 5;
|
||||
static constexpr uint8_t ANALOG_OFF_X = 6;
|
||||
static constexpr uint8_t ANALOG_OFF_Y = 7;
|
||||
static constexpr uint8_t ANALOG_OFF_LB = 8;
|
||||
static constexpr uint8_t ANALOG_OFF_RB = 9;
|
||||
}
|
||||
|
||||
class GamepadMapper
|
||||
{
|
||||
public:
|
||||
uint8_t DPAD_UP = Gamepad::DPAD_UP ;
|
||||
uint8_t DPAD_DOWN = Gamepad::DPAD_DOWN ;
|
||||
uint8_t DPAD_LEFT = Gamepad::DPAD_LEFT ;
|
||||
uint8_t DPAD_RIGHT = Gamepad::DPAD_RIGHT ;
|
||||
uint8_t DPAD_UP_LEFT = Gamepad::DPAD_UP_LEFT ;
|
||||
uint8_t DPAD_UP_RIGHT = Gamepad::DPAD_UP_RIGHT ;
|
||||
uint8_t DPAD_DOWN_LEFT = Gamepad::DPAD_DOWN_LEFT ;
|
||||
uint8_t DPAD_DOWN_RIGHT = Gamepad::DPAD_DOWN_RIGHT;
|
||||
uint8_t DPAD_NONE = Gamepad::DPAD_NONE ;
|
||||
|
||||
uint16_t BUTTON_A = Gamepad::BUTTON_A ;
|
||||
uint16_t BUTTON_B = Gamepad::BUTTON_B ;
|
||||
uint16_t BUTTON_X = Gamepad::BUTTON_X ;
|
||||
uint16_t BUTTON_Y = Gamepad::BUTTON_Y ;
|
||||
uint16_t BUTTON_L3 = Gamepad::BUTTON_L3 ;
|
||||
uint16_t BUTTON_R3 = Gamepad::BUTTON_R3 ;
|
||||
uint16_t BUTTON_BACK = Gamepad::BUTTON_BACK ;
|
||||
uint16_t BUTTON_START = Gamepad::BUTTON_START;
|
||||
uint16_t BUTTON_LB = Gamepad::BUTTON_LB ;
|
||||
uint16_t BUTTON_RB = Gamepad::BUTTON_RB ;
|
||||
uint16_t BUTTON_SYS = Gamepad::BUTTON_SYS ;
|
||||
uint16_t BUTTON_MISC = Gamepad::BUTTON_MISC ;
|
||||
|
||||
GamepadMapper() = default;
|
||||
~GamepadMapper() = default;
|
||||
|
||||
void set_profile(const UserProfile& profile)
|
||||
{
|
||||
set_profile_settings(profile);
|
||||
set_profile_mappings(profile);
|
||||
}
|
||||
|
||||
template <uint8_t bits = 0, typename T>
|
||||
inline std::pair<int16_t, int16_t> scale_joystick_r(T x, T y, bool invert_y = false) const
|
||||
{
|
||||
int16_t joy_x = 0;
|
||||
int16_t joy_y = 0;
|
||||
if constexpr (bits > 0)
|
||||
{
|
||||
joy_x = Range::scale_from_bits<int16_t, bits>(x);
|
||||
joy_y = Range::scale_from_bits<int16_t, bits>(y);
|
||||
}
|
||||
else if constexpr (!std::is_same_v<T, int16_t>)
|
||||
{
|
||||
joy_x = Range::scale<int16_t>(x);
|
||||
joy_y = Range::scale<int16_t>(y);
|
||||
}
|
||||
else
|
||||
{
|
||||
joy_x = x;
|
||||
joy_y = y;
|
||||
}
|
||||
|
||||
return joy_settings_r_en_
|
||||
? apply_joystick_settings(joy_x, joy_y, joy_settings_r_, invert_y)
|
||||
: std::make_pair(joy_x, (invert_y ? Range::invert(joy_y) : joy_y));
|
||||
}
|
||||
|
||||
template <uint8_t bits = 0, typename T>
|
||||
inline std::pair<int16_t, int16_t> scale_joystick_l(T x, T y, bool invert_y = false) const
|
||||
{
|
||||
int16_t joy_x = 0;
|
||||
int16_t joy_y = 0;
|
||||
if constexpr (bits > 0)
|
||||
{
|
||||
joy_x = Range::scale_from_bits<int16_t, bits>(x);
|
||||
joy_y = Range::scale_from_bits<int16_t, bits>(y);
|
||||
}
|
||||
else if constexpr (!std::is_same_v<T, int16_t>)
|
||||
{
|
||||
joy_x = Range::scale<int16_t>(x);
|
||||
joy_y = Range::scale<int16_t>(y);
|
||||
}
|
||||
else
|
||||
{
|
||||
joy_x = x;
|
||||
joy_y = y;
|
||||
}
|
||||
|
||||
return joy_settings_l_en_
|
||||
? apply_joystick_settings(joy_x, joy_y, joy_settings_l_, invert_y)
|
||||
: std::make_pair(joy_x, (invert_y ? Range::invert(joy_y) : joy_y));
|
||||
}
|
||||
|
||||
template <uint8_t bits = 0, typename T>
|
||||
inline uint8_t scale_trigger_l(T value) const
|
||||
{
|
||||
uint8_t trigger_value = 0;
|
||||
if constexpr (bits > 0)
|
||||
{
|
||||
trigger_value = Range::scale_from_bits<uint8_t, bits>(value);
|
||||
}
|
||||
else if constexpr (!std::is_same_v<T, uint8_t>)
|
||||
{
|
||||
trigger_value = Range::scale<uint8_t>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
trigger_value = value;
|
||||
}
|
||||
return trig_settings_l_en_
|
||||
? apply_trigger_settings(trigger_value, trig_settings_l_)
|
||||
: trigger_value;
|
||||
}
|
||||
|
||||
template <uint8_t bits = 0, typename T>
|
||||
inline uint8_t scale_trigger_r(T value) const
|
||||
{
|
||||
uint8_t trigger_value = 0;
|
||||
if constexpr (bits > 0)
|
||||
{
|
||||
trigger_value = Range::scale_from_bits<uint8_t, bits>(value);
|
||||
}
|
||||
else if constexpr (!std::is_same_v<T, uint8_t>)
|
||||
{
|
||||
trigger_value = Range::scale<uint8_t>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
trigger_value = value;
|
||||
}
|
||||
return trig_settings_r_en_
|
||||
? apply_trigger_settings(trigger_value, trig_settings_r_)
|
||||
: trigger_value;
|
||||
}
|
||||
|
||||
private:
|
||||
JoystickSettings joy_settings_l_;
|
||||
JoystickSettings joy_settings_r_;
|
||||
TriggerSettings trig_settings_l_;
|
||||
TriggerSettings trig_settings_r_;
|
||||
|
||||
bool joy_settings_l_en_{false};
|
||||
bool joy_settings_r_en_{false};
|
||||
bool trig_settings_l_en_{false};
|
||||
bool trig_settings_r_en_{false};
|
||||
|
||||
void set_profile_settings(const UserProfile& profile)
|
||||
{
|
||||
if ((joy_settings_l_en_ = !joy_settings_l_.is_same(profile.joystick_settings_l)))
|
||||
{
|
||||
joy_settings_l_.set_from_raw(profile.joystick_settings_l);
|
||||
//This needs to be addressed in the webapp, just multiply here for now
|
||||
joy_settings_l_.axis_restrict *= static_cast<int16_t>(100);
|
||||
joy_settings_l_.angle_restrict *= static_cast<int16_t>(100);
|
||||
joy_settings_l_.anti_dz_angular *= static_cast<int16_t>(100);
|
||||
}
|
||||
if ((joy_settings_r_en_ = !joy_settings_r_.is_same(profile.joystick_settings_r)))
|
||||
{
|
||||
joy_settings_r_.set_from_raw(profile.joystick_settings_r);
|
||||
//This needs to be addressed in the webapp, just multiply here for now
|
||||
joy_settings_r_.axis_restrict *= static_cast<int16_t>(100);
|
||||
joy_settings_r_.angle_restrict *= static_cast<int16_t>(100);
|
||||
joy_settings_r_.anti_dz_angular *= static_cast<int16_t>(100);
|
||||
}
|
||||
if ((trig_settings_l_en_ = !trig_settings_l_.is_same(profile.trigger_settings_l)))
|
||||
{
|
||||
trig_settings_l_.set_from_raw(profile.trigger_settings_l);
|
||||
}
|
||||
if ((trig_settings_r_en_ = !trig_settings_r_.is_same(profile.trigger_settings_r)))
|
||||
{
|
||||
trig_settings_r_.set_from_raw(profile.trigger_settings_r);
|
||||
}
|
||||
|
||||
OGXM_LOG("GamepadMapper: JoyL: %s, JoyR: %s, TrigL: %s, TrigR: %s\n",
|
||||
joy_settings_l_en_ ? "Enabled" : "Disabled",
|
||||
joy_settings_r_en_ ? "Enabled" : "Disabled",
|
||||
trig_settings_l_en_ ? "Enabled" : "Disabled",
|
||||
trig_settings_r_en_ ? "Enabled" : "Disabled");
|
||||
}
|
||||
|
||||
void set_profile_mappings(const UserProfile& profile)
|
||||
{
|
||||
DPAD_UP = profile.dpad_up;
|
||||
DPAD_DOWN = profile.dpad_down;
|
||||
DPAD_LEFT = profile.dpad_left;
|
||||
DPAD_RIGHT = profile.dpad_right;
|
||||
DPAD_UP_LEFT = profile.dpad_up | profile.dpad_left;
|
||||
DPAD_UP_RIGHT = profile.dpad_up | profile.dpad_right;
|
||||
DPAD_DOWN_LEFT = profile.dpad_down | profile.dpad_left;
|
||||
DPAD_DOWN_RIGHT = profile.dpad_down | profile.dpad_right;
|
||||
DPAD_NONE = 0;
|
||||
|
||||
BUTTON_A = profile.button_a;
|
||||
BUTTON_B = profile.button_b;
|
||||
BUTTON_X = profile.button_x;
|
||||
BUTTON_Y = profile.button_y;
|
||||
BUTTON_L3 = profile.button_l3;
|
||||
BUTTON_R3 = profile.button_r3;
|
||||
BUTTON_BACK = profile.button_back;
|
||||
BUTTON_START = profile.button_start;
|
||||
BUTTON_LB = profile.button_lb;
|
||||
BUTTON_RB = profile.button_rb;
|
||||
BUTTON_SYS = profile.button_sys;
|
||||
BUTTON_MISC = profile.button_misc;
|
||||
}
|
||||
|
||||
static inline std::pair<int16_t, int16_t> apply_joystick_settings(
|
||||
int16_t gp_joy_x,
|
||||
int16_t gp_joy_y,
|
||||
const JoystickSettings& set,
|
||||
bool invert_y)
|
||||
{
|
||||
static const Fix16
|
||||
FIX_0(0.0f),
|
||||
FIX_1(1.0f),
|
||||
FIX_2(2.0f),
|
||||
FIX_45(45.0f),
|
||||
FIX_90(90.0f),
|
||||
FIX_100(100.0f),
|
||||
FIX_180(180.0f),
|
||||
FIX_EPSILON(0.0001f),
|
||||
FIX_EPSILON2(0.001f),
|
||||
FIX_ELLIPSE_DEF(1.570796f),
|
||||
FIX_DIAG_DIVISOR(0.29289f);
|
||||
|
||||
Fix16 x = (set.invert_x ? Fix16(Range::invert(gp_joy_x)) : Fix16(gp_joy_x)) / Range::MAX<int16_t>;
|
||||
Fix16 y = ((set.invert_y ^ invert_y) ? Fix16(Range::invert(gp_joy_y)) : Fix16(gp_joy_y)) / Range::MAX<int16_t>;
|
||||
|
||||
const Fix16 abs_x = fix16::abs(x);
|
||||
const Fix16 abs_y = fix16::abs(y);
|
||||
const Fix16 inv_axis_restrict = FIX_1 / (FIX_1 - set.axis_restrict);
|
||||
|
||||
Fix16 rAngle = (abs_x < FIX_EPSILON)
|
||||
? FIX_90
|
||||
: fix16::rad2deg(fix16::abs(fix16::atan(y / x)));
|
||||
|
||||
Fix16 axial_x = (abs_x <= set.axis_restrict && rAngle > FIX_45)
|
||||
? FIX_0
|
||||
: ((abs_x - set.axis_restrict) * inv_axis_restrict);
|
||||
|
||||
Fix16 axial_y = (abs_y <= set.axis_restrict && rAngle <= FIX_45)
|
||||
? FIX_0
|
||||
: ((abs_y - set.axis_restrict) * inv_axis_restrict);
|
||||
|
||||
Fix16 in_magnitude = fix16::sqrt(fix16::sq(axial_x) + fix16::sq(axial_y));
|
||||
|
||||
if (in_magnitude < set.dz_inner)
|
||||
{
|
||||
return { 0, 0 };
|
||||
}
|
||||
|
||||
Fix16 angle =
|
||||
fix16::abs(axial_x) < FIX_EPSILON
|
||||
? FIX_90
|
||||
: fix16::rad2deg(fix16::abs(fix16::atan(axial_y / axial_x)));
|
||||
|
||||
Fix16 anti_r_scale = (set.anti_dz_square_y_scale == FIX_0) ? set.anti_dz_square : set.anti_dz_square_y_scale;
|
||||
Fix16 anti_dz_c = set.anti_dz_circle;
|
||||
|
||||
if (anti_r_scale > FIX_0 && anti_dz_c > FIX_0)
|
||||
{
|
||||
Fix16 anti_ellip_scale = anti_ellip_scale / anti_dz_c;
|
||||
Fix16 ellipse_angle = fix16::atan((FIX_1 / anti_ellip_scale) * fix16::tan(fix16::rad2deg(rAngle)));
|
||||
ellipse_angle = (ellipse_angle < FIX_0) ? FIX_ELLIPSE_DEF : ellipse_angle;
|
||||
|
||||
Fix16 ellipse_x = fix16::cos(ellipse_angle);
|
||||
Fix16 ellipse_y = fix16::sqrt(fix16::sq(anti_ellip_scale) * (FIX_1 - fix16::sq(ellipse_x)));
|
||||
anti_dz_c *= fix16::sqrt(fix16::sq(ellipse_x) + fix16::sq(ellipse_y));
|
||||
}
|
||||
|
||||
if (anti_dz_c > FIX_0)
|
||||
{
|
||||
anti_dz_c = anti_dz_c / ((anti_dz_c * (FIX_1 - set.anti_dz_circle / set.dz_outer)) / (anti_dz_c * (FIX_1 - set.anti_dz_square)));
|
||||
}
|
||||
|
||||
if (abs_x > set.axis_restrict && abs_y > set.axis_restrict)
|
||||
{
|
||||
const Fix16 FIX_ANGLE_MAX = set.angle_restrict / 2.0f;
|
||||
|
||||
if (angle > FIX_0 && angle < FIX_ANGLE_MAX)
|
||||
{
|
||||
angle = FIX_0;
|
||||
}
|
||||
if (angle > (FIX_90 - FIX_ANGLE_MAX))
|
||||
{
|
||||
angle = FIX_90;
|
||||
}
|
||||
if (angle > FIX_ANGLE_MAX && angle < (FIX_90 - FIX_ANGLE_MAX))
|
||||
{
|
||||
angle = ((angle - FIX_ANGLE_MAX) * FIX_90) / ((FIX_90 - FIX_ANGLE_MAX) - FIX_ANGLE_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
Fix16 ref_angle = (angle < FIX_EPSILON2) ? FIX_0 : angle;
|
||||
Fix16 diagonal = (angle > FIX_45) ? (((angle - FIX_45) * (-FIX_45)) / FIX_45) + FIX_45 : angle;
|
||||
|
||||
const Fix16 angle_comp = set.angle_restrict / FIX_2;
|
||||
|
||||
if (angle < FIX_90 && angle > FIX_0)
|
||||
{
|
||||
angle = ((angle * ((FIX_90 - angle_comp) - angle_comp)) / FIX_90) + angle_comp;
|
||||
}
|
||||
|
||||
if (axial_x < FIX_0 && axial_y > FIX_0)
|
||||
{
|
||||
angle = -angle;
|
||||
}
|
||||
if (axial_x > FIX_0 && axial_y < FIX_0)
|
||||
{
|
||||
angle = angle - FIX_180;
|
||||
}
|
||||
if (axial_x < FIX_0 && axial_y < FIX_0)
|
||||
{
|
||||
angle = angle + FIX_180;
|
||||
}
|
||||
|
||||
//Deadzone Warp
|
||||
Fix16 out_magnitude = (in_magnitude - set.dz_inner) / (set.anti_dz_outer - set.dz_inner);
|
||||
out_magnitude = fix16::pow(out_magnitude, (FIX_1 / set.curve)) * (set.dz_outer - anti_dz_c) + anti_dz_c;
|
||||
out_magnitude = (out_magnitude > set.dz_outer && !set.uncap_radius) ? set.dz_outer : out_magnitude;
|
||||
|
||||
Fix16 d_scale = (((out_magnitude - anti_dz_c) * (set.diag_scale_max - set.diag_scale_min)) / (set.dz_outer - anti_dz_c)) + set.diag_scale_min;
|
||||
Fix16 c_scale = (diagonal * (FIX_1 / fix16::sqrt(FIX_2))) / FIX_45; //Both these lines scale the intensity of the warping
|
||||
c_scale = FIX_1 - fix16::sqrt(FIX_1 - c_scale * c_scale); //based on a circular curve to the perfect diagonal
|
||||
d_scale = (c_scale * (d_scale - FIX_1)) / FIX_DIAG_DIVISOR + FIX_1;
|
||||
|
||||
out_magnitude = out_magnitude * d_scale;
|
||||
|
||||
//Scaling values for square antideadzone
|
||||
Fix16 new_x = fix16::cos(fix16::deg2rad(angle)) * out_magnitude;
|
||||
Fix16 new_y = fix16::sin(fix16::deg2rad(angle)) * out_magnitude;
|
||||
|
||||
//Magic angle wobble fix by user ME.
|
||||
// if (angle > 45.0 && angle < 225.0) {
|
||||
// newX = inv(Math.sin(deg2rad(angle - 90.0)))*outputMagnitude;
|
||||
// newY = inv(Math.cos(deg2rad(angle - 270.0)))*outputMagnitude;
|
||||
// }
|
||||
|
||||
//Square antideadzone scaling
|
||||
Fix16 output_x = fix16::abs(new_x) * (FIX_1 - set.anti_dz_square / set.dz_outer) + set.anti_dz_square;
|
||||
if (x < FIX_0)
|
||||
{
|
||||
output_x = -output_x;
|
||||
}
|
||||
if (ref_angle == FIX_90)
|
||||
{
|
||||
output_x = FIX_0;
|
||||
}
|
||||
|
||||
Fix16 output_y = fix16::abs(new_y) * (FIX_1 - anti_r_scale / set.dz_outer) + anti_r_scale;
|
||||
if (y < FIX_0)
|
||||
{
|
||||
output_y = -output_y;
|
||||
}
|
||||
if (ref_angle == FIX_0)
|
||||
{
|
||||
output_y = FIX_0;
|
||||
}
|
||||
|
||||
output_x = fix16::clamp(output_x, -FIX_1, FIX_1) * Range::MAX<int16_t>;
|
||||
output_y = fix16::clamp(output_y, -FIX_1, FIX_1) * Range::MAX<int16_t>;
|
||||
|
||||
return { static_cast<int16_t>(fix16_to_int(output_x)), static_cast<int16_t>(fix16_to_int(output_y)) };
|
||||
}
|
||||
|
||||
static inline uint8_t apply_trigger_settings(uint8_t value, const TriggerSettings& set)
|
||||
{
|
||||
Fix16 abs_value = fix16::abs(Fix16(static_cast<int16_t>(value)) / static_cast<int16_t>(Range::MAX<uint8_t>));
|
||||
|
||||
if (abs_value < set.dz_inner)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const Fix16
|
||||
FIX_0(0.0f),
|
||||
FIX_1(1.0f),
|
||||
FIX_2(2.0f);
|
||||
|
||||
Fix16 value_out = (abs_value - set.dz_inner) / (set.anti_dz_outer - set.dz_inner);
|
||||
value_out = fix16::clamp(value_out, FIX_0, FIX_1);
|
||||
|
||||
if (set.anti_dz_inner > FIX_0)
|
||||
{
|
||||
value_out = set.anti_dz_inner + (FIX_1 - set.anti_dz_inner) * value_out;
|
||||
}
|
||||
if (set.curve != FIX_1)
|
||||
{
|
||||
value_out = fix16::pow(value_out, FIX_1 / set.curve);
|
||||
}
|
||||
if (set.anti_dz_outer < FIX_1)
|
||||
{
|
||||
value_out = fix16::clamp(value_out * (FIX_1 / (FIX_1 - set.anti_dz_outer)), FIX_0, FIX_1);
|
||||
}
|
||||
|
||||
value_out *= set.dz_outer;
|
||||
return static_cast<uint8_t>(fix16_to_int(value_out * static_cast<int16_t>(Range::MAX<uint8_t>)));
|
||||
}
|
||||
|
||||
}; // class GamepadMapper
|
||||
|
||||
#endif // GAMEPAD_H
|
||||
204
Firmware/ESP32/main/Gamepad/Range.h
Normal file
204
Firmware/ESP32/main/Gamepad/Range.h
Normal file
@@ -0,0 +1,204 @@
|
||||
#ifndef _RANGE_H_
|
||||
#define _RANGE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include "Board/ogxm_log.h"
|
||||
|
||||
namespace Range {
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T MAX = std::numeric_limits<T>::max();
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T MIN = std::numeric_limits<T>::min();
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T MID =
|
||||
[] {
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
return MAX<T> / 2 + 1;
|
||||
}
|
||||
return 0;
|
||||
}();
|
||||
static_assert(MID<uint8_t> == 128, "MID<uint8_t> != 128");
|
||||
static_assert(MID<int8_t> == 0, "MID<int8_t> != 0");
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr uint16_t NUM_BITS = sizeof(T) * 8;
|
||||
|
||||
//Maximum value for a given number of bits, result will be signed/unsigned depending on type
|
||||
template<typename T, uint8_t bits>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T BITS_MAX()
|
||||
{
|
||||
static_assert(bits <= NUM_BITS<T>, "BITS_MAX: Return type cannot represet maximum bit value");
|
||||
static_assert(bits <= 64, "BITS_MAX: Bits exceed 64");
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
return (1ULL << bits) - 1;
|
||||
}
|
||||
return (1LL << (bits - 1)) - 1;
|
||||
}
|
||||
static_assert(BITS_MAX<int16_t, 10>() == 511, "BITS_MAX<int16_t>(10) != 511");
|
||||
static_assert(BITS_MAX<uint16_t, 10>() == 1023, "BITS_MAX<uint16_t>(10) != 1023");
|
||||
|
||||
//Minimum value for a given number of bits, result will be signed/unsigned depending on type
|
||||
template<typename T, uint8_t bits>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T BITS_MIN()
|
||||
{
|
||||
static_assert(bits <= NUM_BITS<T>, "BITS_MIN: Return type cannot represet minimum bit value");
|
||||
static_assert(bits <= 64, "BITS_MIN: Bits exceed 64");
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return static_cast<T>(-(1LL << (bits - 1)));
|
||||
}
|
||||
static_assert(BITS_MIN<int16_t, 10>() == -512, "BITS_MIN<int16_t>(10) != -512");
|
||||
static_assert(BITS_MIN<uint16_t, 10>() == 0, "BITS_MIN<uint16_t>(10) != 0");
|
||||
|
||||
template <typename T>
|
||||
static inline T invert(T value)
|
||||
{
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
return Range::MAX<T> - value;
|
||||
}
|
||||
return (value == Range::MIN<T>) ? Range::MAX<T> : -value;
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
requires std::is_integral_v<To> && std::is_integral_v<From>
|
||||
static inline To clamp(From value)
|
||||
{
|
||||
return static_cast<To>((value < Range::MIN<To>)
|
||||
? Range::MIN<To>
|
||||
: (value > Range::MAX<To>)
|
||||
? Range::MAX<To>
|
||||
: value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T clamp(T value, T min, T max)
|
||||
{
|
||||
return (value < min) ? min : (value > max) ? max : value;
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
static inline To clamp(From value, To min_to, To max_to)
|
||||
{
|
||||
return (value < min_to) ? min_to : (value > max_to) ? max_to : static_cast<To>(value);
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
requires std::is_integral_v<To> && std::is_integral_v<From>
|
||||
static constexpr To scale(From value, From min_from, From max_from, To min_to, To max_to)
|
||||
{
|
||||
return static_cast<To>(
|
||||
(static_cast<int64_t>(value - min_from) * (max_to - min_to) / (max_from - min_from)) + min_to);
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
static inline To scale(From value)
|
||||
{
|
||||
return scale<To>(value, Range::MIN<From>, Range::MAX<From>, Range::MIN<To>, Range::MAX<To>);
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
static inline To scale(From value, To min_to, To max_to)
|
||||
{
|
||||
return scale<To>(value, Range::MIN<From>, Range::MAX<From>, min_to, max_to);
|
||||
}
|
||||
|
||||
//Cast value to signed/unsigned for accurate scaling
|
||||
template <typename To, uint8_t bits, typename From>
|
||||
static inline To scale_from_bits(From value)
|
||||
{
|
||||
return scale<To>(
|
||||
Range::clamp(value, BITS_MIN<From, bits>(), BITS_MAX<From, bits>()),
|
||||
BITS_MIN<From, bits>(),
|
||||
BITS_MAX<From, bits>(),
|
||||
Range::MIN<To>,
|
||||
Range::MAX<To>);
|
||||
}
|
||||
|
||||
//Cast value to signed/unsigned for accurate scaling
|
||||
template <typename To, uint8_t bits, typename From>
|
||||
static inline To scale_to_bits(From value)
|
||||
{
|
||||
return scale<To>(
|
||||
Range::clamp(value, BITS_MIN<From, bits>(), BITS_MAX<From, bits>()),
|
||||
Range::MIN<From>,
|
||||
Range::MAX<From>,
|
||||
BITS_MIN<To, bits>(),
|
||||
BITS_MAX<To, bits>());
|
||||
}
|
||||
|
||||
} // namespace Range
|
||||
|
||||
namespace Scale //Scale and invert values
|
||||
{
|
||||
static inline uint8_t int16_to_uint8(int16_t value)
|
||||
{
|
||||
uint16_t shifted_value = static_cast<uint16_t>(value + Range::MID<uint16_t>);
|
||||
return static_cast<uint8_t>(shifted_value >> 8);
|
||||
}
|
||||
static inline uint16_t int16_to_uint16(int16_t value)
|
||||
{
|
||||
return static_cast<uint16_t>(value + Range::MID<uint16_t>);
|
||||
}
|
||||
static inline int8_t int16_to_int8(int16_t value)
|
||||
{
|
||||
return static_cast<int8_t>((value + Range::MID<uint16_t>) >> 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 - Range::MID<uint16_t>);
|
||||
}
|
||||
static inline int8_t uint16_to_int8(uint16_t value)
|
||||
{
|
||||
return static_cast<int8_t>((value >> 8) - Range::MID<uint8_t>);
|
||||
}
|
||||
|
||||
static inline int16_t uint8_to_int16(uint8_t value)
|
||||
{
|
||||
return static_cast<int16_t>((static_cast<int32_t>(value) << 8) - Range::MID<uint16_t>);
|
||||
}
|
||||
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 - Range::MID<uint8_t>);
|
||||
}
|
||||
|
||||
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 + Range::MID<uint8_t>) << 8);
|
||||
}
|
||||
static inline uint8_t int8_to_uint8(int8_t value)
|
||||
{
|
||||
return static_cast<uint8_t>(value + Range::MID<uint8_t>);
|
||||
}
|
||||
|
||||
} // namespace Scale
|
||||
|
||||
#endif // _RANGE_H_
|
||||
106
Firmware/ESP32/main/Gamepad/fix16ext.h
Normal file
106
Firmware/ESP32/main/Gamepad/fix16ext.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef FIX16_EXT_H
|
||||
#define FIX16_EXT_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "libfixmath/fix16.hpp"
|
||||
|
||||
namespace fix16 {
|
||||
|
||||
inline Fix16 abs(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_abs(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 rad2deg(Fix16 rad)
|
||||
{
|
||||
return Fix16(fix16_rad_to_deg(rad.value));
|
||||
}
|
||||
|
||||
inline Fix16 deg2rad(Fix16 deg)
|
||||
{
|
||||
return Fix16(fix16_deg_to_rad(deg.value));
|
||||
}
|
||||
|
||||
inline Fix16 atan(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_atan(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 atan2(Fix16 y, Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_atan2(y.value, x.value));
|
||||
}
|
||||
|
||||
inline Fix16 tan(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_tan(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 cos(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_cos(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 sin(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_sin(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 sqrt(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_sqrt(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 sq(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_sq(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 clamp(Fix16 x, Fix16 min, Fix16 max)
|
||||
{
|
||||
return Fix16(fix16_clamp(x.value, min.value, max.value));
|
||||
}
|
||||
|
||||
inline Fix16 pow(Fix16 x, Fix16 y)
|
||||
{
|
||||
fix16_t& base = x.value;
|
||||
fix16_t& exponent = y.value;
|
||||
|
||||
if (exponent == F16(0.0))
|
||||
return Fix16(fix16_from_int(1));
|
||||
if (base == F16(0.0))
|
||||
return Fix16(fix16_from_int(0));
|
||||
|
||||
int32_t int_exp = fix16_to_int(exponent);
|
||||
|
||||
if (fix16_from_int(int_exp) == exponent)
|
||||
{
|
||||
fix16_t result = F16(1.0);
|
||||
fix16_t current_base = base;
|
||||
|
||||
if (int_exp < 0)
|
||||
{
|
||||
current_base = fix16_div(F16(1.0), base);
|
||||
int_exp = -int_exp;
|
||||
}
|
||||
|
||||
while (int_exp)
|
||||
{
|
||||
if (int_exp & 1)
|
||||
{
|
||||
result = fix16_mul(result, current_base);
|
||||
}
|
||||
current_base = fix16_mul(current_base, current_base);
|
||||
int_exp >>= 1;
|
||||
}
|
||||
|
||||
return Fix16(result);
|
||||
}
|
||||
|
||||
return Fix16(fix16_exp(fix16_mul(exponent, fix16_log(base))));
|
||||
}
|
||||
|
||||
} // namespace fix16
|
||||
|
||||
#endif // FIX16_EXT_H
|
||||
72
Firmware/ESP32/main/I2CDriver/I2CDriver.cpp
Normal file
72
Firmware/ESP32/main/I2CDriver/I2CDriver.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "I2CDriver/I2CDriver.h"
|
||||
|
||||
I2CDriver::~I2CDriver()
|
||||
{
|
||||
i2c_driver_delete(i2c_port_);
|
||||
}
|
||||
|
||||
void I2CDriver::initialize_i2c(i2c_port_t i2c_port, gpio_num_t sda, gpio_num_t scl, uint32_t clk_speed)
|
||||
{
|
||||
if (initialized_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
i2c_port_ = i2c_port;
|
||||
|
||||
i2c_config_t conf;
|
||||
std::memset(&conf, 0, sizeof(i2c_config_t));
|
||||
|
||||
conf.mode = I2C_MODE_MASTER;
|
||||
conf.sda_io_num = sda;
|
||||
conf.scl_io_num = scl;
|
||||
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf.master.clk_speed = clk_speed;
|
||||
|
||||
i2c_param_config(i2c_port_, &conf);
|
||||
i2c_driver_install(i2c_port_, conf.mode, 0, 0, 0);
|
||||
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
void I2CDriver::run_tasks()
|
||||
{
|
||||
std::function<void()> task;
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (task_queue_.pop(task))
|
||||
{
|
||||
task();
|
||||
}
|
||||
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
void I2CDriver::write_packet(uint8_t address, const PacketIn& data_in)
|
||||
{
|
||||
task_queue_.push([this, address, data_in]()
|
||||
{
|
||||
i2c_write_blocking(address, reinterpret_cast<const uint8_t*>(&data_in), sizeof(PacketIn));
|
||||
});
|
||||
}
|
||||
|
||||
void I2CDriver::read_packet(uint8_t address, std::function<void(const PacketOut&)> callback)
|
||||
{
|
||||
task_queue_.push([this, address, callback]()
|
||||
{
|
||||
PacketOut data_out;
|
||||
if (i2c_read_blocking(address, reinterpret_cast<uint8_t*>(&data_out), sizeof(PacketOut)) == ESP_OK)
|
||||
{
|
||||
callback(data_out);
|
||||
}
|
||||
});
|
||||
}
|
||||
108
Firmware/ESP32/main/I2CDriver/I2CDriver.h
Normal file
108
Firmware/ESP32/main/I2CDriver/I2CDriver.h
Normal file
@@ -0,0 +1,108 @@
|
||||
#ifndef _I2C_DRIVER_H_
|
||||
#define _I2C_DRIVER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <driver/i2c.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "UserSettings/DeviceDriverTypes.h"
|
||||
|
||||
class I2CDriver
|
||||
{
|
||||
public:
|
||||
static constexpr bool MULTI_SLAVE =
|
||||
#if CONFIG_MULTI_SLAVE_MODE == 0
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
#endif
|
||||
|
||||
enum class PacketID : uint8_t { UNKNOWN = 0, SET_PAD, GET_PAD, SET_DRIVER };
|
||||
enum class PacketResp : uint8_t { OK = 1, ERROR };
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PacketIn
|
||||
{
|
||||
uint8_t packet_len{sizeof(PacketIn)};
|
||||
PacketID packet_id{static_cast<uint8_t>(PacketID::SET_PAD)};
|
||||
uint8_t index{0};
|
||||
DeviceDriverType device_driver{DeviceDriverType::NONE};
|
||||
uint8_t dpad{0};
|
||||
uint16_t buttons{0};
|
||||
uint8_t trigger_l{0};
|
||||
uint8_t trigger_r{0};
|
||||
int16_t joystick_lx{0};
|
||||
int16_t joystick_ly{0};
|
||||
int16_t joystick_rx{0};
|
||||
int16_t joystick_ry{0};
|
||||
std::array<uint8_t, 15> reserved1{0};
|
||||
};
|
||||
static_assert(sizeof(PacketIn) == 32, "PacketIn is misaligned");
|
||||
|
||||
struct PacketOut
|
||||
{
|
||||
uint8_t packet_len{0};
|
||||
PacketID packet_id{0};
|
||||
uint8_t index{0};
|
||||
uint8_t rumble_l{0};
|
||||
uint8_t rumble_r{0};
|
||||
std::array<uint8_t, 3> reserved{0};
|
||||
};
|
||||
static_assert(sizeof(PacketOut) == 8, "PacketOut is misaligned");
|
||||
#pragma pack(pop)
|
||||
|
||||
I2CDriver() = default;
|
||||
~I2CDriver();
|
||||
|
||||
void initialize_i2c(i2c_port_t i2c_port, gpio_num_t sda, gpio_num_t scl, uint32_t clk_speed);
|
||||
|
||||
//Does not return
|
||||
void run_tasks();
|
||||
|
||||
void write_packet(uint8_t address, const PacketIn& data_in);
|
||||
void read_packet(uint8_t address, std::function<void(const PacketOut&)> callback);
|
||||
|
||||
private:
|
||||
using TaskQueue = RingBuffer<std::function<void()>, CONFIG_I2C_RING_BUFFER_SIZE>;
|
||||
|
||||
TaskQueue task_queue_;
|
||||
i2c_port_t i2c_port_ = I2C_NUM_0;
|
||||
bool initialized_ = false;
|
||||
|
||||
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;
|
||||
}
|
||||
}; // class I2CDriver
|
||||
|
||||
#endif // _I2C_DRIVER_H_
|
||||
70
Firmware/ESP32/main/Kconfig
Normal file
70
Firmware/ESP32/main/Kconfig
Normal file
@@ -0,0 +1,70 @@
|
||||
menu "OGXMini Options"
|
||||
|
||||
config I2C_RING_BUFFER_SIZE
|
||||
int "Set I2C ring buffer size"
|
||||
default 6
|
||||
|
||||
config I2C_PORT
|
||||
int "Set I2C port"
|
||||
default 0
|
||||
|
||||
config I2C_SDA_PIN
|
||||
int "Set I2C SDA pin"
|
||||
default 21
|
||||
|
||||
config I2C_SCL_PIN
|
||||
int "Set I2C SCL pin"
|
||||
default 22
|
||||
|
||||
config I2C_BAUDRATE
|
||||
int "Set I2C baudrate"
|
||||
default 1000000
|
||||
|
||||
config RESET_PIN
|
||||
int "Set reset pin"
|
||||
default 9
|
||||
|
||||
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
|
||||
55
Firmware/ESP32/main/RingBuffer.h
Normal file
55
Firmware/ESP32/main/RingBuffer.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef _RING_BUFFER_H_
|
||||
#define _RING_BUFFER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
|
||||
template<typename Type, size_t SIZE>
|
||||
class RingBuffer
|
||||
{
|
||||
public:
|
||||
RingBuffer() : head_(0), tail_(0) {}
|
||||
|
||||
RingBuffer(const RingBuffer&) = delete;
|
||||
RingBuffer& operator=(const RingBuffer&) = delete;
|
||||
|
||||
RingBuffer(RingBuffer&&) = default;
|
||||
RingBuffer& operator=(RingBuffer&&) = default;
|
||||
|
||||
bool push(const Type& item)
|
||||
{
|
||||
size_t head = head_.load(std::memory_order_relaxed);
|
||||
size_t next_head = (head + 1) % SIZE;
|
||||
|
||||
if (next_head == tail_.load(std::memory_order_acquire))
|
||||
{
|
||||
tail_.store((tail_.load(std::memory_order_relaxed) + 1) % SIZE, std::memory_order_release);
|
||||
}
|
||||
|
||||
buffer_[head] = item;
|
||||
head_.store(next_head, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pop(Type& item)
|
||||
{
|
||||
size_t tail = tail_.load(std::memory_order_relaxed);
|
||||
|
||||
if (tail == head_.load(std::memory_order_acquire))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
item = buffer_[tail];
|
||||
tail_.store((tail + 1) % SIZE, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<Type, SIZE> buffer_;
|
||||
std::atomic<size_t> head_;
|
||||
std::atomic<size_t> tail_;
|
||||
};
|
||||
|
||||
#endif // _RING_BUFFER_H_
|
||||
39
Firmware/ESP32/main/UserSettings/DeviceDriverTypes.h
Normal file
39
Firmware/ESP32/main/UserSettings/DeviceDriverTypes.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef _DEVICE_DRIVER_TYPES_H_
|
||||
#define _DEVICE_DRIVER_TYPES_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class DeviceDriverType : uint8_t
|
||||
{
|
||||
NONE = 0,
|
||||
XBOXOG,
|
||||
XBOXOG_SB,
|
||||
XBOXOG_XR,
|
||||
XINPUT,
|
||||
PS3,
|
||||
DINPUT,
|
||||
PSCLASSIC,
|
||||
SWITCH,
|
||||
WEBAPP = 100,
|
||||
UART_BRIDGE
|
||||
};
|
||||
|
||||
static inline std::string DRIVER_NAME(DeviceDriverType driver)
|
||||
{
|
||||
switch (driver)
|
||||
{
|
||||
case DeviceDriverType::XBOXOG: return "XBOX OG";
|
||||
case DeviceDriverType::XBOXOG_SB: return "XBOX OG SB";
|
||||
case DeviceDriverType::XBOXOG_XR: return "XBOX OG XR";
|
||||
case DeviceDriverType::XINPUT: return "XINPUT";
|
||||
case DeviceDriverType::PS3: return "PS3";
|
||||
case DeviceDriverType::DINPUT: return "DINPUT";
|
||||
case DeviceDriverType::PSCLASSIC: return "PS CLASSIC";
|
||||
case DeviceDriverType::SWITCH: return "SWITCH";
|
||||
case DeviceDriverType::WEBAPP: return "WEBAPP";
|
||||
case DeviceDriverType::UART_BRIDGE: return "UART BRIDGE";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _DEVICE_DRIVER_TYPES_H_
|
||||
63
Firmware/ESP32/main/UserSettings/JoystickSettings.cpp
Normal file
63
Firmware/ESP32/main/UserSettings/JoystickSettings.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "Board/ogxm_log.h"
|
||||
#include "UserSettings/JoystickSettings.h"
|
||||
|
||||
bool JoystickSettings::is_same(const JoystickSettingsRaw& raw) const
|
||||
{
|
||||
return dz_inner == Fix16(raw.dz_inner) &&
|
||||
dz_outer == Fix16(raw.dz_outer) &&
|
||||
anti_dz_circle == Fix16(raw.anti_dz_circle) &&
|
||||
anti_dz_circle_y_scale == Fix16(raw.anti_dz_circle_y_scale) &&
|
||||
anti_dz_square == Fix16(raw.anti_dz_square) &&
|
||||
anti_dz_square_y_scale == Fix16(raw.anti_dz_square_y_scale) &&
|
||||
anti_dz_angular == Fix16(raw.anti_dz_angular) &&
|
||||
anti_dz_outer == Fix16(raw.anti_dz_outer) &&
|
||||
axis_restrict == Fix16(raw.axis_restrict) &&
|
||||
angle_restrict == Fix16(raw.angle_restrict) &&
|
||||
diag_scale_min == Fix16(raw.diag_scale_min) &&
|
||||
diag_scale_max == Fix16(raw.diag_scale_max) &&
|
||||
curve == Fix16(raw.curve) &&
|
||||
uncap_radius == raw.uncap_radius &&
|
||||
invert_y == raw.invert_y &&
|
||||
invert_x == raw.invert_x;
|
||||
}
|
||||
|
||||
void JoystickSettings::set_from_raw(const JoystickSettingsRaw& raw)
|
||||
{
|
||||
dz_inner = Fix16(raw.dz_inner);
|
||||
dz_outer = Fix16(raw.dz_outer);
|
||||
anti_dz_circle = Fix16(raw.anti_dz_circle);
|
||||
anti_dz_circle_y_scale = Fix16(raw.anti_dz_circle_y_scale);
|
||||
anti_dz_square = Fix16(raw.anti_dz_square);
|
||||
anti_dz_square_y_scale = Fix16(raw.anti_dz_square_y_scale);
|
||||
anti_dz_angular = Fix16(raw.anti_dz_angular);
|
||||
anti_dz_outer = Fix16(raw.anti_dz_outer);
|
||||
axis_restrict = Fix16(raw.axis_restrict);
|
||||
angle_restrict = Fix16(raw.angle_restrict);
|
||||
diag_scale_min = Fix16(raw.diag_scale_min);
|
||||
diag_scale_max = Fix16(raw.diag_scale_max);
|
||||
curve = Fix16(raw.curve);
|
||||
uncap_radius = raw.uncap_radius;
|
||||
invert_y = raw.invert_y;
|
||||
invert_x = raw.invert_x;
|
||||
}
|
||||
|
||||
void JoystickSettingsRaw::log_values()
|
||||
{
|
||||
OGXM_LOG("dz_inner: %f\n", fix16_to_float(dz_inner));
|
||||
OGXM_LOG_HEX("dz_inner: ", reinterpret_cast<uint8_t*>(&dz_inner), sizeof(dz_inner));
|
||||
OGXM_LOG("dz_outer: %f\n", fix16_to_float(dz_outer));
|
||||
OGXM_LOG("anti_dz_circle: %f\n", fix16_to_float(anti_dz_circle));
|
||||
OGXM_LOG("anti_dz_circle_y_scale: %f\n", fix16_to_float(anti_dz_circle_y_scale));
|
||||
OGXM_LOG("anti_dz_square: %f\n", fix16_to_float(anti_dz_square));
|
||||
OGXM_LOG("anti_dz_square_y_scale: %f\n", fix16_to_float(anti_dz_square_y_scale));
|
||||
OGXM_LOG("anti_dz_angular: %f\n", fix16_to_float(anti_dz_angular));
|
||||
OGXM_LOG("anti_dz_outer: %f\n", fix16_to_float(anti_dz_outer));
|
||||
OGXM_LOG("axis_restrict: %f\n", fix16_to_float(axis_restrict));
|
||||
OGXM_LOG("angle_restrict: %f\n", fix16_to_float(angle_restrict));
|
||||
OGXM_LOG("diag_scale_min: %f\n", fix16_to_float(diag_scale_min));
|
||||
OGXM_LOG("diag_scale_max: %f\n", fix16_to_float(diag_scale_max));
|
||||
OGXM_LOG("curve: %f\n", fix16_to_float(curve));
|
||||
OGXM_LOG("uncap_radius: %d\n", uncap_radius);
|
||||
OGXM_LOG("invert_y: %d\n", invert_y);
|
||||
OGXM_LOG("invert_x: %d\n", invert_x);
|
||||
}
|
||||
68
Firmware/ESP32/main/UserSettings/JoystickSettings.h
Normal file
68
Firmware/ESP32/main/UserSettings/JoystickSettings.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef _JOYSTICK_SETTINGS_H_
|
||||
#define _JOYSTICK_SETTINGS_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "libfixmath/fix16.hpp"
|
||||
|
||||
struct JoystickSettingsRaw;
|
||||
|
||||
struct JoystickSettings
|
||||
{
|
||||
Fix16 dz_inner{Fix16(0.0f)};
|
||||
Fix16 dz_outer{Fix16(1.0f)};
|
||||
|
||||
Fix16 anti_dz_circle{Fix16(0.0f)};
|
||||
Fix16 anti_dz_circle_y_scale{Fix16(0.0f)};
|
||||
Fix16 anti_dz_square{Fix16(0.0f)};
|
||||
Fix16 anti_dz_square_y_scale{Fix16(0.0f)};
|
||||
Fix16 anti_dz_angular{Fix16(0.0f)};
|
||||
Fix16 anti_dz_outer{Fix16(1.0f)};
|
||||
|
||||
Fix16 axis_restrict{Fix16(0.0f)};
|
||||
Fix16 angle_restrict{Fix16(0.0f)};
|
||||
|
||||
Fix16 diag_scale_min{Fix16(1.0f)};
|
||||
Fix16 diag_scale_max{Fix16(1.0f)};
|
||||
|
||||
Fix16 curve{Fix16(1.0f)};
|
||||
|
||||
bool uncap_radius{true};
|
||||
bool invert_y{false};
|
||||
bool invert_x{false};
|
||||
|
||||
bool is_same(const JoystickSettingsRaw& raw) const;
|
||||
void set_from_raw(const JoystickSettingsRaw& raw);
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct JoystickSettingsRaw
|
||||
{
|
||||
fix16_t dz_inner{fix16_from_int(0)};
|
||||
fix16_t dz_outer{fix16_from_int(1)};
|
||||
|
||||
fix16_t anti_dz_circle{fix16_from_int(0)};
|
||||
fix16_t anti_dz_circle_y_scale{fix16_from_int(0)};
|
||||
fix16_t anti_dz_square{fix16_from_int(0)};
|
||||
fix16_t anti_dz_square_y_scale{fix16_from_int(0)};
|
||||
fix16_t anti_dz_angular{fix16_from_int(0)};
|
||||
fix16_t anti_dz_outer{fix16_from_int(1)};
|
||||
|
||||
fix16_t axis_restrict{fix16_from_int(0)};
|
||||
fix16_t angle_restrict{fix16_from_int(0)};
|
||||
|
||||
fix16_t diag_scale_min{fix16_from_int(1)};
|
||||
fix16_t diag_scale_max{fix16_from_int(1)};
|
||||
|
||||
fix16_t curve{fix16_from_int(1)};
|
||||
|
||||
uint8_t uncap_radius{true};
|
||||
uint8_t invert_y{false};
|
||||
uint8_t invert_x{false};
|
||||
|
||||
void log_values();
|
||||
};
|
||||
static_assert(sizeof(JoystickSettingsRaw) == 55, "JoystickSettingsRaw is an unexpected size");
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif // _JOYSTICK_SETTINGS_H_
|
||||
113
Firmware/ESP32/main/UserSettings/NVSHelper.h
Normal file
113
Firmware/ESP32/main/UserSettings/NVSHelper.h
Normal file
@@ -0,0 +1,113 @@
|
||||
#ifndef _NVS_HELPER_H_
|
||||
#define _NVS_HELPER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <nvs.h>
|
||||
#include <esp_err.h>
|
||||
|
||||
class NVSHelper
|
||||
{
|
||||
public:
|
||||
static NVSHelper& get_instance()
|
||||
{
|
||||
static NVSHelper instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
esp_err_t write(const std::string& key, const void* value, size_t len)
|
||||
{
|
||||
esp_err_t err;
|
||||
nvs_handle_t handle;
|
||||
|
||||
xSemaphoreTake(nvs_mutex_, portMAX_DELAY);
|
||||
|
||||
if ((err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle)) != ESP_OK)
|
||||
{
|
||||
xSemaphoreGive(nvs_mutex_);
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = nvs_set_blob(handle, key.c_str(), value, len)) != ESP_OK)
|
||||
{
|
||||
nvs_close(handle);
|
||||
xSemaphoreGive(nvs_mutex_);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = nvs_commit(handle);
|
||||
nvs_close(handle);
|
||||
|
||||
xSemaphoreGive(nvs_mutex_);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t read(const std::string& key, void* value, size_t len)
|
||||
{
|
||||
esp_err_t err;
|
||||
nvs_handle_t handle;
|
||||
|
||||
xSemaphoreTake(nvs_mutex_, portMAX_DELAY);
|
||||
|
||||
if ((err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle)) != ESP_OK)
|
||||
{
|
||||
xSemaphoreGive(nvs_mutex_);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = nvs_get_blob(handle, key.c_str(), value, &len);
|
||||
nvs_close(handle);
|
||||
|
||||
xSemaphoreGive(nvs_mutex_);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t erase_all()
|
||||
{
|
||||
esp_err_t err;
|
||||
nvs_handle_t handle;
|
||||
|
||||
xSemaphoreTake(nvs_mutex_, portMAX_DELAY);
|
||||
|
||||
if ((err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle)) != ESP_OK)
|
||||
{
|
||||
xSemaphoreGive(nvs_mutex_);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = nvs_erase_all(handle);
|
||||
nvs_close(handle);
|
||||
|
||||
xSemaphoreGive(nvs_mutex_);
|
||||
return err;
|
||||
}
|
||||
|
||||
private:
|
||||
NVSHelper()
|
||||
{
|
||||
nvs_mutex_ = xSemaphoreCreateMutex();
|
||||
xSemaphoreTake(nvs_mutex_, portMAX_DELAY);
|
||||
|
||||
if (nvs_flash_init() != ESP_OK)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
}
|
||||
|
||||
xSemaphoreGive(nvs_mutex_);
|
||||
}
|
||||
~NVSHelper() = default;
|
||||
NVSHelper(const NVSHelper&) = delete;
|
||||
NVSHelper& operator=(const NVSHelper&) = delete;
|
||||
|
||||
SemaphoreHandle_t nvs_mutex_;
|
||||
|
||||
static constexpr char NVS_NAMESPACE[] = "user_data";
|
||||
|
||||
}; // class NVSHelper
|
||||
|
||||
#endif // _NVS_HELPER_H_
|
||||
29
Firmware/ESP32/main/UserSettings/TriggerSettings.cpp
Normal file
29
Firmware/ESP32/main/UserSettings/TriggerSettings.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "Board/ogxm_log.h"
|
||||
#include "UserSettings/TriggerSettings.h"
|
||||
|
||||
bool TriggerSettings::is_same(const TriggerSettingsRaw& raw) const
|
||||
{
|
||||
return dz_inner == Fix16(raw.dz_inner) &&
|
||||
dz_outer == Fix16(raw.dz_outer) &&
|
||||
anti_dz_inner == Fix16(raw.anti_dz_inner) &&
|
||||
anti_dz_outer == Fix16(raw.anti_dz_outer) &&
|
||||
curve == Fix16(raw.curve);
|
||||
}
|
||||
|
||||
void TriggerSettings::set_from_raw(const TriggerSettingsRaw& raw)
|
||||
{
|
||||
dz_inner = Fix16(raw.dz_inner);
|
||||
dz_outer = Fix16(raw.dz_outer);
|
||||
anti_dz_inner = Fix16(raw.anti_dz_inner);
|
||||
anti_dz_outer = Fix16(raw.anti_dz_outer);
|
||||
curve = Fix16(raw.curve);
|
||||
}
|
||||
|
||||
void TriggerSettingsRaw::log_values()
|
||||
{
|
||||
OGXM_LOG("dz_inner: %f\n", fix16_to_float(dz_inner));
|
||||
OGXM_LOG("dz_outer: %f\n", fix16_to_float(dz_outer));
|
||||
OGXM_LOG("anti_dz_inner: %f\n", fix16_to_float(anti_dz_inner));
|
||||
OGXM_LOG("anti_dz_outer: %f\n", fix16_to_float(anti_dz_outer));
|
||||
OGXM_LOG("curve: %f\n", fix16_to_float(curve));
|
||||
}
|
||||
40
Firmware/ESP32/main/UserSettings/TriggerSettings.h
Normal file
40
Firmware/ESP32/main/UserSettings/TriggerSettings.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef TRIGGER_SETTINGS_H
|
||||
#define TRIGGER_SETTINGS_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "libfixmath/fix16.hpp"
|
||||
|
||||
struct TriggerSettingsRaw;
|
||||
|
||||
struct TriggerSettings
|
||||
{
|
||||
Fix16 dz_inner{Fix16(0.0f)};
|
||||
Fix16 dz_outer{Fix16(1.0f)};
|
||||
|
||||
Fix16 anti_dz_inner{Fix16(0.0f)};
|
||||
Fix16 anti_dz_outer{Fix16(1.0f)};
|
||||
|
||||
Fix16 curve{Fix16(1.0f)};
|
||||
|
||||
bool is_same(const TriggerSettingsRaw& raw) const;
|
||||
void set_from_raw(const TriggerSettingsRaw& raw);
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct TriggerSettingsRaw
|
||||
{
|
||||
fix16_t dz_inner{fix16_from_int(0)};
|
||||
fix16_t dz_outer{fix16_from_int(1)};
|
||||
|
||||
fix16_t anti_dz_inner{fix16_from_int(0)};
|
||||
fix16_t anti_dz_outer{fix16_from_int(1)};
|
||||
|
||||
fix16_t curve{fix16_from_int(1)};
|
||||
|
||||
void log_values();
|
||||
};
|
||||
static_assert(sizeof(TriggerSettingsRaw) == 20, "TriggerSettingsRaw is an unexpected size");
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif // TRIGGER_SETTINGS_H
|
||||
40
Firmware/ESP32/main/UserSettings/UserProfile.cpp
Normal file
40
Firmware/ESP32/main/UserSettings/UserProfile.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "Gamepad/Gamepad.h"
|
||||
#include "UserSettings/UserProfile.h"
|
||||
|
||||
UserProfile::UserProfile()
|
||||
{
|
||||
id = 1;
|
||||
|
||||
dpad_up = Gamepad::DPAD_UP;
|
||||
dpad_down = Gamepad::DPAD_DOWN;
|
||||
dpad_left = Gamepad::DPAD_LEFT;
|
||||
dpad_right = Gamepad::DPAD_RIGHT;
|
||||
|
||||
button_a = Gamepad::BUTTON_A;
|
||||
button_b = Gamepad::BUTTON_B;
|
||||
button_x = Gamepad::BUTTON_X;
|
||||
button_y = Gamepad::BUTTON_Y;
|
||||
button_l3 = Gamepad::BUTTON_L3;
|
||||
button_r3 = Gamepad::BUTTON_R3;
|
||||
button_back = Gamepad::BUTTON_BACK;
|
||||
button_start = Gamepad::BUTTON_START;
|
||||
button_lb = Gamepad::BUTTON_LB;
|
||||
button_rb = Gamepad::BUTTON_RB;
|
||||
button_sys = Gamepad::BUTTON_SYS;
|
||||
button_misc = Gamepad::BUTTON_MISC;
|
||||
|
||||
analog_enabled = 0;
|
||||
|
||||
analog_off_up = Gamepad::ANALOG_OFF_UP;
|
||||
analog_off_down = Gamepad::ANALOG_OFF_DOWN;
|
||||
analog_off_left = Gamepad::ANALOG_OFF_LEFT;
|
||||
analog_off_right = Gamepad::ANALOG_OFF_RIGHT;
|
||||
analog_off_a = Gamepad::ANALOG_OFF_A;
|
||||
analog_off_b = Gamepad::ANALOG_OFF_B;
|
||||
analog_off_x = Gamepad::ANALOG_OFF_X;
|
||||
analog_off_y = Gamepad::ANALOG_OFF_Y;
|
||||
analog_off_lb = Gamepad::ANALOG_OFF_LB;
|
||||
analog_off_rb = Gamepad::ANALOG_OFF_RB;
|
||||
}
|
||||
55
Firmware/ESP32/main/UserSettings/UserProfile.h
Normal file
55
Firmware/ESP32/main/UserSettings/UserProfile.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef _USER_PROFILE_H_
|
||||
#define _USER_PROFILE_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "UserSettings/JoystickSettings.h"
|
||||
#include "UserSettings/TriggerSettings.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct UserProfile
|
||||
{
|
||||
uint8_t id;
|
||||
|
||||
JoystickSettingsRaw joystick_settings_l;
|
||||
JoystickSettingsRaw joystick_settings_r;
|
||||
TriggerSettingsRaw trigger_settings_l;
|
||||
TriggerSettingsRaw trigger_settings_r;
|
||||
|
||||
uint8_t dpad_up;
|
||||
uint8_t dpad_down;
|
||||
uint8_t dpad_left;
|
||||
uint8_t dpad_right;
|
||||
|
||||
uint16_t button_a;
|
||||
uint16_t button_b;
|
||||
uint16_t button_x;
|
||||
uint16_t button_y;
|
||||
uint16_t button_l3;
|
||||
uint16_t button_r3;
|
||||
uint16_t button_back;
|
||||
uint16_t button_start;
|
||||
uint16_t button_lb;
|
||||
uint16_t button_rb;
|
||||
uint16_t button_sys;
|
||||
uint16_t button_misc;
|
||||
|
||||
uint8_t analog_enabled;
|
||||
|
||||
uint8_t analog_off_up;
|
||||
uint8_t analog_off_down;
|
||||
uint8_t analog_off_left;
|
||||
uint8_t analog_off_right;
|
||||
uint8_t analog_off_a;
|
||||
uint8_t analog_off_b;
|
||||
uint8_t analog_off_x;
|
||||
uint8_t analog_off_y;
|
||||
uint8_t analog_off_lb;
|
||||
uint8_t analog_off_rb;
|
||||
|
||||
UserProfile();
|
||||
};
|
||||
static_assert(sizeof(UserProfile) == 190, "UserProfile struct size mismatch");
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif // _USER_PROFILE_H_
|
||||
273
Firmware/ESP32/main/UserSettings/UserSettings.cpp
Normal file
273
Firmware/ESP32/main/UserSettings/UserSettings.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <nvs_flash.h>
|
||||
#include <nvs.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "Gamepad/Gamepad.h"
|
||||
#include "UserSettings/NVSHelper.h"
|
||||
|
||||
static constexpr uint32_t BUTTON_COMBO(const uint16_t& buttons, const uint8_t& dpad = 0)
|
||||
{
|
||||
return (static_cast<uint32_t>(buttons) << 16) | static_cast<uint32_t>(dpad);
|
||||
}
|
||||
|
||||
namespace ButtonCombo
|
||||
{
|
||||
static constexpr uint32_t PS3 = BUTTON_COMBO(Gamepad::BUTTON_START, Gamepad::DPAD_LEFT);
|
||||
static constexpr uint32_t DINPUT = BUTTON_COMBO(Gamepad::BUTTON_START | Gamepad::BUTTON_RB, Gamepad::DPAD_LEFT);
|
||||
static constexpr uint32_t XINPUT = BUTTON_COMBO(Gamepad::BUTTON_START, Gamepad::DPAD_UP);
|
||||
static constexpr uint32_t SWITCH = BUTTON_COMBO(Gamepad::BUTTON_START, Gamepad::DPAD_DOWN);
|
||||
static constexpr uint32_t XBOXOG = BUTTON_COMBO(Gamepad::BUTTON_START, Gamepad::DPAD_RIGHT);
|
||||
static constexpr uint32_t XBOXOG_SB = BUTTON_COMBO(Gamepad::BUTTON_START | Gamepad::BUTTON_RB, Gamepad::DPAD_RIGHT);
|
||||
static constexpr uint32_t XBOXOG_XR = BUTTON_COMBO(Gamepad::BUTTON_START | Gamepad::BUTTON_LB, Gamepad::DPAD_RIGHT);
|
||||
static constexpr uint32_t PSCLASSIC = BUTTON_COMBO(Gamepad::BUTTON_START | Gamepad::BUTTON_A);
|
||||
static constexpr uint32_t WEBAPP = BUTTON_COMBO(Gamepad::BUTTON_START | Gamepad::BUTTON_LB | Gamepad::BUTTON_RB);
|
||||
};
|
||||
|
||||
static constexpr DeviceDriverType VALID_DRIVER_TYPES[] =
|
||||
{
|
||||
#if MAX_GAMEPADS > 1
|
||||
DeviceDriverType::DINPUT,
|
||||
DeviceDriverType::SWITCH,
|
||||
|
||||
#else // MAX_GAMEPADS == 1
|
||||
DeviceDriverType::XBOXOG,
|
||||
// DeviceDriverType::XBOXOG_SB,
|
||||
DeviceDriverType::XBOXOG_XR,
|
||||
DeviceDriverType::DINPUT,
|
||||
DeviceDriverType::SWITCH,
|
||||
DeviceDriverType::PS3,
|
||||
DeviceDriverType::PSCLASSIC,
|
||||
DeviceDriverType::XINPUT,
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ComboMap { uint32_t combo; DeviceDriverType driver; };
|
||||
static constexpr std::array<ComboMap, 9> BUTTON_COMBO_MAP =
|
||||
{{
|
||||
{ ButtonCombo::XBOXOG, DeviceDriverType::XBOXOG },
|
||||
{ ButtonCombo::XBOXOG_SB, DeviceDriverType::XBOXOG_SB },
|
||||
{ ButtonCombo::XBOXOG_XR, DeviceDriverType::XBOXOG_XR },
|
||||
{ ButtonCombo::WEBAPP, DeviceDriverType::WEBAPP },
|
||||
{ ButtonCombo::DINPUT, DeviceDriverType::DINPUT },
|
||||
{ ButtonCombo::SWITCH, DeviceDriverType::SWITCH },
|
||||
{ ButtonCombo::XINPUT, DeviceDriverType::XINPUT },
|
||||
{ ButtonCombo::PS3, DeviceDriverType::PS3 },
|
||||
{ ButtonCombo::PSCLASSIC, DeviceDriverType::PSCLASSIC }
|
||||
}};
|
||||
|
||||
const std::string UserSettings::INIT_FLAG_KEY()
|
||||
{
|
||||
return std::string("init_flag");
|
||||
}
|
||||
|
||||
const std::string UserSettings::PROFILE_KEY(const uint8_t profile_id)
|
||||
{
|
||||
return std::string("profile_") + std::to_string(profile_id);
|
||||
}
|
||||
|
||||
const std::string UserSettings::ACTIVE_PROFILE_KEY(const uint8_t index)
|
||||
{
|
||||
return std::string("active_id_") + std::to_string(index);
|
||||
}
|
||||
|
||||
const std::string UserSettings::DRIVER_TYPE_KEY()
|
||||
{
|
||||
return std::string("driver_type");
|
||||
}
|
||||
|
||||
const std::string UserSettings::FIRMWARE_VER_KEY()
|
||||
{
|
||||
return std::string("firmware_ver");
|
||||
}
|
||||
|
||||
void UserSettings::initialize_flash()
|
||||
{
|
||||
ESP_LOGD("UserSettings", "Checking for UserSettings init flag");
|
||||
|
||||
uint8_t init_flag = 0;
|
||||
|
||||
ESP_ERROR_CHECK(nvs_helper_.read(INIT_FLAG_KEY(), &init_flag, sizeof(init_flag)));
|
||||
|
||||
if (init_flag == INIT_FLAG)
|
||||
{
|
||||
ESP_LOGD("UserSettings", "UserSettings already initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(nvs_helper_.erase_all());
|
||||
OGXM_LOG("Initializing UserSettings\n");
|
||||
|
||||
current_driver_ = DEFAULT_DRIVER();
|
||||
uint8_t driver_type = static_cast<uint8_t>(current_driver_);
|
||||
ESP_ERROR_CHECK(nvs_helper_.write(DRIVER_TYPE_KEY(), &driver_type, sizeof(driver_type)));
|
||||
|
||||
uint8_t active_id = 1;
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; i++)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_helper_.write(ACTIVE_PROFILE_KEY(i), &active_id, sizeof(active_id)));
|
||||
}
|
||||
|
||||
UserProfile profile = UserProfile();
|
||||
for (uint8_t i = 0; i < MAX_PROFILES; i++)
|
||||
{
|
||||
profile.id = i + 1;
|
||||
ESP_ERROR_CHECK(nvs_helper_.write(PROFILE_KEY(profile.id), &profile, sizeof(UserProfile)));
|
||||
}
|
||||
|
||||
init_flag = INIT_FLAG;
|
||||
ESP_ERROR_CHECK(nvs_helper_.write(INIT_FLAG_KEY(), &init_flag, sizeof(init_flag)));
|
||||
}
|
||||
|
||||
DeviceDriverType UserSettings::get_current_driver()
|
||||
{
|
||||
if (current_driver_ != DeviceDriverType::NONE)
|
||||
{
|
||||
return current_driver_;
|
||||
}
|
||||
|
||||
uint8_t stored_type = 0;
|
||||
nvs_helper_.read(DRIVER_TYPE_KEY(), &stored_type, sizeof(stored_type));
|
||||
|
||||
if (is_valid_driver(static_cast<DeviceDriverType>(stored_type)))
|
||||
{
|
||||
current_driver_ = static_cast<DeviceDriverType>(stored_type);
|
||||
return current_driver_;
|
||||
}
|
||||
|
||||
current_driver_ = DEFAULT_DRIVER();
|
||||
return current_driver_;
|
||||
}
|
||||
|
||||
//Checks if button combo has been held for 3 seconds, returns true if mode has been changed
|
||||
bool UserSettings::check_for_driver_change(const I2CDriver::PacketIn& packet_in)
|
||||
{
|
||||
static uint32_t last_button_combo = BUTTON_COMBO(packet_in.buttons, packet_in.dpad);
|
||||
static uint8_t call_count = 0;
|
||||
|
||||
uint32_t current_button_combo = BUTTON_COMBO(packet_in.buttons, packet_in.dpad);
|
||||
|
||||
if (!(current_button_combo & (static_cast<uint32_t>(Gamepad::BUTTON_START) << 16)) ||
|
||||
last_button_combo != current_button_combo)
|
||||
{
|
||||
last_button_combo = current_button_combo;
|
||||
call_count = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
++call_count;
|
||||
|
||||
if (call_count < GP_CHECK_COUNT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
call_count = 0;
|
||||
|
||||
DeviceDriverType new_driver = DeviceDriverType::NONE;
|
||||
|
||||
for (const auto& combo : BUTTON_COMBO_MAP)
|
||||
{
|
||||
if (combo.combo == current_button_combo &&
|
||||
is_valid_driver(combo.driver))
|
||||
{
|
||||
new_driver = combo.driver;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_driver == DeviceDriverType::NONE || new_driver == current_driver_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
current_driver_ = new_driver;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UserSettings::store_driver_type(DeviceDriverType new_driver_type)
|
||||
{
|
||||
if (!is_valid_driver(new_driver_type))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t new_driver = static_cast<uint8_t>(new_driver_type);
|
||||
nvs_helper_.write(DRIVER_TYPE_KEY(), &new_driver, sizeof(new_driver));
|
||||
}
|
||||
|
||||
void UserSettings::store_profile(const uint8_t index, UserProfile& profile)
|
||||
{
|
||||
if (index > MAX_GAMEPADS || profile.id < 1 || profile.id > MAX_PROFILES)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OGXM_LOG("Storing profile %d for gamepad %d\n", profile.id, index);
|
||||
|
||||
if (nvs_helper_.write(PROFILE_KEY(profile.id), &profile, sizeof(UserProfile)) == ESP_OK)
|
||||
{
|
||||
OGXM_LOG("Profile %d stored successfully\n", profile.id);
|
||||
|
||||
nvs_helper_.write(ACTIVE_PROFILE_KEY(index), &profile.id, sizeof(profile.id));
|
||||
}
|
||||
}
|
||||
|
||||
void UserSettings::store_profile_and_driver_type(DeviceDriverType new_driver_type, const uint8_t index, UserProfile& profile)
|
||||
{
|
||||
store_driver_type(new_driver_type);
|
||||
store_profile(index, profile);
|
||||
}
|
||||
|
||||
uint8_t UserSettings::get_active_profile_id(const uint8_t index)
|
||||
{
|
||||
uint8_t read_profile_id = 0;
|
||||
|
||||
if (nvs_helper_.read(ACTIVE_PROFILE_KEY(index), &read_profile_id, sizeof(read_profile_id)) != ESP_OK ||
|
||||
read_profile_id < 1 ||
|
||||
read_profile_id > MAX_PROFILES)
|
||||
{
|
||||
return 0x01;
|
||||
}
|
||||
return read_profile_id;
|
||||
}
|
||||
|
||||
UserProfile UserSettings::get_profile_by_index(const uint8_t index)
|
||||
{
|
||||
return get_profile_by_id(get_active_profile_id(index));
|
||||
}
|
||||
|
||||
UserProfile UserSettings::get_profile_by_id(const uint8_t profile_id)
|
||||
{
|
||||
UserProfile profile;
|
||||
|
||||
if (profile_id < 1 ||
|
||||
profile_id > MAX_PROFILES ||
|
||||
nvs_helper_.read(PROFILE_KEY(profile_id), &profile, sizeof(UserProfile)) != ESP_OK)
|
||||
{
|
||||
return UserProfile();
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
DeviceDriverType UserSettings::DEFAULT_DRIVER()
|
||||
{
|
||||
return VALID_DRIVER_TYPES[0];
|
||||
}
|
||||
|
||||
bool UserSettings::is_valid_driver(DeviceDriverType mode)
|
||||
{
|
||||
for (const auto& driver : VALID_DRIVER_TYPES)
|
||||
{
|
||||
if (mode == driver)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
60
Firmware/ESP32/main/UserSettings/UserSettings.h
Normal file
60
Firmware/ESP32/main/UserSettings/UserSettings.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef _USER_SETTINGS_H_
|
||||
#define _USER_SETTINGS_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "I2CDriver/I2CDriver.h"
|
||||
#include "UserSettings/UserProfile.h"
|
||||
#include "UserSettings/DeviceDriverTypes.h"
|
||||
#include "UserSettings/NVSHelper.h"
|
||||
|
||||
class UserSettings
|
||||
{
|
||||
public:
|
||||
static constexpr uint8_t MAX_PROFILES = 8;
|
||||
static constexpr uint32_t GP_CHECK_DELAY_MS = 1000;
|
||||
|
||||
static UserSettings& get_instance()
|
||||
{
|
||||
static UserSettings instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void initialize_flash();
|
||||
|
||||
DeviceDriverType get_current_driver();
|
||||
bool check_for_driver_change(const I2CDriver::PacketIn& packet_in);
|
||||
|
||||
UserProfile get_profile_by_index(const uint8_t index);
|
||||
UserProfile get_profile_by_id(const uint8_t profile_id);
|
||||
uint8_t get_active_profile_id(const uint8_t index);
|
||||
|
||||
void store_driver_type(DeviceDriverType new_driver_type);
|
||||
void store_profile(uint8_t index, UserProfile& profile);
|
||||
void store_profile_and_driver_type(DeviceDriverType new_driver_type, uint8_t index, UserProfile& profile);
|
||||
|
||||
private:
|
||||
UserSettings() = default;
|
||||
~UserSettings() = default;
|
||||
UserSettings(const UserSettings&) = delete;
|
||||
UserSettings& operator=(const UserSettings&) = delete;
|
||||
|
||||
static constexpr uint8_t GP_CHECK_COUNT = 3000 / GP_CHECK_DELAY_MS;
|
||||
static constexpr uint8_t INIT_FLAG = 0x12;
|
||||
|
||||
NVSHelper& nvs_helper_{NVSHelper::get_instance()};
|
||||
DeviceDriverType current_driver_{DeviceDriverType::NONE};
|
||||
|
||||
bool is_valid_driver(DeviceDriverType mode);
|
||||
|
||||
DeviceDriverType DEFAULT_DRIVER();
|
||||
const std::string INIT_FLAG_KEY();
|
||||
const std::string PROFILE_KEY(const uint8_t profile_id);
|
||||
const std::string ACTIVE_PROFILE_KEY(const uint8_t index);
|
||||
const std::string DRIVER_TYPE_KEY();
|
||||
const std::string FIRMWARE_VER_KEY();
|
||||
};
|
||||
|
||||
#endif // _USER_SETTINGS_H_
|
||||
82
Firmware/ESP32/main/btstack_config.h
Normal file
82
Firmware/ESP32/main/btstack_config.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#ifndef _ESP_BTSTACK_CONFIG_H
|
||||
#define _ESP_BTSTACK_CONFIG_H
|
||||
|
||||
// BTstack features that can be enabled
|
||||
// #define ENABLE_LE_PERIPHERAL
|
||||
// #define ENABLE_LE_CENTRAL
|
||||
// #define ENABLE_L2CAP_LE_CREDIT_BASED_FLOW_CONTROL_MODE
|
||||
// #define ENABLE_LOG_INFO
|
||||
// #define ENABLE_LOG_ERROR
|
||||
// #define ENABLE_PRINTF_HEXDUMP
|
||||
#define ENABLE_SCO_OVER_HCI
|
||||
|
||||
// BTstack configuration. buffers, sizes, ...
|
||||
#define HCI_OUTGOING_PRE_BUFFER_SIZE 4
|
||||
#define HCI_ACL_PAYLOAD_SIZE (1691 + 4)
|
||||
#define HCI_ACL_CHUNK_SIZE_ALIGNMENT 4
|
||||
// #define MAX_NR_AVDTP_CONNECTIONS 1
|
||||
// #define MAX_NR_AVDTP_STREAM_ENDPOINTS 1
|
||||
// #define MAX_NR_AVRCP_CONNECTIONS 2
|
||||
// #define MAX_NR_BNEP_CHANNELS 1
|
||||
// #define MAX_NR_BNEP_SERVICES 1
|
||||
// #define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 2
|
||||
// #define MAX_NR_GATT_CLIENTS 4
|
||||
// #define MAX_NR_HCI_CONNECTIONS 4
|
||||
// #define MAX_NR_HID_HOST_CONNECTIONS 4
|
||||
// #define MAX_NR_HIDS_CLIENTS 4
|
||||
// #define MAX_NR_HFP_CONNECTIONS 1
|
||||
// #define MAX_NR_L2CAP_CHANNELS 6
|
||||
// #define MAX_NR_L2CAP_SERVICES 5
|
||||
// #define MAX_NR_RFCOMM_CHANNELS 1
|
||||
// #define MAX_NR_RFCOMM_MULTIPLEXERS 1
|
||||
// #define MAX_NR_RFCOMM_SERVICES 1
|
||||
// #define MAX_NR_SERVICE_RECORD_ITEMS 4
|
||||
// #define MAX_NR_SM_LOOKUP_ENTRIES 3
|
||||
// #define MAX_NR_WHITELIST_ENTRIES 16
|
||||
// #define MAX_NR_LE_DEVICE_DB_ENTRIES 16
|
||||
|
||||
// Limit number of ACL/SCO Buffer to use by stack to avoid cyw43 shared bus overrun
|
||||
#define MAX_NR_CONTROLLER_ACL_BUFFERS 6
|
||||
#define MAX_NR_CONTROLLER_SCO_PACKETS 6
|
||||
|
||||
// Enable and configure HCI Controller to Host Flow Control to avoid cyw43 shared bus overrun
|
||||
#define ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL
|
||||
#define HCI_HOST_ACL_PACKET_LEN 1024
|
||||
#define HCI_HOST_ACL_PACKET_NUM 6
|
||||
#define HCI_HOST_SCO_PACKET_LEN 120
|
||||
#define HCI_HOST_SCO_PACKET_NUM 6
|
||||
|
||||
#ifndef HCI_INCOMING_PRE_BUFFER_SIZE
|
||||
#define HCI_INCOMING_PRE_BUFFER_SIZE 6
|
||||
#endif
|
||||
|
||||
// Link Key DB and LE Device DB using TLV on top of Flash Sector interface
|
||||
// #define NVM_NUM_DEVICE_DB_ENTRIES 16
|
||||
// #define NVM_NUM_LINK_KEYS 16
|
||||
|
||||
// We don't give btstack a malloc, so use a fixed-size ATT DB.
|
||||
#define MAX_ATT_DB_SIZE 512
|
||||
// #define HAVE_MALLOC
|
||||
|
||||
// BTstack HAL configuration
|
||||
// #define HAVE_EMBEDDED_TIME_MS
|
||||
|
||||
// // map btstack_assert onto Pico SDK assert()
|
||||
// #define HAVE_ASSERT
|
||||
|
||||
// // Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A).
|
||||
#define HCI_RESET_RESEND_TIMEOUT_MS 1000
|
||||
|
||||
// #define ENABLE_SOFTWARE_AES128
|
||||
// #define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS
|
||||
|
||||
// #define HAVE_BTSTACK_STDIN
|
||||
|
||||
// To get the audio demos working even with HCI dump at 115200, this truncates long ACL packetws
|
||||
#define HCI_DUMP_STDOUT_MAX_SIZE_ACL 100
|
||||
|
||||
#ifdef ENABLE_CLASSIC
|
||||
#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
|
||||
#endif
|
||||
|
||||
#endif // _ESP_BTSTACK_CONFIG_H
|
||||
9
Firmware/ESP32/main/main.c
Normal file
9
Firmware/ESP32/main/main.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
cpp_main();
|
||||
}
|
||||
22
Firmware/ESP32/main/main.cpp
Normal file
22
Firmware/ESP32/main/main.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <functional>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "BTManager/BTManager.h"
|
||||
|
||||
void cpp_main()
|
||||
{
|
||||
xTaskCreatePinnedToCore(
|
||||
[](void* parameter)
|
||||
{
|
||||
BTManager::get_instance().run_task();
|
||||
},
|
||||
"bp32",
|
||||
2048 * 4,
|
||||
NULL,
|
||||
configMAX_PRIORITIES-4,
|
||||
NULL,
|
||||
0 );
|
||||
}
|
||||
16
Firmware/ESP32/main/main.h
Normal file
16
Firmware/ESP32/main/main.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef _C_WRAPPER_H_
|
||||
#define _C_WRAPPER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void cpp_main();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _C_WRAPPER_H_
|
||||
2420
Firmware/ESP32/sdkconfig
Normal file
2420
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
|
||||
#
|
||||
1
Firmware/ESP32_Blueretro
Submodule
1
Firmware/ESP32_Blueretro
Submodule
Submodule Firmware/ESP32_Blueretro added at bb98a87da7
2
Firmware/FWDefines.cmake
Normal file
2
Firmware/FWDefines.cmake
Normal file
@@ -0,0 +1,2 @@
|
||||
set(FW_NAME "OGX-Mini")
|
||||
set(FW_VERSION "v1.0.0a3")
|
||||
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
|
||||
}
|
||||
13
Firmware/RP2040/.vscode/settings.json
vendored
Normal file
13
Firmware/RP2040/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
|
||||
|
||||
"cmake.configureArgs": [
|
||||
"-DOGXM_BOARD=ESP32_BLUERETRO_I2C",
|
||||
// "-DOGXM_RETAIL=TRUE",
|
||||
"-DMAX_GAMEPADS=1"
|
||||
],
|
||||
|
||||
"files.associations": {
|
||||
|
||||
}
|
||||
}
|
||||
417
Firmware/RP2040/CMakeLists.txt
Normal file
417
Firmware/RP2040/CMakeLists.txt
Normal file
@@ -0,0 +1,417 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../FWDefines.cmake)
|
||||
|
||||
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
|
||||
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
set(SRC ${CMAKE_CURRENT_LIST_DIR}/src)
|
||||
set(EXTERNAL_DIR ${CMAKE_CURRENT_LIST_DIR}/../external)
|
||||
set(PICOSDK_VERSION_TAG "2.1.0")
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/init_submodules.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/patch_libs.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/generate_gatt_header.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/get_pico_sdk.cmake)
|
||||
|
||||
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(LIBFIXMATH_PATH ${EXTERNAL_DIR}/libfixmath)
|
||||
|
||||
get_pico_sdk(${EXTERNAL_DIR} ${PICOSDK_VERSION_TAG})
|
||||
init_git_submodules(${EXTERNAL_DIR}
|
||||
${BLUEPAD32_ROOT}
|
||||
${PICO_TINYUSB_PATH}
|
||||
)
|
||||
apply_lib_patches(${EXTERNAL_DIR})
|
||||
|
||||
set(SOURCES_BOARD
|
||||
${SRC}/main.cpp
|
||||
|
||||
${SRC}/OGXMini/OGXMini.cpp
|
||||
${SRC}/OGXMini/Board/Standard.cpp
|
||||
${SRC}/OGXMini/Board/PicoW.cpp
|
||||
${SRC}/OGXMini/Board/Four_Channel_I2C.cpp
|
||||
${SRC}/OGXMini/Board/ESP32_Bluepad32_I2C.cpp
|
||||
${SRC}/OGXMini/Board/ESP32_Blueretro_I2C.cpp
|
||||
|
||||
${SRC}/TaskQueue/TaskQueue.cpp
|
||||
|
||||
${SRC}/Board/ogxm_log.cpp
|
||||
${SRC}/Board/esp32_api.cpp
|
||||
${SRC}/Board/board_api.cpp
|
||||
${SRC}/Board/board_api_private/board_api_led.cpp
|
||||
${SRC}/Board/board_api_private/board_api_rgb.cpp
|
||||
${SRC}/Board/board_api_private/board_api_bt.cpp
|
||||
${SRC}/Board/board_api_private/board_api_usbh.cpp
|
||||
|
||||
${SRC}/UserSettings/UserSettings.cpp
|
||||
${SRC}/UserSettings/UserProfile.cpp
|
||||
${SRC}/UserSettings/JoystickSettings.cpp
|
||||
${SRC}/UserSettings/TriggerSettings.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
|
||||
#fix16
|
||||
libfixmath
|
||||
)
|
||||
set(INC_DIRS_BOARD
|
||||
)
|
||||
|
||||
# Config options
|
||||
|
||||
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})
|
||||
|
||||
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")
|
||||
add_compile_definitions(CONFIG_OGXM_BOARD_PI_PICO=1)
|
||||
set(EN_USB_HOST TRUE)
|
||||
|
||||
elseif (OGXM_BOARD STREQUAL "PI_PICO2")
|
||||
add_compile_definitions(CONFIG_OGXM_BOARD_PI_PICO2=1)
|
||||
set(EN_USB_HOST TRUE)
|
||||
set(PICO_PLATFORM rp2350)
|
||||
set(FLASH_SIZE_MB 4)
|
||||
|
||||
elseif(OGXM_BOARD STREQUAL "PI_PICOW")
|
||||
add_compile_definitions(CONFIG_OGXM_BOARD_PI_PICOW=1)
|
||||
set(EN_BLUETOOTH TRUE)
|
||||
set(PICO_BOARD pico_w)
|
||||
|
||||
elseif(OGXM_BOARD STREQUAL "PI_PICO2W")
|
||||
add_compile_definitions(CONFIG_OGXM_BOARD_PI_PICO2W=1)
|
||||
set(EN_BLUETOOTH TRUE)
|
||||
set(PICO_BOARD pico2_w)
|
||||
set(PICO_PLATFORM rp2350)
|
||||
set(FLASH_SIZE_MB 4)
|
||||
|
||||
elseif(OGXM_BOARD STREQUAL "ADAFRUIT_FEATHER")
|
||||
add_compile_definitions(CONFIG_OGXM_BOARD_ADAFRUIT_FEATHER=1)
|
||||
set(EN_USB_HOST TRUE)
|
||||
set(EN_RGB TRUE)
|
||||
set(FLASH_SIZE_MB 8)
|
||||
|
||||
elseif(OGXM_BOARD STREQUAL "RP2040_ZERO")
|
||||
add_compile_definitions(CONFIG_OGXM_BOARD_RP2040_ZERO=1)
|
||||
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_I2C")
|
||||
add_compile_definitions(CONFIG_OGXM_BOARD_EXTERNAL_4CH=1)
|
||||
set(EN_USB_HOST TRUE)
|
||||
set(EN_4CH TRUE)
|
||||
set(EN_RGB TRUE)
|
||||
|
||||
elseif(OGXM_BOARD STREQUAL "ESP32_BLUEPAD32_I2C")
|
||||
add_compile_definitions(CONFIG_OGXM_BOARD_ESP32_BLUEPAD32_I2C=1)
|
||||
set(EN_ESP32 TRUE)
|
||||
set(EN_UART_BRIDGE TRUE)
|
||||
|
||||
if(OGXM_RETAIL STREQUAL "TRUE")
|
||||
message(STATUS "Retail mode enabled.")
|
||||
add_compile_definitions(OGXM_RETAIL=1)
|
||||
endif()
|
||||
|
||||
elseif(OGXM_BOARD STREQUAL "ESP32_BLUERETRO_I2C")
|
||||
add_compile_definitions(CONFIG_OGXM_BOARD_ESP32_BLUERETRO_I2C=1)
|
||||
set(EN_ESP32 TRUE)
|
||||
set(EN_BLUERETRO_I2C TRUE)
|
||||
set(EN_UART_BRIDGE TRUE)
|
||||
|
||||
if(OGXM_RETAIL STREQUAL "TRUE")
|
||||
message(STATUS "Retail mode enabled.")
|
||||
add_compile_definitions(OGXM_RETAIL=1)
|
||||
endif()
|
||||
|
||||
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
|
||||
# HID
|
||||
${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/N64/N64.cpp
|
||||
${SRC}/USBHost/HostDriver/HIDGeneric/HIDGeneric.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
|
||||
|
||||
# XInput
|
||||
${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/XInput/tuh_xinput/tuh_xinput.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.")
|
||||
|
||||
generate_gatt_header(${BTSTACK_ROOT} ${SRC}/BLEServer/att_delayed_response.gatt ${SRC}/BLEServer/att_delayed_response.h)
|
||||
|
||||
list(APPEND SOURCES_BOARD
|
||||
${SRC}/Bluepad32/Bluepad32.cpp
|
||||
${SRC}/BLEServer/BLEServer.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/4Channel/I2CMaster.cpp
|
||||
# ${SRC}/I2CDriver/4Channel/I2CSlave.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.")
|
||||
if (EN_BLUERETRO_I2C)
|
||||
# Nothing
|
||||
else()
|
||||
list(APPEND LIBS_BOARD
|
||||
pico_i2c_slave
|
||||
)
|
||||
endif()
|
||||
list(APPEND LIBS_BOARD
|
||||
hardware_i2c
|
||||
)
|
||||
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()
|
||||
|
||||
string(TIMESTAMP CURRENT_DATETIME "%Y-%m-%d %H:%M:%S")
|
||||
add_compile_definitions(BUILD_DATETIME="${CURRENT_DATETIME}")
|
||||
add_compile_definitions(FIRMWARE_NAME="${FW_NAME}")
|
||||
add_compile_definitions(FIRMWARE_VERSION="${FW_VERSION}")
|
||||
add_compile_definitions(PICO_FLASH_SIZE_BYTES=${FLASH_SIZE_MB}*1024*1024)
|
||||
add_compile_definitions(NVS_SECTORS=4)
|
||||
|
||||
# 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)
|
||||
add_compile_definitions(PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64)
|
||||
endif()
|
||||
|
||||
include(${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||
|
||||
message("PICO_SDK_VERSION_STRING: ${PICO_SDK_VERSION_STRING}")
|
||||
if(PICO_SDK_VERSION_STRING VERSION_LESS "${PICOSDK_VERSION_TAG}")
|
||||
message(FATAL_ERROR
|
||||
"Raspberry Pi Pico SDK version ${PICOSDK_VERSION_TAG} (or later) required.
|
||||
Your version is ${PICO_SDK_VERSION_STRING}"
|
||||
)
|
||||
endif()
|
||||
|
||||
project(${FW_NAME} C CXX ASM)
|
||||
|
||||
pico_sdk_init()
|
||||
|
||||
add_executable(${FW_NAME} ${SOURCES_BOARD})
|
||||
|
||||
set(BUILD_STR "")
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
if(OGXM_BOARD STREQUAL "PI_PICOW")
|
||||
message(FATAL_ERROR "Debug build will not work with the Pico W currently")
|
||||
endif()
|
||||
|
||||
message(STATUS "Debug build enabled.")
|
||||
set(BUILD_STR "-Debug")
|
||||
|
||||
set(TX_PIN 0)
|
||||
set(RX_PIN 1)
|
||||
set(UART_PORT)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/get_pico_uart_port.cmake)
|
||||
get_pico_uart_port(${TX_PIN} UART_PORT)
|
||||
|
||||
message(STATUS "UART port: ${UART_PORT}, TX: ${TX_PIN}, RX: ${RX_PIN}")
|
||||
|
||||
pico_enable_stdio_uart(${FW_NAME} 1)
|
||||
target_compile_definitions(${FW_NAME} PRIVATE
|
||||
PICO_DEFAULT_UART=${UART_PORT}
|
||||
PICO_DEFAULT_UART_TX_PIN=${TX_PIN}
|
||||
PICO_DEFAULT_UART_RX_PIN=${RX_PIN}
|
||||
)
|
||||
|
||||
add_compile_definitions(CFG_TUSB_DEBUG=1)
|
||||
add_compile_definitions(OGXM_DEBUG=1)
|
||||
|
||||
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
|
||||
-Wno-unused-parameter # Disable warnings for unused parameters
|
||||
# -Werror # Treat warnings as errors
|
||||
)
|
||||
|
||||
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
pico_enable_stdio_uart(${FW_NAME} 0)
|
||||
add_compile_definitions(CFG_TUSB_DEBUG=0)
|
||||
add_compile_options(
|
||||
-O3 # 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()
|
||||
|
||||
add_subdirectory(${LIBFIXMATH_PATH} libfixmath)
|
||||
|
||||
target_link_libraries(${FW_NAME} PRIVATE ${LIBS_BOARD})
|
||||
|
||||
target_compile_definitions(libfixmath PRIVATE
|
||||
FIXMATH_FAST_SIN
|
||||
FIXMATH_NO_64BIT
|
||||
FIXMATH_NO_CACHE
|
||||
FIXMATH_NO_HARD_DIVISION
|
||||
FIXMATH_NO_OVERFLOW
|
||||
# FIXMATH_NO_ROUNDING
|
||||
# FIXMATH_OPTIMIZE_8BIT
|
||||
)
|
||||
|
||||
if(OGXM_RETAIL STREQUAL "TRUE")
|
||||
set(EXE_FILENAME "${FW_NAME}-${FW_VERSION}-${OGXM_BOARD}${BUILD_STR}-Retail")
|
||||
else()
|
||||
set(EXE_FILENAME "${FW_NAME}-${FW_VERSION}-${OGXM_BOARD}${BUILD_STR}")
|
||||
endif()
|
||||
|
||||
set_target_properties(${FW_NAME} PROPERTIES OUTPUT_NAME ${EXE_FILENAME})
|
||||
|
||||
pico_add_extra_outputs(${FW_NAME})
|
||||
37
Firmware/RP2040/cmake/get_pico_sdk.cmake
Normal file
37
Firmware/RP2040/cmake/get_pico_sdk.cmake
Normal file
@@ -0,0 +1,37 @@
|
||||
function(get_pico_sdk EXTERNAL_DIR VERSION_TAG)
|
||||
if(NOT DEFINED ENV{PICO_SDK_PATH})
|
||||
message("PICO_SDK_PATH not set")
|
||||
set(PICO_SDK_PATH ${EXTERNAL_DIR}/pico-sdk PARENT_SCOPE)
|
||||
|
||||
if(NOT EXISTS ${PICO_SDK_PATH})
|
||||
message("Cloning pico-sdk to ${PICO_SDK_PATH}")
|
||||
execute_process(
|
||||
COMMAND git clone --recursive https://github.com/raspberrypi/pico-sdk.git
|
||||
WORKING_DIRECTORY ${EXTERNAL_DIR}
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND git fetch --tags
|
||||
WORKING_DIRECTORY ENV{PICO_SDK_PATH}
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND git checkout tags/${VERSION_TAG}
|
||||
WORKING_DIRECTORY ENV{PICO_SDK_PATH}
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND git submodule update --init --recursive
|
||||
WORKING_DIRECTORY ENV{PICO_SDK_PATH}
|
||||
)
|
||||
|
||||
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()
|
||||
9
Firmware/RP2040/cmake/get_pico_uart_port.cmake
Normal file
9
Firmware/RP2040/cmake/get_pico_uart_port.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
function(get_pico_uart_port TX_PIN_IN PICO_UART_PORT_OUT)
|
||||
if(TX_PIN_IN EQUAL 0 OR TX_PIN_IN EQUAL 12 OR TX_PIN_IN EQUAL 16)
|
||||
set(${PICO_UART_PORT_OUT} 0 PARENT_SCOPE)
|
||||
elseif(TX_PIN_IN EQUAL 4 OR TX_PIN_IN EQUAL 8)
|
||||
set(${PICO_UART_PORT_OUT} 1 PARENT_SCOPE)
|
||||
else()
|
||||
message(FATAL_ERROR "Invalid TX pin for Pico UART")
|
||||
endif()
|
||||
endfunction()
|
||||
324
Firmware/RP2040/src/BLEServer/BLEServer.cpp
Normal file
324
Firmware/RP2040/src/BLEServer/BLEServer.cpp
Normal file
@@ -0,0 +1,324 @@
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "att_delayed_response.h"
|
||||
#include "btstack.h"
|
||||
|
||||
#include "BLEServer/BLEServer.h"
|
||||
#include "UserSettings/UserProfile.h"
|
||||
#include "UserSettings/UserSettings.h"
|
||||
#include "TaskQueue/TaskQueue.h"
|
||||
|
||||
namespace BLEServer {
|
||||
|
||||
static constexpr uint16_t PACKET_LEN_MAX = 20;
|
||||
|
||||
namespace Handle {
|
||||
static constexpr uint16_t FW_VERSION = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789020_01_VALUE_HANDLE;
|
||||
static constexpr uint16_t FW_NAME = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789021_01_VALUE_HANDLE;
|
||||
|
||||
static constexpr uint16_t SETUP_READ = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789030_01_VALUE_HANDLE;
|
||||
static constexpr uint16_t SETUP_WRITE = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789031_01_VALUE_HANDLE;
|
||||
static constexpr uint16_t GET_SETUP = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789032_01_VALUE_HANDLE;
|
||||
|
||||
static constexpr uint16_t PROFILE = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789040_01_VALUE_HANDLE;
|
||||
|
||||
static constexpr uint16_t GAMEPAD = ATT_CHARACTERISTIC_12345678_1234_1234_1234_123456789050_01_VALUE_HANDLE;
|
||||
}
|
||||
|
||||
namespace ADV {
|
||||
// Flags general discoverable, BR/EDR not supported
|
||||
static const uint8_t FLAGS[] = { 0x02, 0x01, 0x06 };
|
||||
static const uint8_t NAME_TYPE = 0x09;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct Data {
|
||||
uint8_t flags[sizeof(FLAGS)];
|
||||
uint8_t name_len;
|
||||
uint8_t name_type;
|
||||
uint8_t name[sizeof(FIRMWARE_NAME) - 1];
|
||||
|
||||
Data() {
|
||||
std::memcpy(flags, FLAGS, sizeof(flags));
|
||||
name_len = sizeof(FIRMWARE_NAME);
|
||||
name_type = NAME_TYPE;
|
||||
std::string fw_name = FIRMWARE_NAME;
|
||||
std::memcpy(name, fw_name.c_str(), std::min(sizeof(name), fw_name.size()));
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Data) == 5 + sizeof(FIRMWARE_NAME) - 1, "BLEServer::ADV::Data struct size mismatch");
|
||||
#pragma pack(pop)
|
||||
}
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct SetupPacket {
|
||||
DeviceDriverType device_type{DeviceDriverType::NONE};
|
||||
uint8_t max_gamepads{MAX_GAMEPADS};
|
||||
uint8_t player_idx{0};
|
||||
uint8_t profile_id{0};
|
||||
};
|
||||
static_assert(sizeof(SetupPacket) == 4, "BLEServer::SetupPacket struct size mismatch");
|
||||
#pragma pack(pop)
|
||||
|
||||
class ProfileReader {
|
||||
public:
|
||||
ProfileReader() = default;
|
||||
~ProfileReader() = default;
|
||||
|
||||
void set_setup_packet(const SetupPacket& setup_packet) {
|
||||
setup_packet_ = setup_packet;
|
||||
current_offset_ = 0;
|
||||
}
|
||||
|
||||
const SetupPacket& get_setup_packet() const {
|
||||
return setup_packet_;
|
||||
}
|
||||
|
||||
uint16_t get_xfer_len() {
|
||||
return static_cast<uint16_t>(std::min(static_cast<size_t>(PACKET_LEN_MAX), sizeof(UserProfile) - current_offset_));
|
||||
}
|
||||
|
||||
uint16_t get_profile_data(uint8_t* buffer, uint16_t buffer_len) {
|
||||
size_t copy_len = get_xfer_len();
|
||||
if (!buffer || buffer_len < copy_len) {
|
||||
return 0;
|
||||
}
|
||||
if (current_offset_ == 0 && !set_profile()) {
|
||||
return 0;
|
||||
}
|
||||
std::memcpy(buffer, reinterpret_cast<uint8_t*>(&profile_) + current_offset_, copy_len);
|
||||
current_offset_ += copy_len;
|
||||
if (current_offset_ >= sizeof(UserProfile)) {
|
||||
current_offset_ = 0;
|
||||
}
|
||||
return copy_len;
|
||||
}
|
||||
|
||||
private:
|
||||
SetupPacket setup_packet_;
|
||||
UserProfile profile_;
|
||||
size_t current_offset_ = 0;
|
||||
|
||||
bool set_profile() {
|
||||
if (setup_packet_.profile_id == 0xFF) {
|
||||
if (setup_packet_.player_idx >= UserSettings::MAX_PROFILES) {
|
||||
return false;
|
||||
}
|
||||
profile_ = UserSettings::get_instance().get_profile_by_index(setup_packet_.player_idx);
|
||||
} else {
|
||||
if (setup_packet_.profile_id > UserSettings::MAX_PROFILES) {
|
||||
return false;
|
||||
}
|
||||
profile_ = UserSettings::get_instance().get_profile_by_id(setup_packet_.profile_id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ProfileWriter
|
||||
{
|
||||
public:
|
||||
ProfileWriter() = default;
|
||||
~ProfileWriter() = default;
|
||||
|
||||
void set_setup_packet(const SetupPacket& setup_packet) {
|
||||
setup_packet_ = setup_packet;
|
||||
current_offset_ = 0;
|
||||
}
|
||||
|
||||
const SetupPacket& get_setup_packet() const {
|
||||
return setup_packet_;
|
||||
}
|
||||
|
||||
uint16_t get_xfer_len() {
|
||||
return static_cast<uint16_t>(std::min(static_cast<size_t>(PACKET_LEN_MAX), sizeof(UserProfile) - current_offset_));
|
||||
}
|
||||
|
||||
size_t set_profile_data(const uint8_t* buffer, uint16_t buffer_len) {
|
||||
size_t copy_len = get_xfer_len();
|
||||
if (!buffer || buffer_len < copy_len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::memcpy(reinterpret_cast<uint8_t*>(&profile_) + current_offset_, buffer, copy_len);
|
||||
|
||||
current_offset_ += copy_len;
|
||||
size_t ret = current_offset_;
|
||||
|
||||
if (current_offset_ >= sizeof(UserProfile)) {
|
||||
current_offset_ = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool commit_profile() {
|
||||
bool success = false;
|
||||
if (setup_packet_.device_type != DeviceDriverType::NONE) {
|
||||
success = TaskQueue::Core0::queue_delayed_task(TaskQueue::Core0::get_new_task_id(), 1000, false,
|
||||
[driver_type = setup_packet_.device_type, profile = profile_, index = setup_packet_.player_idx]
|
||||
{
|
||||
UserSettings::get_instance().store_profile_and_driver_type(driver_type, index, profile);
|
||||
});
|
||||
} else {
|
||||
success = TaskQueue::Core0::queue_delayed_task(TaskQueue::Core0::get_new_task_id(), 1000, false,
|
||||
[index = setup_packet_.player_idx, profile = profile_]
|
||||
{
|
||||
UserSettings::get_instance().store_profile(index, profile);
|
||||
});
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private:
|
||||
SetupPacket setup_packet_;
|
||||
UserProfile profile_;
|
||||
size_t current_offset_ = 0;
|
||||
};
|
||||
|
||||
std::array<Gamepad*, MAX_GAMEPADS> gamepads_;
|
||||
ProfileReader profile_reader_;
|
||||
ProfileWriter profile_writer_;
|
||||
|
||||
static int verify_write(const uint16_t buffer_size, const uint16_t expected_size) {
|
||||
if (buffer_size != expected_size) {
|
||||
return ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void disconnect_client_cb(btstack_timer_source_t *ts) {
|
||||
hci_con_handle_t connection_handle = *static_cast<hci_con_handle_t*>(ts->context);
|
||||
hci_send_cmd(&hci_disconnect, connection_handle);
|
||||
delete static_cast<hci_con_handle_t*>(ts->context);
|
||||
}
|
||||
|
||||
static void queue_disconnect(hci_con_handle_t connection_handle, uint32_t dealy_ms) {
|
||||
static btstack_timer_source_t disconnect_timer;
|
||||
|
||||
hci_con_handle_t* connection_handle_ptr = new hci_con_handle_t(connection_handle);
|
||||
|
||||
disconnect_timer.process = disconnect_client_cb;
|
||||
disconnect_timer.context = connection_handle_ptr;
|
||||
|
||||
btstack_run_loop_set_timer(&disconnect_timer, dealy_ms);
|
||||
btstack_run_loop_add_timer(&disconnect_timer);
|
||||
}
|
||||
|
||||
static uint16_t att_read_callback( hci_con_handle_t connection_handle,
|
||||
uint16_t att_handle,
|
||||
uint16_t offset,
|
||||
uint8_t *buffer,
|
||||
uint16_t buffer_size) {
|
||||
std::string fw_version;
|
||||
std::string fw_name;
|
||||
Gamepad::PadIn pad_in;
|
||||
|
||||
switch (att_handle) {
|
||||
case Handle::FW_VERSION:
|
||||
fw_version = FIRMWARE_VERSION;
|
||||
if (buffer) {
|
||||
std::memcpy(buffer, reinterpret_cast<const uint8_t*>(fw_version.c_str()), fw_version.size());
|
||||
}
|
||||
return static_cast<uint16_t>(fw_version.size());
|
||||
|
||||
case Handle::FW_NAME:
|
||||
fw_name = FIRMWARE_NAME;
|
||||
if (buffer) {
|
||||
std::memcpy(buffer, reinterpret_cast<const uint8_t*>(fw_name.c_str()), fw_name.size());;
|
||||
}
|
||||
return static_cast<uint16_t>(fw_name.size());
|
||||
|
||||
case Handle::GET_SETUP:
|
||||
if (buffer) {
|
||||
buffer[0] = static_cast<uint8_t>(UserSettings::get_instance().get_current_driver());
|
||||
buffer[1] = MAX_GAMEPADS;
|
||||
buffer[2] = 0;
|
||||
buffer[3] = UserSettings::get_instance().get_active_profile_id(0);
|
||||
}
|
||||
return static_cast<uint16_t>(sizeof(SetupPacket));
|
||||
|
||||
case Handle::PROFILE:
|
||||
if (buffer) {
|
||||
return profile_reader_.get_profile_data(buffer, buffer_size);
|
||||
}
|
||||
return profile_reader_.get_xfer_len();
|
||||
|
||||
case Handle::GAMEPAD:
|
||||
if (buffer) {
|
||||
pad_in = gamepads_.front()->get_pad_in();
|
||||
std::memcpy(buffer, &pad_in, sizeof(Gamepad::PadIn));
|
||||
}
|
||||
return static_cast<uint16_t>(sizeof(Gamepad::PadIn));
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int att_write_callback( hci_con_handle_t connection_handle,
|
||||
uint16_t att_handle,
|
||||
uint16_t transaction_mode,
|
||||
uint16_t offset,
|
||||
uint8_t *buffer,
|
||||
uint16_t buffer_size) {
|
||||
int ret = 0;
|
||||
|
||||
switch (att_handle) {
|
||||
case Handle::SETUP_READ:
|
||||
if ((ret = verify_write(buffer_size, sizeof(SetupPacket))) != 0) {
|
||||
break;
|
||||
}
|
||||
profile_reader_.set_setup_packet(*reinterpret_cast<SetupPacket*>(buffer));
|
||||
break;
|
||||
|
||||
case Handle::SETUP_WRITE:
|
||||
if ((ret = verify_write(buffer_size, sizeof(SetupPacket))) != 0) {
|
||||
break;
|
||||
}
|
||||
profile_writer_.set_setup_packet(*reinterpret_cast<SetupPacket*>(buffer));
|
||||
break;
|
||||
|
||||
case Handle::PROFILE:
|
||||
if ((ret = verify_write(buffer_size, profile_writer_.get_xfer_len())) != 0) {
|
||||
break;
|
||||
}
|
||||
if (profile_writer_.set_profile_data(buffer, buffer_size) == sizeof(UserProfile)) {
|
||||
queue_disconnect(connection_handle, 500);
|
||||
profile_writer_.commit_profile();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void init_server(Gamepad(&gamepads)[MAX_GAMEPADS]) {
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; i++) {
|
||||
gamepads_[i] = &gamepads[i];
|
||||
}
|
||||
|
||||
UserSettings::get_instance().initialize_flash();
|
||||
|
||||
// setup ATT server
|
||||
att_server_init(profile_data, att_read_callback, att_write_callback);
|
||||
|
||||
// setup advertisements
|
||||
uint16_t adv_int_min = 0x0030;
|
||||
uint16_t adv_int_max = 0x0030;
|
||||
uint8_t adv_type = 0;
|
||||
|
||||
bd_addr_t null_addr;
|
||||
std::memset(null_addr, 0, sizeof(null_addr));
|
||||
|
||||
static ADV::Data adv_data = ADV::Data();
|
||||
|
||||
gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
|
||||
gap_advertisements_set_data(static_cast<uint8_t>(sizeof(adv_data)), reinterpret_cast<uint8_t*>(&adv_data));
|
||||
gap_advertisements_enable(1);
|
||||
}
|
||||
|
||||
} // namespace BLEServer
|
||||
9
Firmware/RP2040/src/BLEServer/BLEServer.h
Normal file
9
Firmware/RP2040/src/BLEServer/BLEServer.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Gamepad/Gamepad.h"
|
||||
|
||||
namespace BLEServer {
|
||||
void init_server(Gamepad(&gamepads)[MAX_GAMEPADS]);
|
||||
}
|
||||
29
Firmware/RP2040/src/BLEServer/att_delayed_response.gatt
Normal file
29
Firmware/RP2040/src/BLEServer/att_delayed_response.gatt
Normal file
@@ -0,0 +1,29 @@
|
||||
PRIMARY_SERVICE, GAP_SERVICE
|
||||
CHARACTERISTIC, GAP_DEVICE_NAME, READ, "XPad"
|
||||
|
||||
PRIMARY_SERVICE, GATT_SERVICE
|
||||
CHARACTERISTIC, GATT_DATABASE_HASH, READ,
|
||||
|
||||
// Handle::PRIMARY_SERVICE
|
||||
PRIMARY_SERVICE, 12345678-1234-1234-1234-123456789012
|
||||
|
||||
// Handle::FW_VERSION
|
||||
CHARACTERISTIC, 12345678-1234-1234-1234-123456789020, READ | DYNAMIC,
|
||||
|
||||
// Handle::FW_NAME
|
||||
CHARACTERISTIC, 12345678-1234-1234-1234-123456789021, READ | DYNAMIC,
|
||||
|
||||
// Handle::SETUP_READ
|
||||
CHARACTERISTIC, 12345678-1234-1234-1234-123456789030, WRITE | DYNAMIC,
|
||||
|
||||
// Handle::SETUP_WRITE
|
||||
CHARACTERISTIC, 12345678-1234-1234-1234-123456789031, WRITE | DYNAMIC,
|
||||
|
||||
// Handle::GET_SETUP
|
||||
CHARACTERISTIC, 12345678-1234-1234-1234-123456789032, READ | DYNAMIC,
|
||||
|
||||
// Handle::PROFILE
|
||||
CHARACTERISTIC, 12345678-1234-1234-1234-123456789040, READ | WRITE | DYNAMIC,
|
||||
|
||||
// Handle::GAMEPAD
|
||||
CHARACTERISTIC, 12345678-1234-1234-1234-123456789050, READ | WRITE | DYNAMIC,
|
||||
301
Firmware/RP2040/src/Bluepad32/Bluepad32.cpp
Normal file
301
Firmware/RP2040/src/Bluepad32/Bluepad32.cpp
Normal file
@@ -0,0 +1,301 @@
|
||||
#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"
|
||||
#include "Board/ogxm_log.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 = 250;
|
||||
static constexpr uint32_t LED_CHECK_TIME_MS = 500;
|
||||
|
||||
struct BTDevice {
|
||||
bool connected{false};
|
||||
Gamepad* gamepad{nullptr};
|
||||
};
|
||||
|
||||
BTDevice bt_devices_[MAX_GAMEPADS];
|
||||
btstack_timer_source_t feedback_timer_;
|
||||
btstack_timer_source_t led_timer_;
|
||||
bool led_timer_set_{false};
|
||||
bool feedback_timer_set_{false};
|
||||
|
||||
bool any_connected()
|
||||
{
|
||||
for (auto& device : bt_devices_)
|
||||
{
|
||||
if (device.connected)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//This solves a function pointer/crash issue with bluepad32
|
||||
void set_rumble(uni_hid_device_t* bp_device, uint16_t length, uint8_t rumble_l, uint8_t rumble_r)
|
||||
{
|
||||
switch (bp_device->controller_type)
|
||||
{
|
||||
case CONTROLLER_TYPE_XBoxOneController:
|
||||
uni_hid_parser_xboxone_play_dual_rumble(bp_device, 0, length + 10, 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 < MAX_GAMEPADS; ++i)
|
||||
{
|
||||
if (!bt_devices_[i].connected ||
|
||||
!(bp_device = uni_hid_device_get_instance_for_idx(i)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Gamepad::PadOut gp_out = bt_devices_[i].gamepad->get_pad_out();
|
||||
if (gp_out.rumble_l > 0 || gp_out.rumble_r > 0)
|
||||
{
|
||||
set_rumble(bp_device, static_cast<uint16_t>(FEEDBACK_TIME_MS), gp_out.rumble_l, gp_out.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);
|
||||
// uni_bt_del_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;
|
||||
}
|
||||
|
||||
bt_devices_[idx].connected = false;
|
||||
bt_devices_[idx].gamepad->reset_pad_in();
|
||||
|
||||
if (!led_timer_set_ && !any_connected()) {
|
||||
led_timer_set_ = true;
|
||||
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_);
|
||||
}
|
||||
if (feedback_timer_set_ && !any_connected()) {
|
||||
feedback_timer_set_ = false;
|
||||
btstack_run_loop_remove_timer(&feedback_timer_);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bt_devices_[idx].connected = true;
|
||||
|
||||
if (led_timer_set_) {
|
||||
led_timer_set_ = false;
|
||||
btstack_run_loop_remove_timer(&led_timer_);
|
||||
board_api::set_led(true);
|
||||
}
|
||||
if (!feedback_timer_set_) {
|
||||
feedback_timer_set_ = true;
|
||||
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_);
|
||||
}
|
||||
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);
|
||||
|
||||
Gamepad* gamepad = bt_devices_[idx].gamepad;
|
||||
Gamepad::PadIn gp_in;
|
||||
|
||||
switch (uni_gp->dpad)
|
||||
{
|
||||
case DPAD_UP:
|
||||
gp_in.dpad = gamepad->MAP_DPAD_UP;
|
||||
break;
|
||||
case DPAD_DOWN:
|
||||
gp_in.dpad = gamepad->MAP_DPAD_DOWN;
|
||||
break;
|
||||
case DPAD_LEFT:
|
||||
gp_in.dpad = gamepad->MAP_DPAD_LEFT;
|
||||
break;
|
||||
case DPAD_RIGHT:
|
||||
gp_in.dpad = gamepad->MAP_DPAD_RIGHT;
|
||||
break;
|
||||
case DPAD_UP | DPAD_RIGHT:
|
||||
gp_in.dpad = gamepad->MAP_DPAD_UP_RIGHT;
|
||||
break;
|
||||
case DPAD_DOWN | DPAD_RIGHT:
|
||||
gp_in.dpad = gamepad->MAP_DPAD_DOWN_RIGHT;
|
||||
break;
|
||||
case DPAD_DOWN | DPAD_LEFT:
|
||||
gp_in.dpad = gamepad->MAP_DPAD_DOWN_LEFT;
|
||||
break;
|
||||
case DPAD_UP | DPAD_LEFT:
|
||||
gp_in.dpad = gamepad->MAP_DPAD_UP_LEFT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (uni_gp->buttons & BUTTON_A) gp_in.buttons |= gamepad->MAP_BUTTON_A;
|
||||
if (uni_gp->buttons & BUTTON_B) gp_in.buttons |= gamepad->MAP_BUTTON_B;
|
||||
if (uni_gp->buttons & BUTTON_X) gp_in.buttons |= gamepad->MAP_BUTTON_X;
|
||||
if (uni_gp->buttons & BUTTON_Y) gp_in.buttons |= gamepad->MAP_BUTTON_Y;
|
||||
if (uni_gp->buttons & BUTTON_SHOULDER_L) gp_in.buttons |= gamepad->MAP_BUTTON_LB;
|
||||
if (uni_gp->buttons & BUTTON_SHOULDER_R) gp_in.buttons |= gamepad->MAP_BUTTON_RB;
|
||||
if (uni_gp->buttons & BUTTON_THUMB_L) gp_in.buttons |= gamepad->MAP_BUTTON_L3;
|
||||
if (uni_gp->buttons & BUTTON_THUMB_R) gp_in.buttons |= gamepad->MAP_BUTTON_R3;
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_BACK) gp_in.buttons |= gamepad->MAP_BUTTON_BACK;
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_START) gp_in.buttons |= gamepad->MAP_BUTTON_START;
|
||||
if (uni_gp->misc_buttons & MISC_BUTTON_SYSTEM) gp_in.buttons |= gamepad->MAP_BUTTON_SYS;
|
||||
|
||||
gp_in.trigger_l = gamepad->scale_trigger_l<10>(static_cast<uint16_t>(uni_gp->brake));
|
||||
gp_in.trigger_r = gamepad->scale_trigger_r<10>(static_cast<uint16_t>(uni_gp->throttle));
|
||||
|
||||
std::tie(gp_in.joystick_lx, gp_in.joystick_ly) = gamepad->scale_joystick_l<10>(uni_gp->axis_x, uni_gp->axis_y);
|
||||
std::tie(gp_in.joystick_rx, gp_in.joystick_ry) = gamepad->scale_joystick_r<10>(uni_gp->axis_rx, uni_gp->axis_ry);
|
||||
|
||||
gamepad->set_pad_in(gp_in);
|
||||
}
|
||||
|
||||
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 run_task(Gamepad(&gamepads)[MAX_GAMEPADS])
|
||||
{
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
|
||||
{
|
||||
bt_devices_[i].gamepad = &gamepads[i];
|
||||
}
|
||||
|
||||
uni_platform_set_custom(get_driver());
|
||||
uni_init(0, nullptr);
|
||||
|
||||
led_timer_set_ = true;
|
||||
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();
|
||||
}
|
||||
|
||||
} // namespace bluepad32
|
||||
14
Firmware/RP2040/src/Bluepad32/Bluepad32.h
Normal file
14
Firmware/RP2040/src/Bluepad32/Bluepad32.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "Gamepad/Gamepad.h"
|
||||
#include "Board/Config.h"
|
||||
|
||||
/* NOTE: Everything bluepad32/uni needs to be wrapped
|
||||
and kept away from tinyusb due to naming conflicts */
|
||||
|
||||
namespace bluepad32 {
|
||||
void run_task(Gamepad(&gamepads)[MAX_GAMEPADS]);
|
||||
}
|
||||
129
Firmware/RP2040/src/Board/Config.h
Normal file
129
Firmware/RP2040/src/Board/Config.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#define PI_PICO 0
|
||||
#define RP2040_ZERO 1
|
||||
#define ADAFRUIT_FEATHER 2
|
||||
#define PI_PICOW 3
|
||||
#define ESP32_BLUEPAD32_I2C 4
|
||||
#define ESP32_BLUERETRO_I2C 5
|
||||
#define EXTERNAL_4CH_I2C 6
|
||||
#define INTERNAL_4CH_I2C 7
|
||||
#define BOARDS_COUNT 8
|
||||
|
||||
#define SYSCLOCK_KHZ 240000
|
||||
|
||||
#ifndef MAX_GAMEPADS
|
||||
#define MAX_GAMEPADS 1
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OGXM_BOARD_PI_PICO) || defined(CONFIG_OGXM_BOARD_PI_PICO2)
|
||||
#define OGXM_BOARD PI_PICO
|
||||
#define PIO_USB_DP_PIN 0 // DM = 1
|
||||
#define LED_INDICATOR_PIN 25
|
||||
|
||||
#elif defined(CONFIG_OGXM_BOARD_PI_PICOW) || defined(CONFIG_OGXM_BOARD_PI_PICO2W)
|
||||
#define OGXM_BOARD PI_PICOW
|
||||
|
||||
#elif defined(CONFIG_OGXM_BOARD_RP2040_ZERO)
|
||||
#define OGXM_BOARD RP2040_ZERO
|
||||
#define RGB_PXL_PIN 16
|
||||
#define PIO_USB_DP_PIN 10 // DM = 11
|
||||
#define LED_INDICATOR_PIN 14
|
||||
|
||||
#elif defined(CONFIG_OGXM_BOARD_ADAFRUIT_FEATHER)
|
||||
#define OGXM_BOARD ADAFRUIT_FEATHER
|
||||
#define RGB_PWR_PIN 20
|
||||
#define RGB_PXL_PIN 21
|
||||
|
||||
#define PIO_USB_DP_PIN 16 // DM = 17
|
||||
#define LED_INDICATOR_PIN 13
|
||||
#define VCC_EN_PIN 18
|
||||
|
||||
#elif defined(CONFIG_OGXM_BOARD_INTERNAL_4CH)
|
||||
#define OGXM_BOARD INTERNAL_4CH_I2C
|
||||
#define PIO_USB_DP_PIN 16 // DM = 17
|
||||
#define FOUR_CH_ENABLED 1
|
||||
#define I2C_SDA_PIN 10
|
||||
#define I2C_SCL_PIN 11
|
||||
#define SLAVE_ADDR_PIN_1 20
|
||||
#define SLAVE_ADDR_PIN_2 21
|
||||
|
||||
#elif defined(CONFIG_OGXM_BOARD_EXTERNAL_4CH)
|
||||
#define OGXM_BOARD EXTERNAL_4CH_I2C
|
||||
#define RGB_PXL_PIN 16
|
||||
#define FOUR_CH_ENABLED 1
|
||||
#define PIO_USB_DP_PIN 10 // DM = 11
|
||||
#define I2C_SDA_PIN 6
|
||||
#define I2C_SCL_PIN 7
|
||||
#define SLAVE_ADDR_PIN_1 13
|
||||
#define SLAVE_ADDR_PIN_2 14
|
||||
|
||||
#elif defined(CONFIG_OGXM_BOARD_ESP32_BLUEPAD32_I2C)
|
||||
#define OGXM_BOARD ESP32_BLUEPAD32_I2C
|
||||
#define I2C_SDA_PIN 18
|
||||
#define I2C_SCL_PIN 19
|
||||
#define UART0_TX_PIN 16
|
||||
#define UART0_RX_PIN 17
|
||||
#define MODE_SEL_PIN 21
|
||||
#define ESP_PROG_PIN 20 // ESP32 IO0
|
||||
#define ESP_RST_PIN 8 // ESP32 EN
|
||||
|
||||
#if MAX_GAMEPADS > 1
|
||||
#undef MAX_GAMEPADS
|
||||
#define MAX_GAMEPADS 1
|
||||
#endif
|
||||
|
||||
#elif defined(CONFIG_OGXM_BOARD_ESP32_BLUERETRO_I2C)
|
||||
#define OGXM_BOARD ESP32_BLUERETRO_I2C
|
||||
#define I2C_SDA_PIN 18
|
||||
#define I2C_SCL_PIN 19
|
||||
#define UART0_TX_PIN 16
|
||||
#define UART0_RX_PIN 17
|
||||
#define MODE_SEL_PIN 21
|
||||
#define ESP_PROG_PIN 20 // ESP32 IO0
|
||||
#define ESP_RST_PIN 8 // ESP32 EN
|
||||
|
||||
#if MAX_GAMEPADS > 1
|
||||
#undef MAX_GAMEPADS
|
||||
#define MAX_GAMEPADS 1
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error "Invalid OGXMini board selected"
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OGXM_DEBUG)
|
||||
//Pins and port are defined in CMakeLists.txt
|
||||
#define DEBUG_UART_PORT __CONCAT(uart,PICO_DEFAULT_UART)
|
||||
#endif // defined(CONFIG_OGXM_DEBUG)
|
||||
|
||||
#if defined(I2C_SDA_PIN)
|
||||
#define I2C_BAUDRATE 400 * 1000
|
||||
#define I2C_PORT ((I2C_SDA_PIN == 2 ) || \
|
||||
(I2C_SDA_PIN == 6 ) || \
|
||||
(I2C_SDA_PIN == 10) || \
|
||||
(I2C_SDA_PIN == 14) || \
|
||||
(I2C_SDA_PIN == 18) || \
|
||||
(I2C_SDA_PIN == 26)) ? i2c1 : i2c0
|
||||
#endif // defined(I2C_SDA_PIN)
|
||||
|
||||
#if defined(PIO_USB_DP_PIN)
|
||||
#define PIO_USB_CONFIG { \
|
||||
PIO_USB_DP_PIN, \
|
||||
PIO_USB_TX_DEFAULT, \
|
||||
PIO_SM_USB_TX_DEFAULT, \
|
||||
PIO_USB_DMA_TX_DEFAULT, \
|
||||
PIO_USB_RX_DEFAULT, \
|
||||
PIO_SM_USB_RX_DEFAULT, \
|
||||
PIO_SM_USB_EOP_DEFAULT, \
|
||||
NULL, \
|
||||
PIO_USB_DEBUG_PIN_NONE, \
|
||||
PIO_USB_DEBUG_PIN_NONE, \
|
||||
false, \
|
||||
PIO_USB_PINOUT_DPDM \
|
||||
}
|
||||
#endif // defined(PIO_USB_DP_PIN)
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
114
Firmware/RP2040/src/Board/Pico_WS2812/WS2812.cpp
Normal file
114
Firmware/RP2040/src/Board/Pico_WS2812/WS2812.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Copyright ForsakenNGS 2021
|
||||
https://github.com/ForsakenNGS/Pico_WS2812
|
||||
*/
|
||||
|
||||
#include "Board/Pico_WS2812/WS2812.hpp"
|
||||
#include "WS2812.pio.h"
|
||||
|
||||
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 = new uint32_t[length];
|
||||
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);
|
||||
|
||||
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() {
|
||||
for (uint i = 0; i < length; i++) {
|
||||
pio_sm_put_blocking(pio, sm, data[i]);
|
||||
}
|
||||
}
|
||||
63
Firmware/RP2040/src/Board/Pico_WS2812/WS2812.hpp
Normal file
63
Firmware/RP2040/src/Board/Pico_WS2812/WS2812.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright ForsakenNGS 2021
|
||||
https://github.com/ForsakenNGS/Pico_WS2812
|
||||
*/
|
||||
|
||||
#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];
|
||||
uint32_t *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);
|
||||
}
|
||||
%}
|
||||
107
Firmware/RP2040/src/Board/board_api.cpp
Normal file
107
Firmware/RP2040/src/Board/board_api.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include <pico/stdlib.h>
|
||||
#include <pico/mutex.h>
|
||||
#include <pico/multicore.h>
|
||||
#include <hardware/clocks.h>
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#include "Board/Config.h"
|
||||
#include "Board/board_api.h"
|
||||
#include "Board/ogxm_log.h"
|
||||
#include "Board/board_api_private/board_api_private.h"
|
||||
#include "TaskQueue/TaskQueue.h"
|
||||
|
||||
namespace board_api {
|
||||
|
||||
mutex_t gpio_mutex_;
|
||||
|
||||
bool usb::host_connected() {
|
||||
if (board_api_usbh::host_connected) {
|
||||
return board_api_usbh::host_connected();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//Only call this from core0
|
||||
void usb::disconnect_all() {
|
||||
OGXM_LOG("Disconnecting USB and resetting Core1\n");
|
||||
|
||||
TaskQueue::suspend_delayed_tasks();
|
||||
multicore_reset_core1();
|
||||
sleep_ms(500);
|
||||
tud_disconnect();
|
||||
sleep_ms(500);
|
||||
}
|
||||
|
||||
// If using PicoW, only use this method from the core running btstack and after you've called init_bluetooth
|
||||
void set_led(bool state) {
|
||||
mutex_enter_blocking(&gpio_mutex_);
|
||||
|
||||
if (board_api_led::set_led) {
|
||||
board_api_led::set_led(state);
|
||||
}
|
||||
if (board_api_bt::set_led) {
|
||||
board_api_bt::set_led(state);
|
||||
}
|
||||
if (board_api_rgb::set_led) {
|
||||
board_api_rgb::set_led(state ? 0x00 : 0xFF, state ? 0xFF : 0x00, 0x00);
|
||||
}
|
||||
|
||||
mutex_exit(&gpio_mutex_);
|
||||
}
|
||||
|
||||
void reboot() {
|
||||
#define AIRCR_REG (*((volatile uint32_t *)(0xE000ED0C)))
|
||||
#define AIRCR_SYSRESETREQ (1 << 2)
|
||||
#define AIRCR_VECTKEY (0x5FA << 16)
|
||||
|
||||
OGXM_LOG("Rebooting\n");
|
||||
|
||||
AIRCR_REG = AIRCR_VECTKEY | AIRCR_SYSRESETREQ;
|
||||
while(1);
|
||||
}
|
||||
|
||||
uint32_t ms_since_boot() {
|
||||
return to_ms_since_boot(get_absolute_time());
|
||||
}
|
||||
|
||||
//Call after board is initialized
|
||||
void init_bluetooth() {
|
||||
if (board_api_bt::init) {
|
||||
board_api_bt::init();
|
||||
}
|
||||
}
|
||||
|
||||
//Call on core0 before any other method
|
||||
void init_board() {
|
||||
if (!set_sys_clock_khz(SYSCLOCK_KHZ, true)) {
|
||||
if (!set_sys_clock_khz((SYSCLOCK_KHZ / 2), true)) {
|
||||
panic("Failed to set sys clock");
|
||||
}
|
||||
}
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
if (!mutex_is_initialized(&gpio_mutex_)) {
|
||||
mutex_init(&gpio_mutex_);
|
||||
mutex_enter_blocking(&gpio_mutex_);
|
||||
|
||||
if (ogxm_log::init) {
|
||||
ogxm_log::init();
|
||||
}
|
||||
if (board_api_led::init) {
|
||||
board_api_led::init();
|
||||
}
|
||||
if (board_api_rgb::init) {
|
||||
board_api_rgb::init();
|
||||
}
|
||||
if (board_api_usbh::init) {
|
||||
board_api_usbh::init();
|
||||
}
|
||||
|
||||
mutex_exit(&gpio_mutex_);
|
||||
}
|
||||
OGXM_LOG("Board initialized\n");
|
||||
}
|
||||
|
||||
} // namespace board_api
|
||||
20
Firmware/RP2040/src/Board/board_api.h
Normal file
20
Firmware/RP2040/src/Board/board_api.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef _OGXM_BOARD_API_H_
|
||||
#define _OGXM_BOARD_API_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace board_api {
|
||||
void init_board();
|
||||
void init_bluetooth();
|
||||
void reboot();
|
||||
void set_led(bool state);
|
||||
uint32_t ms_since_boot();
|
||||
|
||||
namespace usb {
|
||||
bool host_connected();
|
||||
void disconnect_all();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _OGXM_BOARD_API_H_
|
||||
34
Firmware/RP2040/src/Board/board_api_private/board_api_bt.cpp
Normal file
34
Firmware/RP2040/src/Board/board_api_private/board_api_bt.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "Board/Config.h"
|
||||
#if defined(CONFIG_EN_BLUETOOTH)
|
||||
|
||||
#include <atomic>
|
||||
#include <pico/cyw43_arch.h>
|
||||
|
||||
#include "Board/board_api_private/board_api_private.h"
|
||||
|
||||
#if defined(CYW43_WL_GPIO_LED_PIN) && defined(LED_INDICATOR_PIN)
|
||||
static_assert(CYW43_WL_GPIO_LED_PIN != LED_INDICATOR_PIN, "CYW43_WL_GPIO_LED_PIN cannot be the same as LED_INDICATOR_PIN");
|
||||
#endif
|
||||
|
||||
namespace board_api_bt {
|
||||
|
||||
std::atomic<bool> inited{false};
|
||||
|
||||
void init() {
|
||||
if (cyw43_arch_init() != 0) {
|
||||
panic("CYW43 init failed");
|
||||
} else {
|
||||
inited.store(true);
|
||||
}
|
||||
}
|
||||
|
||||
void set_led(bool state) {
|
||||
if (!inited.load()) {
|
||||
return;
|
||||
}
|
||||
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, state ? 1 : 0);
|
||||
}
|
||||
|
||||
} // namespace board_api_bt
|
||||
|
||||
#endif // defined(CONFIG_EN_BLUETOOTH)
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "Board/Config.h"
|
||||
#if defined(LED_INDICATOR_PIN)
|
||||
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
namespace board_api_led {
|
||||
|
||||
void init() {
|
||||
gpio_init(LED_INDICATOR_PIN);
|
||||
gpio_set_dir(LED_INDICATOR_PIN, GPIO_OUT);
|
||||
gpio_put(LED_INDICATOR_PIN, 0);
|
||||
}
|
||||
|
||||
void set_led(bool state) {
|
||||
gpio_put(LED_INDICATOR_PIN, state ? 1 : 0);
|
||||
}
|
||||
|
||||
} // namespace board_api
|
||||
|
||||
#endif // LED_INDICATOR_PIN
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef BOARD_API_PRIVATE_H
|
||||
#define BOARD_API_PRIVATE_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace board_api_bt {
|
||||
void init() __attribute__((weak));
|
||||
void set_led(bool state) __attribute__((weak));
|
||||
}
|
||||
|
||||
namespace board_api_led {
|
||||
void init() __attribute__((weak));
|
||||
void set_led(bool state) __attribute__((weak));
|
||||
}
|
||||
|
||||
namespace board_api_rgb {
|
||||
void init() __attribute__((weak));
|
||||
void set_led(uint8_t r, uint8_t g, uint8_t b) __attribute__((weak));
|
||||
}
|
||||
|
||||
namespace board_api_usbh {
|
||||
void init() __attribute__((weak));
|
||||
bool host_connected() __attribute__((weak));
|
||||
}
|
||||
|
||||
#endif // BOARD_API_PRIVATE_H
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "Board/Config.h"
|
||||
#if defined(CONFIG_EN_RGB)
|
||||
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
#include "Board/Pico_WS2812/WS2812.hpp"
|
||||
#include "Board/board_api_private/board_api_private.h"
|
||||
|
||||
namespace board_api_rgb {
|
||||
|
||||
WS2812& get_ws2812() {
|
||||
static WS2812 ws2812 = WS2812(RGB_PXL_PIN, 1, pio1, 0, WS2812::FORMAT_GRB);
|
||||
return ws2812;
|
||||
}
|
||||
|
||||
void init() {
|
||||
#if defined(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(0xFF, 0, 0);
|
||||
}
|
||||
|
||||
void set_led(uint8_t r, uint8_t g, uint8_t b) {
|
||||
get_ws2812().setPixelColor(0, WS2812::RGB(r, g, b));
|
||||
get_ws2812().show();
|
||||
}
|
||||
|
||||
} // namespace board_api_rgb
|
||||
|
||||
#endif // defined(CONFIG_EN_RGB)
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "Board/Config.h"
|
||||
#if defined(CONFIG_EN_USB_HOST)
|
||||
|
||||
#include <atomic>
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
#include "Board/board_api_private/board_api_private.h"
|
||||
|
||||
namespace board_api_usbh {
|
||||
|
||||
std::atomic<bool> host_connected_ = false;
|
||||
|
||||
void host_pin_isr(uint gpio, uint32_t events) {
|
||||
gpio_set_irq_enabled(PIO_USB_DP_PIN, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
|
||||
gpio_set_irq_enabled(PIO_USB_DP_PIN + 1, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
|
||||
|
||||
if (gpio == PIO_USB_DP_PIN || gpio == PIO_USB_DP_PIN + 1) {
|
||||
uint32_t dp_state = gpio_get(PIO_USB_DP_PIN);
|
||||
uint32_t dm_state = gpio_get(PIO_USB_DP_PIN + 1);
|
||||
|
||||
if (dp_state || dm_state) {
|
||||
host_connected_.store(true);
|
||||
} else {
|
||||
host_connected_.store(false);
|
||||
gpio_set_irq_enabled(PIO_USB_DP_PIN, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
|
||||
gpio_set_irq_enabled(PIO_USB_DP_PIN + 1, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool host_connected() {
|
||||
return host_connected_.load();
|
||||
}
|
||||
|
||||
void init() {
|
||||
#if defined(VCC_EN_PIN)
|
||||
gpio_init(VCC_EN_PIN);
|
||||
gpio_set_dir(VCC_EN_PIN, GPIO_OUT);
|
||||
gpio_put(VCC_EN_PIN, 1);
|
||||
#endif
|
||||
|
||||
gpio_init(PIO_USB_DP_PIN);
|
||||
gpio_set_dir(PIO_USB_DP_PIN, GPIO_IN);
|
||||
gpio_pull_down(PIO_USB_DP_PIN);
|
||||
|
||||
gpio_init(PIO_USB_DP_PIN + 1);
|
||||
gpio_set_dir(PIO_USB_DP_PIN + 1, GPIO_IN);
|
||||
gpio_pull_down(PIO_USB_DP_PIN + 1);
|
||||
|
||||
if (gpio_get(PIO_USB_DP_PIN) || gpio_get(PIO_USB_DP_PIN + 1)) {
|
||||
host_connected_.store(true);
|
||||
} else {
|
||||
gpio_set_irq_enabled_with_callback(PIO_USB_DP_PIN, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &host_pin_isr);
|
||||
gpio_set_irq_enabled_with_callback(PIO_USB_DP_PIN + 1, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &host_pin_isr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace board_api_usbh
|
||||
|
||||
#endif // defined(CONFIG_EN_USB_HOST)
|
||||
50
Firmware/RP2040/src/Board/esp32_api.cpp
Normal file
50
Firmware/RP2040/src/Board/esp32_api.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "Board/Config.h"
|
||||
#if defined(CONFIG_EN_ESP32)
|
||||
|
||||
#include <pico/stdlib.h>
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
#include "Board/esp32_api.h"
|
||||
|
||||
bool esp32_api::uart_bridge_mode()
|
||||
{
|
||||
gpio_pull_up(MODE_SEL_PIN);
|
||||
return (gpio_get(MODE_SEL_PIN) == 0);
|
||||
}
|
||||
|
||||
void esp32_api::reset()
|
||||
{
|
||||
gpio_put(ESP_RST_PIN, 0);
|
||||
sleep_ms(500);
|
||||
gpio_put(ESP_RST_PIN, 1);
|
||||
sleep_ms(250);
|
||||
}
|
||||
|
||||
void esp32_api::enter_programming_mode()
|
||||
{
|
||||
gpio_put(ESP_PROG_PIN, 1);
|
||||
sleep_ms(250);
|
||||
gpio_put(ESP_PROG_PIN, 0);
|
||||
sleep_ms(250);
|
||||
|
||||
reset();
|
||||
|
||||
gpio_put(ESP_PROG_PIN, 1);
|
||||
}
|
||||
|
||||
void esp32_api::init()
|
||||
{
|
||||
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);
|
||||
|
||||
gpio_init(MODE_SEL_PIN);
|
||||
gpio_set_dir(MODE_SEL_PIN, GPIO_IN);
|
||||
gpio_pull_up(MODE_SEL_PIN);
|
||||
}
|
||||
|
||||
#endif // defined(CONFIG_EN_ESP32)
|
||||
13
Firmware/RP2040/src/Board/esp32_api.h
Normal file
13
Firmware/RP2040/src/Board/esp32_api.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "Board/Config.h"
|
||||
#if defined(CONFIG_EN_ESP32)
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace esp32_api {
|
||||
bool uart_bridge_mode();
|
||||
void reset();
|
||||
void enter_programming_mode();
|
||||
void init();
|
||||
} // namespace board_api_esp32
|
||||
|
||||
#endif // defined(CONFIG_EN_ESP32)
|
||||
89
Firmware/RP2040/src/Board/ogxm_log.cpp
Normal file
89
Firmware/RP2040/src/Board/ogxm_log.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "Board/Config.h"
|
||||
#if defined(CONFIG_OGXM_DEBUG)
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <pico/mutex.h>
|
||||
#include <hardware/uart.h>
|
||||
#include <hardware/gpio.h>
|
||||
|
||||
#include "USBDevice/DeviceDriver/DeviceDriverTypes.h"
|
||||
#include "Board/ogxm_log.h"
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, DeviceDriverType type) {
|
||||
switch (type) {
|
||||
case DeviceDriverType::NONE: os << "NONE"; break;
|
||||
case DeviceDriverType::XBOXOG: os << "XBOXOG"; break;
|
||||
case DeviceDriverType::XBOXOG_SB: os << "XBOXOG_SB"; break;
|
||||
case DeviceDriverType::XBOXOG_XR: os << "XBOXOG_XR"; break;
|
||||
case DeviceDriverType::XINPUT: os << "XINPUT"; break;
|
||||
case DeviceDriverType::PS3: os << "PS3"; break;
|
||||
case DeviceDriverType::DINPUT: os << "DINPUT"; break;
|
||||
case DeviceDriverType::PSCLASSIC: os << "PSCLASSIC"; break;
|
||||
case DeviceDriverType::SWITCH: os << "SWITCH"; break;
|
||||
case DeviceDriverType::WEBAPP: os << "WEBAPP"; break;
|
||||
case DeviceDriverType::UART_BRIDGE: os << "UART_BRIDGE"; break;
|
||||
default: os << "UNKNOWN"; break;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
namespace ogxm_log {
|
||||
|
||||
void init() {
|
||||
uart_init(DEBUG_UART_PORT, PICO_DEFAULT_UART_BAUD_RATE);
|
||||
gpio_set_function(PICO_DEFAULT_UART_TX_PIN, GPIO_FUNC_UART);
|
||||
gpio_set_function(PICO_DEFAULT_UART_RX_PIN, GPIO_FUNC_UART);
|
||||
}
|
||||
|
||||
void log(const std::string& message) {
|
||||
static mutex_t log_mutex;
|
||||
|
||||
if (!mutex_is_initialized(&log_mutex)) {
|
||||
mutex_init(&log_mutex);
|
||||
}
|
||||
|
||||
mutex_enter_blocking(&log_mutex);
|
||||
|
||||
std::string formatted_msg = "OGXM: " + message;
|
||||
|
||||
uart_puts(DEBUG_UART_PORT, formatted_msg.c_str());
|
||||
|
||||
mutex_exit(&log_mutex);
|
||||
}
|
||||
|
||||
void log(const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
char buffer[256];
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
|
||||
std::string formatted_msg = std::string(buffer);
|
||||
|
||||
log(formatted_msg);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void log_hex(const uint8_t* data, size_t size) {
|
||||
std::ostringstream hex_stream;
|
||||
hex_stream << std::hex << std::setfill('0');
|
||||
int count = 0;
|
||||
for (uint16_t i = 0; i < size; ++i) {
|
||||
hex_stream << std::setw(2) << static_cast<int>(data[i]) << " ";
|
||||
if (++count == 16) {
|
||||
hex_stream << "\n";
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
hex_stream << "\n";
|
||||
log(hex_stream.str());
|
||||
}
|
||||
|
||||
} // namespace ogxm_log
|
||||
|
||||
#endif // defined(CONFIG_OGXM_DEBUG)
|
||||
55
Firmware/RP2040/src/Board/ogxm_log.h
Normal file
55
Firmware/RP2040/src/Board/ogxm_log.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef BOARD_API_LOG_H
|
||||
#define BOARD_API_LOG_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Board/Config.h"
|
||||
#if defined(CONFIG_OGXM_DEBUG)
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "USBDevice/DeviceDriver/DeviceDriverTypes.h"
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, DeviceDriverType type);
|
||||
|
||||
namespace ogxm_log {
|
||||
void init() __attribute__((weak));
|
||||
//Don't use this directly, use the OGXM_LOG macro
|
||||
void log(const std::string& message);
|
||||
//Don't use this directly, use the OGXM_LOG macro
|
||||
void log(const char* fmt, ...);
|
||||
//Don't use this directly, use the OGXM_LOG_HEX macro
|
||||
void log_hex(const uint8_t* data, size_t size);
|
||||
|
||||
template <typename T>
|
||||
std::string to_string(const T& value) {
|
||||
std::ostringstream stream;
|
||||
stream << value;
|
||||
return stream.str();
|
||||
}
|
||||
}
|
||||
|
||||
#define OGXM_LOG ogxm_log::log
|
||||
#define OGXM_LOG_HEX ogxm_log::log_hex
|
||||
#define OGXM_ASSERT(x) if (!(x)) { OGXM_LOG("Assertion failed: " #x); while(1); }
|
||||
#define OGXM_ASSERT_MSG(x, msg) if (!(x)) { OGXM_LOG("Assertion failed: " #x " " msg); while(1); }
|
||||
#define OGXM_TO_STRING ogxm_log::to_string
|
||||
|
||||
#else // CONFIG_OGXM_DEBUG
|
||||
|
||||
namespace ogxm_log {
|
||||
void init() __attribute__((weak));
|
||||
}
|
||||
|
||||
#define OGXM_LOG(...)
|
||||
#define OGXM_LOG_HEX(...)
|
||||
#define OGXM_ASSERT(x)
|
||||
#define OGXM_ASSERT_MSG(x, msg)
|
||||
#define OGXM_TO_STRING(x)
|
||||
|
||||
#endif // CONFIG_OGXM_DEBUG
|
||||
|
||||
#endif // BOARD_API_LOG_H
|
||||
66
Firmware/RP2040/src/Descriptors/CDCDev.h
Normal file
66
Firmware/RP2040/src/Descriptors/CDCDev.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#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) )
|
||||
|
||||
static const uint16_t VID = 0xCafe;
|
||||
static const uint16_t PID = (0x4000 | _PID_MAP(CDC, 0));
|
||||
static const uint16_t USB_VER = 0x0200;
|
||||
|
||||
const tusb_desc_device_t DESC_DEVICE =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = USB_VER,
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.idVendor = VID,
|
||||
.idProduct = PID,
|
||||
.bcdDevice = 0x0100,
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
enum Itf
|
||||
{
|
||||
CDC_0 = 0,
|
||||
CDC_0_DATA,
|
||||
ITF_TOTAL
|
||||
};
|
||||
|
||||
static const int CONFIG_LEN = (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN);
|
||||
|
||||
static const uint8_t DESC_CONFIG[] =
|
||||
{
|
||||
TUD_CONFIG_DESCRIPTOR(1, Itf::ITF_TOTAL, 0, CONFIG_LEN, 0x00, 500),
|
||||
TUD_CDC_DESCRIPTOR(Itf::CDC_0, 4, 0x80 | (Itf::CDC_0 + 1), 8, (Itf::CDC_0 + 2), 0x80 | (Itf::CDC_0 + 2), 64),
|
||||
};
|
||||
|
||||
static const uint8_t STRING_DESC_LANGUAGE[] = { 0x09, 0x04 };
|
||||
static const uint8_t STRING_MANUFACTURER[] = "Wired Opposite";
|
||||
static const uint8_t STRING_PRODUCT[] = "OGX-Mini";
|
||||
static const uint8_t STRING_INTERFACE[] = "OGX-Mini CDC";
|
||||
|
||||
static const uint8_t *DESC_STRING[] =
|
||||
{
|
||||
STRING_DESC_LANGUAGE,
|
||||
STRING_MANUFACTURER,
|
||||
STRING_PRODUCT,
|
||||
nullptr, //Serial
|
||||
STRING_INTERFACE
|
||||
};
|
||||
|
||||
}; // namespace CDCDesc
|
||||
|
||||
#endif // _CDC_DEV_DESCRIPTORS_H_
|
||||
298
Firmware/RP2040/src/Descriptors/DInput.h
Normal file
298
Firmware/RP2040/src/Descriptors/DInput.h
Normal file
@@ -0,0 +1,298 @@
|
||||
#pragma once
|
||||
|
||||
#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 SYS = 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;
|
||||
|
||||
InReport()
|
||||
{
|
||||
std::memset(this, 0, sizeof(InReport));
|
||||
}
|
||||
};
|
||||
#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
|
||||
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_
|
||||
442
Firmware/RP2040/src/Descriptors/PS3.h
Normal file
442
Firmware/RP2040/src/Descriptors/PS3.h
Normal file
@@ -0,0 +1,442 @@
|
||||
#ifndef _PS3_DESCRIPTORS_H_
|
||||
#define _PS3_DESCRIPTORS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstring>
|
||||
#include <random>
|
||||
|
||||
namespace PS3
|
||||
{
|
||||
static constexpr uint8_t MAGIC_BYTES[8] = { 0x21, 0x26, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
|
||||
static constexpr uint8_t JOYSTICK_MID = 0x7F;
|
||||
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 SYS = 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 unk0;
|
||||
|
||||
uint8_t buttons[3];
|
||||
uint8_t unk1;
|
||||
|
||||
uint8_t joystick_lx;
|
||||
uint8_t joystick_ly;
|
||||
uint8_t joystick_rx;
|
||||
uint8_t joystick_ry;
|
||||
|
||||
uint8_t unk2[2];
|
||||
uint8_t move_power_status;
|
||||
uint8_t unk3;
|
||||
|
||||
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 unk4[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 gyro_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 = gyro_z = SIXAXIS_MID;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(InReport) == 49, "PS3::InReport size mismatch");
|
||||
|
||||
struct OutReport
|
||||
{
|
||||
//uint8_t report_id;
|
||||
uint8_t reserved0;
|
||||
struct Rumble
|
||||
{
|
||||
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 */
|
||||
} rumble;
|
||||
uint8_t reserved1[4];
|
||||
uint8_t leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
|
||||
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%) */
|
||||
} leds[4]; /* LEDx at (4 - x) */
|
||||
struct LEDs unused; /* LED5, not actually soldered */
|
||||
uint8_t reserved2[13];
|
||||
|
||||
OutReport()
|
||||
{
|
||||
std::memset(this, 0, sizeof(OutReport));
|
||||
std::memcpy(this, DEFAULT_OUT_REPORT, sizeof(DEFAULT_OUT_REPORT));
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(OutReport) == 48, "PS3::OutReport size mismatch");
|
||||
static_assert(sizeof(OutReport) >= sizeof(DEFAULT_OUT_REPORT));
|
||||
|
||||
static constexpr uint8_t DEFAULT_BT_INFO_HEADER[] =
|
||||
{
|
||||
0xFF, 0xFF,
|
||||
0x00, 0x20, 0x40, 0xCE, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00
|
||||
};
|
||||
|
||||
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::memcpy(device_address, DEFAULT_BT_INFO_HEADER, sizeof(DEFAULT_BT_INFO_HEADER));
|
||||
|
||||
std::mt19937 gen(12345);
|
||||
std::uniform_int_distribution<uint8_t> dist(0, 0xFF);
|
||||
|
||||
for (uint8_t i = 4; i < sizeof(device_address); i++)
|
||||
{
|
||||
device_address[i] = dist(gen);
|
||||
}
|
||||
for (uint8_t i = 1; i < sizeof(host_address); i++)
|
||||
{
|
||||
host_address[i] = dist(gen);
|
||||
}
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(BTInfo) == 17, "PS3::BTInfo size mismatch");
|
||||
static_assert(sizeof(BTInfo) >= sizeof(DEFAULT_BT_INFO_HEADER));
|
||||
#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
|
||||
};
|
||||
|
||||
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.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)
|
||||
};
|
||||
|
||||
static constexpr uint8_t OUTPUT_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_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_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_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_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
|
||||
};
|
||||
} // 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_
|
||||
150
Firmware/RP2040/src/Descriptors/PS5.h
Normal file
150
Firmware/RP2040/src/Descriptors/PS5.h
Normal file
@@ -0,0 +1,150 @@
|
||||
#ifndef _PS5_DESCRIPTORS_H_
|
||||
#define _PS5_DESCRIPTORS_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
namespace PS5
|
||||
{
|
||||
static constexpr uint8_t DPAD_MASK = 0x0F;
|
||||
static constexpr uint8_t JOYSTICK_MID = 0x80;
|
||||
|
||||
namespace OutReportID
|
||||
{
|
||||
static constexpr uint8_t CONTROL = 0x02;
|
||||
static constexpr uint8_t RUMBLE = 0x05;
|
||||
};
|
||||
|
||||
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 reserved0;
|
||||
|
||||
struct Touchpad
|
||||
{
|
||||
uint32_t counter : 7; // Incremented every time a finger touches the touchpad
|
||||
uint32_t touching : 1; // Indicates if a finger is currently touching the touchpad
|
||||
uint32_t x : 12; // X coordinate of the touchpoint
|
||||
uint32_t y : 12; // Y coordinate of the touchpoint
|
||||
} points[2]; // Array of touchpoints (up to 2)
|
||||
// uint32_t touchpad[2];
|
||||
|
||||
uint8_t reserved1[12];
|
||||
|
||||
uint8_t status; // ?
|
||||
|
||||
uint8_t reserved2[10];
|
||||
|
||||
InReport()
|
||||
{
|
||||
std::memset(this, 0, sizeof(InReport));
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(InReport) == 60, "PS5::InReport is not correct size");
|
||||
|
||||
struct OutReport
|
||||
{
|
||||
uint8_t report_id;
|
||||
uint8_t control_flag[2];
|
||||
|
||||
/* 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 led_control_flag;
|
||||
uint8_t reserved3[2];
|
||||
uint8_t pulse_option;
|
||||
uint8_t led_brightness;
|
||||
uint8_t player_number;
|
||||
uint8_t lightbar_red;
|
||||
uint8_t lightbar_green;
|
||||
uint8_t lightbar_blue;
|
||||
|
||||
OutReport()
|
||||
{
|
||||
std::memset(this, 0, sizeof(OutReport));
|
||||
}
|
||||
};
|
||||
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_
|
||||
255
Firmware/RP2040/src/Descriptors/XInput.h
Normal file
255
Firmware/RP2040/src/Descriptors/XInput.h
Normal file
@@ -0,0 +1,255 @@
|
||||
#ifndef _XINPUT_DESCRIPTORS_H_
|
||||
#define _XINPUT_DESCRIPTORS_H_
|
||||
|
||||
#include <cstdint>
|
||||
#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];
|
||||
|
||||
InReport()
|
||||
{
|
||||
std::memset(this, 0, sizeof(InReport));
|
||||
report_size = sizeof(InReport);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(InReport) == 20, "XInput::InReport is misaligned");
|
||||
|
||||
struct WiredChatpadReport
|
||||
{
|
||||
uint8_t report_id;
|
||||
uint8_t chatpad[3];
|
||||
|
||||
WiredChatpadReport()
|
||||
{
|
||||
std::memset(this, 0, sizeof(WiredChatpadReport));
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(WiredChatpadReport) == 4, "XInput::WiredChatpadReport is misaligned");
|
||||
|
||||
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];
|
||||
|
||||
InReportWireless()
|
||||
{
|
||||
std::memset(this, 0, sizeof(InReportWireless));
|
||||
report_size = sizeof(InReportWireless);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(InReportWireless) == 28, "XInput::InReportWireless is misaligned");
|
||||
|
||||
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];
|
||||
|
||||
OutReport()
|
||||
{
|
||||
std::memset(this, 0, sizeof(OutReport));
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(OutReport) == 8, "XInput::OutReport is misaligned");
|
||||
#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 *DESC_STRING[] __attribute__((unused)) =
|
||||
{
|
||||
STRING_LANGUAGE,
|
||||
STRING_MANUFACTURER,
|
||||
STRING_PRODUCT,
|
||||
STRING_VERSION
|
||||
};
|
||||
|
||||
static const uint8_t DESC_DEVICE[] =
|
||||
{
|
||||
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 DESC_CONFIGURATION[] =
|
||||
{
|
||||
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_
|
||||
594
Firmware/RP2040/src/Descriptors/XboxOG.h
Normal file
594
Firmware/RP2040/src/Descriptors/XboxOG.h
Normal file
@@ -0,0 +1,594 @@
|
||||
#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;
|
||||
|
||||
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;
|
||||
|
||||
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_
|
||||
640
Firmware/RP2040/src/Gamepad/Gamepad.h
Normal file
640
Firmware/RP2040/src/Gamepad/Gamepad.h
Normal file
@@ -0,0 +1,640 @@
|
||||
#ifndef _GAMEPAD_H_
|
||||
#define _GAMEPAD_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
#include <limits>
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <pico/mutex.h>
|
||||
|
||||
#include "libfixmath/fix16.hpp"
|
||||
|
||||
#include "Gamepad/Range.h"
|
||||
#include "Gamepad/fix16ext.h"
|
||||
#include "UserSettings/UserProfile.h"
|
||||
#include "UserSettings/JoystickSettings.h"
|
||||
#include "UserSettings/TriggerSettings.h"
|
||||
#include "Board/ogxm_log.h"
|
||||
|
||||
class Gamepad
|
||||
{
|
||||
public:
|
||||
//Defaults used by device to get buttons
|
||||
|
||||
static constexpr uint8_t DPAD_UP = 0x01;
|
||||
static constexpr uint8_t DPAD_DOWN = 0x02;
|
||||
static constexpr uint8_t DPAD_LEFT = 0x04;
|
||||
static constexpr uint8_t DPAD_RIGHT = 0x08;
|
||||
static constexpr uint8_t DPAD_UP_LEFT = DPAD_UP | DPAD_LEFT;
|
||||
static constexpr uint8_t DPAD_UP_RIGHT = DPAD_UP | DPAD_RIGHT;
|
||||
static constexpr uint8_t DPAD_DOWN_LEFT = DPAD_DOWN | DPAD_LEFT;
|
||||
static constexpr uint8_t DPAD_DOWN_RIGHT = DPAD_DOWN | DPAD_RIGHT;
|
||||
static constexpr uint8_t DPAD_NONE = 0x00;
|
||||
|
||||
static constexpr uint16_t BUTTON_A = 0x0001;
|
||||
static constexpr uint16_t BUTTON_B = 0x0002;
|
||||
static constexpr uint16_t BUTTON_X = 0x0004;
|
||||
static constexpr uint16_t BUTTON_Y = 0x0008;
|
||||
static constexpr uint16_t BUTTON_L3 = 0x0010;
|
||||
static constexpr uint16_t BUTTON_R3 = 0x0020;
|
||||
static constexpr uint16_t BUTTON_BACK = 0x0040;
|
||||
static constexpr uint16_t BUTTON_START = 0x0080;
|
||||
static constexpr uint16_t BUTTON_LB = 0x0100;
|
||||
static constexpr uint16_t BUTTON_RB = 0x0200;
|
||||
static constexpr uint16_t BUTTON_SYS = 0x0400;
|
||||
static constexpr uint16_t BUTTON_MISC = 0x0800;
|
||||
|
||||
static constexpr uint8_t ANALOG_OFF_UP = 0;
|
||||
static constexpr uint8_t ANALOG_OFF_DOWN = 1;
|
||||
static constexpr uint8_t ANALOG_OFF_LEFT = 2;
|
||||
static constexpr uint8_t ANALOG_OFF_RIGHT = 3;
|
||||
static constexpr uint8_t ANALOG_OFF_A = 4;
|
||||
static constexpr uint8_t ANALOG_OFF_B = 5;
|
||||
static constexpr uint8_t ANALOG_OFF_X = 6;
|
||||
static constexpr uint8_t ANALOG_OFF_Y = 7;
|
||||
static constexpr uint8_t ANALOG_OFF_LB = 8;
|
||||
static constexpr uint8_t ANALOG_OFF_RB = 9;
|
||||
|
||||
//Mappings used by host to set buttons
|
||||
|
||||
uint8_t MAP_DPAD_UP = DPAD_UP ;
|
||||
uint8_t MAP_DPAD_DOWN = DPAD_DOWN ;
|
||||
uint8_t MAP_DPAD_LEFT = DPAD_LEFT ;
|
||||
uint8_t MAP_DPAD_RIGHT = DPAD_RIGHT ;
|
||||
uint8_t MAP_DPAD_UP_LEFT = DPAD_UP_LEFT ;
|
||||
uint8_t MAP_DPAD_UP_RIGHT = DPAD_UP_RIGHT ;
|
||||
uint8_t MAP_DPAD_DOWN_LEFT = DPAD_DOWN_LEFT ;
|
||||
uint8_t MAP_DPAD_DOWN_RIGHT = DPAD_DOWN_RIGHT;
|
||||
uint8_t MAP_DPAD_NONE = DPAD_NONE ;
|
||||
|
||||
uint16_t MAP_BUTTON_A = BUTTON_A ;
|
||||
uint16_t MAP_BUTTON_B = BUTTON_B ;
|
||||
uint16_t MAP_BUTTON_X = BUTTON_X ;
|
||||
uint16_t MAP_BUTTON_Y = BUTTON_Y ;
|
||||
uint16_t MAP_BUTTON_L3 = BUTTON_L3 ;
|
||||
uint16_t MAP_BUTTON_R3 = BUTTON_R3 ;
|
||||
uint16_t MAP_BUTTON_BACK = BUTTON_BACK ;
|
||||
uint16_t MAP_BUTTON_START = BUTTON_START;
|
||||
uint16_t MAP_BUTTON_LB = BUTTON_LB ;
|
||||
uint16_t MAP_BUTTON_RB = BUTTON_RB ;
|
||||
uint16_t MAP_BUTTON_SYS = BUTTON_SYS ;
|
||||
uint16_t MAP_BUTTON_MISC = BUTTON_MISC ;
|
||||
|
||||
uint8_t MAP_ANALOG_OFF_UP = ANALOG_OFF_UP ;
|
||||
uint8_t MAP_ANALOG_OFF_DOWN = ANALOG_OFF_DOWN ;
|
||||
uint8_t MAP_ANALOG_OFF_LEFT = ANALOG_OFF_LEFT ;
|
||||
uint8_t MAP_ANALOG_OFF_RIGHT = ANALOG_OFF_RIGHT;
|
||||
uint8_t MAP_ANALOG_OFF_A = ANALOG_OFF_A ;
|
||||
uint8_t MAP_ANALOG_OFF_B = ANALOG_OFF_B ;
|
||||
uint8_t MAP_ANALOG_OFF_X = ANALOG_OFF_X ;
|
||||
uint8_t MAP_ANALOG_OFF_Y = ANALOG_OFF_Y ;
|
||||
uint8_t MAP_ANALOG_OFF_LB = ANALOG_OFF_LB ;
|
||||
uint8_t MAP_ANALOG_OFF_RB = ANALOG_OFF_RB ;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PadIn
|
||||
{
|
||||
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;
|
||||
uint8_t analog[10];
|
||||
|
||||
PadIn()
|
||||
{
|
||||
std::memset(this, 0, sizeof(PadIn));
|
||||
}
|
||||
};
|
||||
|
||||
struct PadOut
|
||||
{
|
||||
uint8_t rumble_l;
|
||||
uint8_t rumble_r;
|
||||
|
||||
PadOut()
|
||||
{
|
||||
std::memset(this, 0, sizeof(PadOut));
|
||||
}
|
||||
};
|
||||
|
||||
using ChatpadIn = std::array<uint8_t, 3>;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
Gamepad()
|
||||
{
|
||||
mutex_init(&pad_in_mutex_);
|
||||
mutex_init(&pad_out_mutex_);
|
||||
mutex_init(&chatpad_in_mutex_);
|
||||
reset_pad_in();
|
||||
reset_pad_out();
|
||||
reset_chatpad_in();
|
||||
};
|
||||
|
||||
~Gamepad() = default;
|
||||
|
||||
//Get
|
||||
inline bool new_pad_in() const { return new_pad_in_.load(); }
|
||||
inline bool new_pad_out() const { return new_pad_out_.load(); }
|
||||
|
||||
//True if both host and device have enabled analog
|
||||
inline bool analog_enabled() const { return analog_enabled_.load(std::memory_order_relaxed); }
|
||||
|
||||
inline PadIn get_pad_in()
|
||||
{
|
||||
mutex_enter_blocking(&pad_in_mutex_);
|
||||
PadIn pad_in = pad_in_;
|
||||
new_pad_in_.store(false);
|
||||
mutex_exit(&pad_in_mutex_);
|
||||
|
||||
return pad_in;
|
||||
}
|
||||
|
||||
inline PadOut get_pad_out()
|
||||
{
|
||||
mutex_enter_blocking(&pad_out_mutex_);
|
||||
PadOut pad_out = pad_out_;
|
||||
new_pad_out_.store(false);
|
||||
mutex_exit(&pad_out_mutex_);
|
||||
|
||||
return pad_out;
|
||||
}
|
||||
|
||||
inline ChatpadIn get_chatpad_in()
|
||||
{
|
||||
mutex_enter_blocking(&chatpad_in_mutex_);
|
||||
ChatpadIn chatpad_in = chatpad_in_;
|
||||
mutex_exit(&chatpad_in_mutex_);
|
||||
|
||||
return chatpad_in;
|
||||
}
|
||||
|
||||
//Set
|
||||
|
||||
void set_analog_device(bool value)
|
||||
{
|
||||
analog_device_.store(value);
|
||||
if (analog_host_.load() && analog_device_.load() && profile_analog_enabled_)
|
||||
{
|
||||
analog_enabled_.store(true);
|
||||
}
|
||||
}
|
||||
|
||||
void set_analog_host(bool value)
|
||||
{
|
||||
analog_host_.store(value);
|
||||
if (analog_host_.load() && analog_device_.load() && profile_analog_enabled_)
|
||||
{
|
||||
analog_enabled_.store(true);
|
||||
}
|
||||
}
|
||||
|
||||
void set_profile(const UserProfile& user_profile)
|
||||
{
|
||||
set_profile_mappings(user_profile);
|
||||
set_profile_settings(user_profile);
|
||||
}
|
||||
|
||||
inline void set_pad_in(PadIn pad_in)
|
||||
{
|
||||
mutex_enter_blocking(&pad_in_mutex_);
|
||||
pad_in_ = pad_in;
|
||||
new_pad_in_.store(true);
|
||||
mutex_exit(&pad_in_mutex_);
|
||||
}
|
||||
|
||||
inline void set_pad_out(const PadOut& pad_out)
|
||||
{
|
||||
mutex_enter_blocking(&pad_out_mutex_);
|
||||
pad_out_ = pad_out;
|
||||
new_pad_out_.store(true);
|
||||
mutex_exit(&pad_out_mutex_);
|
||||
}
|
||||
|
||||
inline void set_chatpad_in(const ChatpadIn& chatpad_in)
|
||||
{
|
||||
mutex_enter_blocking(&chatpad_in_mutex_);
|
||||
chatpad_in_ = chatpad_in;
|
||||
mutex_exit(&chatpad_in_mutex_);
|
||||
}
|
||||
|
||||
inline void reset_pad_in()
|
||||
{
|
||||
mutex_enter_blocking(&pad_in_mutex_);
|
||||
pad_in_ = PadIn();
|
||||
mutex_exit(&pad_in_mutex_);
|
||||
new_pad_in_.store(true);
|
||||
}
|
||||
|
||||
inline void reset_pad_out()
|
||||
{
|
||||
mutex_enter_blocking(&pad_out_mutex_);
|
||||
pad_out_ = PadOut();
|
||||
new_pad_out_.store(true);
|
||||
mutex_exit(&pad_out_mutex_);
|
||||
}
|
||||
|
||||
inline void reset_chatpad_in()
|
||||
{
|
||||
mutex_enter_blocking(&chatpad_in_mutex_);
|
||||
chatpad_in_.fill(0);
|
||||
mutex_exit(&chatpad_in_mutex_);
|
||||
}
|
||||
|
||||
template <uint8_t bits = 0, typename T>
|
||||
inline std::pair<int16_t, int16_t> scale_joystick_r(T x, T y, bool invert_y = false) const
|
||||
{
|
||||
int16_t joy_x = 0;
|
||||
int16_t joy_y = 0;
|
||||
if constexpr (bits > 0)
|
||||
{
|
||||
joy_x = Range::scale_from_bits<int16_t, bits>(x);
|
||||
joy_y = Range::scale_from_bits<int16_t, bits>(y);
|
||||
}
|
||||
else if constexpr (!std::is_same_v<T, int16_t>)
|
||||
{
|
||||
joy_x = Range::scale<int16_t>(x);
|
||||
joy_y = Range::scale<int16_t>(y);
|
||||
}
|
||||
else
|
||||
{
|
||||
joy_x = x;
|
||||
joy_y = y;
|
||||
}
|
||||
|
||||
return joy_settings_r_en_
|
||||
? apply_joystick_settings(joy_x, joy_y, joy_settings_r_, invert_y)
|
||||
: std::make_pair(joy_x, invert_y ? Range::invert(joy_y) : joy_y);
|
||||
}
|
||||
|
||||
template <uint8_t bits = 0, typename T>
|
||||
inline std::pair<int16_t, int16_t> scale_joystick_l(T x, T y, bool invert_y = false) const
|
||||
{
|
||||
int16_t joy_x = 0;
|
||||
int16_t joy_y = 0;
|
||||
if constexpr (bits > 0)
|
||||
{
|
||||
joy_x = Range::scale_from_bits<int16_t, bits>(x);
|
||||
joy_y = Range::scale_from_bits<int16_t, bits>(y);
|
||||
}
|
||||
else if constexpr (!std::is_same_v<T, int16_t>)
|
||||
{
|
||||
joy_x = Range::scale<int16_t>(x);
|
||||
joy_y = Range::scale<int16_t>(y);
|
||||
}
|
||||
else
|
||||
{
|
||||
joy_x = x;
|
||||
joy_y = y;
|
||||
}
|
||||
|
||||
return joy_settings_l_en_
|
||||
? apply_joystick_settings(joy_x, joy_y, joy_settings_l_, invert_y)
|
||||
: std::make_pair(joy_x, invert_y ? Range::invert(joy_y) : joy_y);
|
||||
}
|
||||
|
||||
template <uint8_t bits = 0, typename T>
|
||||
inline uint8_t scale_trigger_l(T value) const
|
||||
{
|
||||
uint8_t trigger_value = 0;
|
||||
if constexpr (bits > 0)
|
||||
{
|
||||
trigger_value = Range::scale_from_bits<uint8_t, bits>(value);
|
||||
}
|
||||
else if constexpr (!std::is_same_v<T, uint8_t>)
|
||||
{
|
||||
trigger_value = Range::scale<uint8_t>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
trigger_value = value;
|
||||
}
|
||||
return trig_settings_l_en_
|
||||
? apply_trigger_settings(trigger_value, trig_settings_l_)
|
||||
: trigger_value;
|
||||
}
|
||||
|
||||
template <uint8_t bits = 0, typename T>
|
||||
inline uint8_t scale_trigger_r(T value) const
|
||||
{
|
||||
uint8_t trigger_value = 0;
|
||||
if constexpr (bits > 0)
|
||||
{
|
||||
trigger_value = Range::scale_from_bits<uint8_t, bits>(value);
|
||||
}
|
||||
else if constexpr (!std::is_same_v<T, uint8_t>)
|
||||
{
|
||||
trigger_value = Range::scale<uint8_t>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
trigger_value = value;
|
||||
}
|
||||
return trig_settings_r_en_
|
||||
? apply_trigger_settings(trigger_value, trig_settings_r_)
|
||||
: trigger_value;
|
||||
}
|
||||
|
||||
private:
|
||||
mutex_t pad_in_mutex_;
|
||||
mutex_t pad_out_mutex_;
|
||||
mutex_t chatpad_in_mutex_;
|
||||
|
||||
PadOut pad_out_;
|
||||
PadIn pad_in_;
|
||||
ChatpadIn chatpad_in_{0};
|
||||
|
||||
std::atomic<bool> new_pad_in_{false};
|
||||
std::atomic<bool> new_pad_out_{false};
|
||||
|
||||
std::atomic<bool> analog_enabled_{false};
|
||||
std::atomic<bool> analog_host_{false};
|
||||
std::atomic<bool> analog_device_{false};
|
||||
|
||||
bool profile_analog_enabled_{false};
|
||||
|
||||
JoystickSettings joy_settings_l_;
|
||||
JoystickSettings joy_settings_r_;
|
||||
TriggerSettings trig_settings_l_;
|
||||
TriggerSettings trig_settings_r_;
|
||||
|
||||
bool joy_settings_l_en_{false};
|
||||
bool joy_settings_r_en_{false};
|
||||
bool trig_settings_l_en_{false};
|
||||
bool trig_settings_r_en_{false};
|
||||
|
||||
void set_profile_settings(const UserProfile& profile)
|
||||
{
|
||||
profile_analog_enabled_ = profile.analog_enabled ? true : false;
|
||||
OGXM_LOG("profile_analog_enabled_: %d\n", profile_analog_enabled_);
|
||||
|
||||
if ((joy_settings_l_en_ = !joy_settings_l_.is_same(profile.joystick_settings_l)))
|
||||
{
|
||||
joy_settings_l_.set_from_raw(profile.joystick_settings_l);
|
||||
//This needs to be addressed in the webapp, just multiply here for now
|
||||
joy_settings_l_.axis_restrict *= static_cast<int16_t>(100);
|
||||
joy_settings_l_.angle_restrict *= static_cast<int16_t>(100);
|
||||
joy_settings_l_.anti_dz_angular *= static_cast<int16_t>(100);
|
||||
}
|
||||
if ((joy_settings_r_en_ = !joy_settings_r_.is_same(profile.joystick_settings_r)))
|
||||
{
|
||||
joy_settings_r_.set_from_raw(profile.joystick_settings_r);
|
||||
//This needs to be addressed in the webapp, just multiply here for now
|
||||
joy_settings_r_.axis_restrict *= static_cast<int16_t>(100);
|
||||
joy_settings_r_.angle_restrict *= static_cast<int16_t>(100);
|
||||
joy_settings_r_.anti_dz_angular *= static_cast<int16_t>(100);
|
||||
}
|
||||
if ((trig_settings_l_en_ = !trig_settings_l_.is_same(profile.trigger_settings_l)))
|
||||
{
|
||||
trig_settings_l_.set_from_raw(profile.trigger_settings_l);
|
||||
}
|
||||
if ((trig_settings_r_en_ = !trig_settings_r_.is_same(profile.trigger_settings_r)))
|
||||
{
|
||||
trig_settings_r_.set_from_raw(profile.trigger_settings_r);
|
||||
}
|
||||
|
||||
OGXM_LOG("GamepadMapper: JoyL: %s, JoyR: %s, TrigL: %s, TrigR: %s\n",
|
||||
joy_settings_l_en_ ? "Enabled" : "Disabled",
|
||||
joy_settings_r_en_ ? "Enabled" : "Disabled",
|
||||
trig_settings_l_en_ ? "Enabled" : "Disabled",
|
||||
trig_settings_r_en_ ? "Enabled" : "Disabled");
|
||||
}
|
||||
|
||||
void set_profile_mappings(const UserProfile& profile)
|
||||
{
|
||||
MAP_DPAD_UP = profile.dpad_up;
|
||||
MAP_DPAD_DOWN = profile.dpad_down;
|
||||
MAP_DPAD_LEFT = profile.dpad_left;
|
||||
MAP_DPAD_RIGHT = profile.dpad_right;
|
||||
MAP_DPAD_UP_LEFT = profile.dpad_up | profile.dpad_left;
|
||||
MAP_DPAD_UP_RIGHT = profile.dpad_up | profile.dpad_right;
|
||||
MAP_DPAD_DOWN_LEFT = profile.dpad_down | profile.dpad_left;
|
||||
MAP_DPAD_DOWN_RIGHT = profile.dpad_down | profile.dpad_right;
|
||||
MAP_DPAD_NONE = 0;
|
||||
|
||||
MAP_BUTTON_A = profile.button_a;
|
||||
MAP_BUTTON_B = profile.button_b;
|
||||
MAP_BUTTON_X = profile.button_x;
|
||||
MAP_BUTTON_Y = profile.button_y;
|
||||
MAP_BUTTON_L3 = profile.button_l3;
|
||||
MAP_BUTTON_R3 = profile.button_r3;
|
||||
MAP_BUTTON_BACK = profile.button_back;
|
||||
MAP_BUTTON_START = profile.button_start;
|
||||
MAP_BUTTON_LB = profile.button_lb;
|
||||
MAP_BUTTON_RB = profile.button_rb;
|
||||
MAP_BUTTON_SYS = profile.button_sys;
|
||||
MAP_BUTTON_MISC = profile.button_misc;
|
||||
|
||||
MAP_ANALOG_OFF_UP = profile.analog_off_up;
|
||||
MAP_ANALOG_OFF_DOWN = profile.analog_off_down;
|
||||
MAP_ANALOG_OFF_LEFT = profile.analog_off_left;
|
||||
MAP_ANALOG_OFF_RIGHT = profile.analog_off_right;
|
||||
MAP_ANALOG_OFF_A = profile.analog_off_a;
|
||||
MAP_ANALOG_OFF_B = profile.analog_off_b;
|
||||
MAP_ANALOG_OFF_X = profile.analog_off_x;
|
||||
MAP_ANALOG_OFF_Y = profile.analog_off_y;
|
||||
MAP_ANALOG_OFF_LB = profile.analog_off_lb;
|
||||
MAP_ANALOG_OFF_RB = profile.analog_off_rb;
|
||||
}
|
||||
|
||||
static inline std::pair<int16_t, int16_t> apply_joystick_settings(
|
||||
int16_t gp_joy_x,
|
||||
int16_t gp_joy_y,
|
||||
const JoystickSettings& set,
|
||||
bool invert_y)
|
||||
{
|
||||
static const Fix16
|
||||
FIX_0(0.0f),
|
||||
FIX_1(1.0f),
|
||||
FIX_2(2.0f),
|
||||
FIX_45(45.0f),
|
||||
FIX_90(90.0f),
|
||||
FIX_100(100.0f),
|
||||
FIX_180(180.0f),
|
||||
FIX_EPSILON(0.0001f),
|
||||
FIX_EPSILON2(0.001f),
|
||||
FIX_ELLIPSE_DEF(1.570796f),
|
||||
FIX_DIAG_DIVISOR(0.29289f);
|
||||
|
||||
Fix16 x = (set.invert_x ? Fix16(Range::invert(gp_joy_x)) : Fix16(gp_joy_x)) / Range::MAX<int16_t>;
|
||||
Fix16 y = ((set.invert_y ^ invert_y) ? Fix16(Range::invert(gp_joy_y)) : Fix16(gp_joy_y)) / Range::MAX<int16_t>;
|
||||
|
||||
const Fix16 abs_x = fix16::abs(x);
|
||||
const Fix16 abs_y = fix16::abs(y);
|
||||
const Fix16 inv_axis_restrict = FIX_1 / (FIX_1 - set.axis_restrict);
|
||||
|
||||
Fix16 rAngle = (abs_x < FIX_EPSILON)
|
||||
? FIX_90
|
||||
: fix16::rad2deg(fix16::abs(fix16::atan(y / x)));
|
||||
|
||||
Fix16 axial_x = (abs_x <= set.axis_restrict && rAngle > FIX_45)
|
||||
? FIX_0
|
||||
: ((abs_x - set.axis_restrict) * inv_axis_restrict);
|
||||
|
||||
Fix16 axial_y = (abs_y <= set.axis_restrict && rAngle <= FIX_45)
|
||||
? FIX_0
|
||||
: ((abs_y - set.axis_restrict) * inv_axis_restrict);
|
||||
|
||||
Fix16 in_magnitude = fix16::sqrt(fix16::sq(axial_x) + fix16::sq(axial_y));
|
||||
|
||||
if (in_magnitude < set.dz_inner)
|
||||
{
|
||||
return { 0, 0 };
|
||||
}
|
||||
|
||||
Fix16 angle =
|
||||
fix16::abs(axial_x) < FIX_EPSILON
|
||||
? FIX_90
|
||||
: fix16::rad2deg(fix16::abs(fix16::atan(axial_y / axial_x)));
|
||||
|
||||
Fix16 anti_r_scale = (set.anti_dz_square_y_scale == FIX_0) ? set.anti_dz_square : set.anti_dz_square_y_scale;
|
||||
Fix16 anti_dz_c = set.anti_dz_circle;
|
||||
|
||||
if (anti_r_scale > FIX_0 && anti_dz_c > FIX_0)
|
||||
{
|
||||
Fix16 anti_ellip_scale = anti_ellip_scale / anti_dz_c;
|
||||
Fix16 ellipse_angle = fix16::atan((FIX_1 / anti_ellip_scale) * fix16::tan(fix16::rad2deg(rAngle)));
|
||||
ellipse_angle = (ellipse_angle < FIX_0) ? FIX_ELLIPSE_DEF : ellipse_angle;
|
||||
|
||||
Fix16 ellipse_x = fix16::cos(ellipse_angle);
|
||||
Fix16 ellipse_y = fix16::sqrt(fix16::sq(anti_ellip_scale) * (FIX_1 - fix16::sq(ellipse_x)));
|
||||
anti_dz_c *= fix16::sqrt(fix16::sq(ellipse_x) + fix16::sq(ellipse_y));
|
||||
}
|
||||
|
||||
if (anti_dz_c > FIX_0)
|
||||
{
|
||||
anti_dz_c = anti_dz_c / ((anti_dz_c * (FIX_1 - set.anti_dz_circle / set.dz_outer)) / (anti_dz_c * (FIX_1 - set.anti_dz_square)));
|
||||
}
|
||||
|
||||
if (abs_x > set.axis_restrict && abs_y > set.axis_restrict)
|
||||
{
|
||||
const Fix16 FIX_ANGLE_MAX = set.angle_restrict / 2.0f;
|
||||
|
||||
if (angle > FIX_0 && angle < FIX_ANGLE_MAX)
|
||||
{
|
||||
angle = FIX_0;
|
||||
}
|
||||
if (angle > (FIX_90 - FIX_ANGLE_MAX))
|
||||
{
|
||||
angle = FIX_90;
|
||||
}
|
||||
if (angle > FIX_ANGLE_MAX && angle < (FIX_90 - FIX_ANGLE_MAX))
|
||||
{
|
||||
angle = ((angle - FIX_ANGLE_MAX) * FIX_90) / ((FIX_90 - FIX_ANGLE_MAX) - FIX_ANGLE_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
Fix16 ref_angle = (angle < FIX_EPSILON2) ? FIX_0 : angle;
|
||||
Fix16 diagonal = (angle > FIX_45) ? (((angle - FIX_45) * (-FIX_45)) / FIX_45) + FIX_45 : angle;
|
||||
|
||||
const Fix16 angle_comp = set.angle_restrict / FIX_2;
|
||||
|
||||
if (angle < FIX_90 && angle > FIX_0)
|
||||
{
|
||||
angle = ((angle * ((FIX_90 - angle_comp) - angle_comp)) / FIX_90) + angle_comp;
|
||||
}
|
||||
|
||||
if (axial_x < FIX_0 && axial_y > FIX_0)
|
||||
{
|
||||
angle = -angle;
|
||||
}
|
||||
if (axial_x > FIX_0 && axial_y < FIX_0)
|
||||
{
|
||||
angle = angle - FIX_180;
|
||||
}
|
||||
if (axial_x < FIX_0 && axial_y < FIX_0)
|
||||
{
|
||||
angle = angle + FIX_180;
|
||||
}
|
||||
|
||||
//Deadzone Warp
|
||||
Fix16 out_magnitude = (in_magnitude - set.dz_inner) / (set.anti_dz_outer - set.dz_inner);
|
||||
out_magnitude = fix16::pow(out_magnitude, (FIX_1 / set.curve)) * (set.dz_outer - anti_dz_c) + anti_dz_c;
|
||||
out_magnitude = (out_magnitude > set.dz_outer && !set.uncap_radius) ? set.dz_outer : out_magnitude;
|
||||
|
||||
Fix16 d_scale = (((out_magnitude - anti_dz_c) * (set.diag_scale_max - set.diag_scale_min)) / (set.dz_outer - anti_dz_c)) + set.diag_scale_min;
|
||||
Fix16 c_scale = (diagonal * (FIX_1 / fix16::sqrt(FIX_2))) / FIX_45; //Both these lines scale the intensity of the warping
|
||||
c_scale = FIX_1 - fix16::sqrt(FIX_1 - c_scale * c_scale); //based on a circular curve to the perfect diagonal
|
||||
d_scale = (c_scale * (d_scale - FIX_1)) / FIX_DIAG_DIVISOR + FIX_1;
|
||||
|
||||
out_magnitude = out_magnitude * d_scale;
|
||||
|
||||
//Scaling values for square antideadzone
|
||||
Fix16 new_x = fix16::cos(fix16::deg2rad(angle)) * out_magnitude;
|
||||
Fix16 new_y = fix16::sin(fix16::deg2rad(angle)) * out_magnitude;
|
||||
|
||||
//Magic angle wobble fix by user ME.
|
||||
// if (angle > 45.0 && angle < 225.0) {
|
||||
// newX = inv(Math.sin(deg2rad(angle - 90.0)))*outputMagnitude;
|
||||
// newY = inv(Math.cos(deg2rad(angle - 270.0)))*outputMagnitude;
|
||||
// }
|
||||
|
||||
//Square antideadzone scaling
|
||||
Fix16 output_x = fix16::abs(new_x) * (FIX_1 - set.anti_dz_square / set.dz_outer) + set.anti_dz_square;
|
||||
if (x < FIX_0)
|
||||
{
|
||||
output_x = -output_x;
|
||||
}
|
||||
if (ref_angle == FIX_90)
|
||||
{
|
||||
output_x = FIX_0;
|
||||
}
|
||||
|
||||
Fix16 output_y = fix16::abs(new_y) * (FIX_1 - anti_r_scale / set.dz_outer) + anti_r_scale;
|
||||
if (y < FIX_0)
|
||||
{
|
||||
output_y = -output_y;
|
||||
}
|
||||
if (ref_angle == FIX_0)
|
||||
{
|
||||
output_y = FIX_0;
|
||||
}
|
||||
|
||||
output_x = fix16::clamp(output_x, -FIX_1, FIX_1) * Range::MAX<int16_t>;
|
||||
output_y = fix16::clamp(output_y, -FIX_1, FIX_1) * Range::MAX<int16_t>;
|
||||
|
||||
return { static_cast<int16_t>(fix16_to_int(output_x)), static_cast<int16_t>(fix16_to_int(output_y)) };
|
||||
}
|
||||
|
||||
uint8_t apply_trigger_settings(uint8_t value, const TriggerSettings& set) const
|
||||
{
|
||||
Fix16 abs_value = fix16::abs(Fix16(static_cast<int16_t>(value)) / static_cast<int16_t>(Range::MAX<uint8_t>));
|
||||
|
||||
if (abs_value < set.dz_inner)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const Fix16
|
||||
FIX_0(0.0f),
|
||||
FIX_1(1.0f),
|
||||
FIX_2(2.0f);
|
||||
|
||||
Fix16 value_out = (abs_value - set.dz_inner) / (set.anti_dz_outer - set.dz_inner);
|
||||
value_out = fix16::clamp(value_out, FIX_0, FIX_1);
|
||||
|
||||
if (set.anti_dz_inner > FIX_0)
|
||||
{
|
||||
value_out = set.anti_dz_inner + (FIX_1 - set.anti_dz_inner) * value_out;
|
||||
}
|
||||
if (set.curve != FIX_1)
|
||||
{
|
||||
value_out = fix16::pow(value_out, FIX_1 / set.curve);
|
||||
}
|
||||
if (set.anti_dz_outer < FIX_1)
|
||||
{
|
||||
value_out = fix16::clamp(value_out * (FIX_1 / (FIX_1 - set.anti_dz_outer)), FIX_0, FIX_1);
|
||||
}
|
||||
|
||||
value_out *= set.dz_outer;
|
||||
return static_cast<uint8_t>(fix16_to_int(value_out * static_cast<int16_t>(Range::MAX<uint8_t>)));
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _GAMEPAD_H_
|
||||
204
Firmware/RP2040/src/Gamepad/Range.h
Normal file
204
Firmware/RP2040/src/Gamepad/Range.h
Normal file
@@ -0,0 +1,204 @@
|
||||
#ifndef _RANGE_H_
|
||||
#define _RANGE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include "Board/ogxm_log.h"
|
||||
|
||||
namespace Range {
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T MAX = std::numeric_limits<T>::max();
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T MIN = std::numeric_limits<T>::min();
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T MID =
|
||||
[] {
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
return MAX<T> / 2 + 1;
|
||||
}
|
||||
return 0;
|
||||
}();
|
||||
static_assert(MID<uint8_t> == 128, "MID<uint8_t> != 128");
|
||||
static_assert(MID<int8_t> == 0, "MID<int8_t> != 0");
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr uint16_t NUM_BITS = sizeof(T) * 8;
|
||||
|
||||
//Maximum value for a given number of bits, result will be signed/unsigned depending on type
|
||||
template<typename T, uint8_t bits>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T BITS_MAX()
|
||||
{
|
||||
static_assert(bits <= NUM_BITS<T>, "BITS_MAX: Return type cannot represet maximum bit value");
|
||||
static_assert(bits <= 64, "BITS_MAX: Bits exceed 64");
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
return (1ULL << bits) - 1;
|
||||
}
|
||||
return (1LL << (bits - 1)) - 1;
|
||||
}
|
||||
static_assert(BITS_MAX<int16_t, 10>() == 511, "BITS_MAX<int16_t>(10) != 511");
|
||||
static_assert(BITS_MAX<uint16_t, 10>() == 1023, "BITS_MAX<uint16_t>(10) != 1023");
|
||||
|
||||
//Minimum value for a given number of bits, result will be signed/unsigned depending on type
|
||||
template<typename T, uint8_t bits>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T BITS_MIN()
|
||||
{
|
||||
static_assert(bits <= NUM_BITS<T>, "BITS_MIN: Return type cannot represet minimum bit value");
|
||||
static_assert(bits <= 64, "BITS_MIN: Bits exceed 64");
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return static_cast<T>(-(1LL << (bits - 1)));
|
||||
}
|
||||
static_assert(BITS_MIN<int16_t, 10>() == -512, "BITS_MIN<int16_t>(10) != -512");
|
||||
static_assert(BITS_MIN<uint16_t, 10>() == 0, "BITS_MIN<uint16_t>(10) != 0");
|
||||
|
||||
template <typename T>
|
||||
static inline T invert(T value)
|
||||
{
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
return Range::MAX<T> - value;
|
||||
}
|
||||
return (value == Range::MIN<T>) ? Range::MAX<T> : -value;
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
requires std::is_integral_v<To> && std::is_integral_v<From>
|
||||
static inline To clamp(From value)
|
||||
{
|
||||
return static_cast<To>((value < Range::MIN<To>)
|
||||
? Range::MIN<To>
|
||||
: (value > Range::MAX<To>)
|
||||
? Range::MAX<To>
|
||||
: value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T clamp(T value, T min, T max)
|
||||
{
|
||||
return (value < min) ? min : (value > max) ? max : value;
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
static inline To clamp(From value, To min_to, To max_to)
|
||||
{
|
||||
return (value < min_to) ? min_to : (value > max_to) ? max_to : static_cast<To>(value);
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
requires std::is_integral_v<To> && std::is_integral_v<From>
|
||||
static constexpr To scale(From value, From min_from, From max_from, To min_to, To max_to)
|
||||
{
|
||||
return static_cast<To>(
|
||||
(static_cast<int64_t>(value - min_from) * (max_to - min_to) / (max_from - min_from)) + min_to);
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
static inline To scale(From value)
|
||||
{
|
||||
return scale<To>(value, Range::MIN<From>, Range::MAX<From>, Range::MIN<To>, Range::MAX<To>);
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
static inline To scale(From value, To min_to, To max_to)
|
||||
{
|
||||
return scale<To>(value, Range::MIN<From>, Range::MAX<From>, min_to, max_to);
|
||||
}
|
||||
|
||||
//Cast value to signed/unsigned for accurate scaling
|
||||
template <typename To, uint8_t bits, typename From>
|
||||
static inline To scale_from_bits(From value)
|
||||
{
|
||||
return scale<To>(
|
||||
Range::clamp(value, BITS_MIN<From, bits>(), BITS_MAX<From, bits>()),
|
||||
BITS_MIN<From, bits>(),
|
||||
BITS_MAX<From, bits>(),
|
||||
Range::MIN<To>,
|
||||
Range::MAX<To>);
|
||||
}
|
||||
|
||||
//Cast value to signed/unsigned for accurate scaling
|
||||
template <typename To, uint8_t bits, typename From>
|
||||
static inline To scale_to_bits(From value)
|
||||
{
|
||||
return scale<To>(
|
||||
Range::clamp(value, BITS_MIN<From, bits>(), BITS_MAX<From, bits>()),
|
||||
Range::MIN<From>,
|
||||
Range::MAX<From>,
|
||||
BITS_MIN<To, bits>(),
|
||||
BITS_MAX<To, bits>());
|
||||
}
|
||||
|
||||
} // namespace Range
|
||||
|
||||
namespace Scale //Scale and invert values
|
||||
{
|
||||
static inline uint8_t int16_to_uint8(int16_t value)
|
||||
{
|
||||
uint16_t shifted_value = static_cast<uint16_t>(value + Range::MID<uint16_t>);
|
||||
return static_cast<uint8_t>(shifted_value >> 8);
|
||||
}
|
||||
static inline uint16_t int16_to_uint16(int16_t value)
|
||||
{
|
||||
return static_cast<uint16_t>(value + Range::MID<uint16_t>);
|
||||
}
|
||||
static inline int8_t int16_to_int8(int16_t value)
|
||||
{
|
||||
return static_cast<int8_t>((value + Range::MID<uint16_t>) >> 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 - Range::MID<uint16_t>);
|
||||
}
|
||||
static inline int8_t uint16_to_int8(uint16_t value)
|
||||
{
|
||||
return static_cast<int8_t>((value >> 8) - Range::MID<uint8_t>);
|
||||
}
|
||||
|
||||
static inline int16_t uint8_to_int16(uint8_t value)
|
||||
{
|
||||
return static_cast<int16_t>((static_cast<int32_t>(value) << 8) - Range::MID<uint16_t>);
|
||||
}
|
||||
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 - Range::MID<uint8_t>);
|
||||
}
|
||||
|
||||
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 + Range::MID<uint8_t>) << 8);
|
||||
}
|
||||
static inline uint8_t int8_to_uint8(int8_t value)
|
||||
{
|
||||
return static_cast<uint8_t>(value + Range::MID<uint8_t>);
|
||||
}
|
||||
|
||||
} // namespace Scale
|
||||
|
||||
#endif // _RANGE_H_
|
||||
106
Firmware/RP2040/src/Gamepad/fix16ext.h
Normal file
106
Firmware/RP2040/src/Gamepad/fix16ext.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef FIX16_EXT_H
|
||||
#define FIX16_EXT_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "libfixmath/fix16.hpp"
|
||||
|
||||
namespace fix16 {
|
||||
|
||||
inline Fix16 abs(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_abs(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 rad2deg(Fix16 rad)
|
||||
{
|
||||
return Fix16(fix16_rad_to_deg(rad.value));
|
||||
}
|
||||
|
||||
inline Fix16 deg2rad(Fix16 deg)
|
||||
{
|
||||
return Fix16(fix16_deg_to_rad(deg.value));
|
||||
}
|
||||
|
||||
inline Fix16 atan(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_atan(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 atan2(Fix16 y, Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_atan2(y.value, x.value));
|
||||
}
|
||||
|
||||
inline Fix16 tan(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_tan(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 cos(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_cos(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 sin(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_sin(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 sqrt(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_sqrt(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 sq(Fix16 x)
|
||||
{
|
||||
return Fix16(fix16_sq(x.value));
|
||||
}
|
||||
|
||||
inline Fix16 clamp(Fix16 x, Fix16 min, Fix16 max)
|
||||
{
|
||||
return Fix16(fix16_clamp(x.value, min.value, max.value));
|
||||
}
|
||||
|
||||
inline Fix16 pow(Fix16 x, Fix16 y)
|
||||
{
|
||||
fix16_t& base = x.value;
|
||||
fix16_t& exponent = y.value;
|
||||
|
||||
if (exponent == F16(0.0))
|
||||
return Fix16(fix16_from_int(1));
|
||||
if (base == F16(0.0))
|
||||
return Fix16(fix16_from_int(0));
|
||||
|
||||
int32_t int_exp = fix16_to_int(exponent);
|
||||
|
||||
if (fix16_from_int(int_exp) == exponent)
|
||||
{
|
||||
fix16_t result = F16(1.0);
|
||||
fix16_t current_base = base;
|
||||
|
||||
if (int_exp < 0)
|
||||
{
|
||||
current_base = fix16_div(F16(1.0), base);
|
||||
int_exp = -int_exp;
|
||||
}
|
||||
|
||||
while (int_exp)
|
||||
{
|
||||
if (int_exp & 1)
|
||||
{
|
||||
result = fix16_mul(result, current_base);
|
||||
}
|
||||
current_base = fix16_mul(current_base, current_base);
|
||||
int_exp >>= 1;
|
||||
}
|
||||
|
||||
return Fix16(result);
|
||||
}
|
||||
|
||||
return Fix16(fix16_exp(fix16_mul(exponent, fix16_log(base))));
|
||||
}
|
||||
|
||||
} // namespace fix16
|
||||
|
||||
#endif // FIX16_EXT_H
|
||||
201
Firmware/RP2040/src/OGXMini/Board/ESP32_Bluepad32_I2C.cpp
Normal file
201
Firmware/RP2040/src/OGXMini/Board/ESP32_Bluepad32_I2C.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
#include "Board/Config.h"
|
||||
#include "OGXMini/Board/ESP32_Bluepad32_I2C.h"
|
||||
#if (OGXM_BOARD == ESP32_BLUEPAD32_I2C)
|
||||
|
||||
#include <cstring>
|
||||
#include <pico/multicore.h>
|
||||
#include <pico/i2c_slave.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/i2c.h>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "bsp/board_api.h"
|
||||
|
||||
#include "USBDevice/DeviceManager.h"
|
||||
#include "UserSettings/UserSettings.h"
|
||||
#include "Board/board_api.h"
|
||||
#include "Board/esp32_api.h"
|
||||
#include "Gamepad/Gamepad.h"
|
||||
#include "TaskQueue/TaskQueue.h"
|
||||
|
||||
enum class PacketID : uint8_t {
|
||||
UNKNOWN = 0,
|
||||
SET_PAD,
|
||||
GET_PAD,
|
||||
SET_DRIVER
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PacketIn {
|
||||
uint8_t packet_len{sizeof(PacketIn)};
|
||||
PacketID packet_id{PacketID::SET_PAD};
|
||||
uint8_t index{0};
|
||||
DeviceDriverType device_type{DeviceDriverType::NONE};
|
||||
Gamepad::PadIn pad_in{Gamepad::PadIn()};
|
||||
uint8_t reserved[5]{0};
|
||||
};
|
||||
static_assert(sizeof(PacketIn) == 32, "i2c_driver_esp::PacketIn size mismatch");
|
||||
|
||||
struct PacketOut {
|
||||
uint8_t packet_len{sizeof(PacketOut)};
|
||||
PacketID packet_id{PacketID::GET_PAD};
|
||||
uint8_t index{0};
|
||||
Gamepad::PadOut pad_out{Gamepad::PadOut()};
|
||||
uint8_t reserved[3]{0};
|
||||
};
|
||||
static_assert(sizeof(PacketOut) == 8, "i2c_driver_esp::PacketOut size mismatch");
|
||||
#pragma pack(pop)
|
||||
|
||||
constexpr size_t MAX_BUFFER_SIZE = std::max(sizeof(PacketOut), sizeof(PacketIn));
|
||||
constexpr uint8_t I2C_ADDR = 0x01;
|
||||
|
||||
static Gamepad _gamepads[MAX_GAMEPADS];
|
||||
static bool _uart_bridge_mode = false;
|
||||
|
||||
static inline void slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event) {
|
||||
static size_t count = 0;
|
||||
static PacketIn packet_in;
|
||||
static PacketOut packet_out;
|
||||
static DeviceDriverType current_device_type =
|
||||
UserSettings::get_instance().get_current_driver();
|
||||
|
||||
switch (event) {
|
||||
case I2C_SLAVE_RECEIVE:
|
||||
if (count < sizeof(PacketIn)) {
|
||||
reinterpret_cast<uint8_t*>(&packet_in)[count] = i2c_read_byte_raw(i2c);
|
||||
++count;
|
||||
}
|
||||
break;
|
||||
case I2C_SLAVE_FINISH:
|
||||
if (count == 0) {
|
||||
break;
|
||||
}
|
||||
switch (packet_in.packet_id) {
|
||||
case PacketID::SET_PAD:
|
||||
if (packet_in.index < MAX_GAMEPADS) {
|
||||
_gamepads[packet_in.index].set_pad_in(packet_in.pad_in);
|
||||
}
|
||||
break;
|
||||
case PacketID::SET_DRIVER:
|
||||
if (packet_in.device_type != DeviceDriverType::NONE &&
|
||||
packet_in.device_type != current_device_type) {
|
||||
OGXM_LOG("I2C: Driver change detected.\n");
|
||||
//Any writes to flash should be done on Core0
|
||||
TaskQueue::Core0::queue_delayed_task(
|
||||
TaskQueue::Core0::get_new_task_id(), 1000, false,
|
||||
[new_device_type = packet_in.device_type] {
|
||||
UserSettings::get_instance().store_driver_type(new_device_type);
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
count = 0;
|
||||
break;
|
||||
case I2C_SLAVE_REQUEST:
|
||||
if (packet_in.index < MAX_GAMEPADS) {
|
||||
packet_out.index = packet_in.index;
|
||||
packet_out.pad_out = _gamepads[packet_in.index].get_pad_out();
|
||||
}
|
||||
i2c_write_raw_blocking( i2c,
|
||||
reinterpret_cast<const uint8_t*>(&packet_out),
|
||||
packet_out.packet_len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void core1_task() {
|
||||
i2c_init(I2C_PORT, I2C_BAUDRATE);
|
||||
|
||||
gpio_init(I2C_SDA_PIN);
|
||||
gpio_init(I2C_SCL_PIN);
|
||||
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(I2C_SDA_PIN);
|
||||
gpio_pull_up(I2C_SCL_PIN);
|
||||
|
||||
i2c_slave_init(I2C_PORT, I2C_ADDR, &slave_handler);
|
||||
|
||||
OGXM_LOG("I2C Driver initialized\n");
|
||||
|
||||
while (true) {
|
||||
tight_loop_contents();
|
||||
}
|
||||
}
|
||||
|
||||
void run_uart_bridge() {
|
||||
esp32_api::enter_programming_mode();
|
||||
|
||||
OGXM_LOG("Entering UART Bridge mode\n");
|
||||
|
||||
//Runs UART Bridge task, doesn't return unless programming is complete
|
||||
DeviceManager::get_instance().get_driver()->process(0, _gamepads[0]);
|
||||
|
||||
OGXM_LOG("Exiting UART Bridge mode\n");
|
||||
|
||||
board_api::usb::disconnect_all();
|
||||
UserSettings::get_instance().write_datetime();
|
||||
board_api::reboot();
|
||||
}
|
||||
|
||||
bool update_needed(UserSettings& user_settings) {
|
||||
#if defined(OGXM_RETAIL)
|
||||
return !user_settings.verify_datetime();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void esp32_bp32_i2c::initialize() {
|
||||
board_api::init_board();
|
||||
esp32_api::init();
|
||||
|
||||
UserSettings& user_settings = UserSettings::get_instance();
|
||||
user_settings.initialize_flash();
|
||||
|
||||
//MODE_SEL_PIN is used to determine if UART bridge should be run
|
||||
_uart_bridge_mode =
|
||||
(esp32_api::uart_bridge_mode() || update_needed(user_settings));
|
||||
|
||||
DeviceDriverType driver_type =
|
||||
_uart_bridge_mode ?
|
||||
DeviceDriverType::UART_BRIDGE : user_settings.get_current_driver();
|
||||
|
||||
DeviceManager::get_instance().initialize_driver(driver_type, _gamepads);
|
||||
}
|
||||
|
||||
void esp32_bp32_i2c::run() {
|
||||
if (_uart_bridge_mode) {
|
||||
run_uart_bridge();
|
||||
return;
|
||||
}
|
||||
|
||||
multicore_reset_core1();
|
||||
multicore_launch_core1(core1_task);
|
||||
|
||||
esp32_api::reset();
|
||||
|
||||
DeviceDriver* device_driver = DeviceManager::get_instance().get_driver();
|
||||
|
||||
tud_init(BOARD_TUD_RHPORT);
|
||||
|
||||
while (true) {
|
||||
TaskQueue::Core0::process_tasks();
|
||||
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i) {
|
||||
device_driver->process(i, _gamepads[i]);
|
||||
tud_task();
|
||||
}
|
||||
sleep_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
// #else // OGXM_BOARD == ESP32_BLUEPAD32_I2C
|
||||
|
||||
// void esp32_bp32_i2c::initialize() {}
|
||||
// void esp32_bp32_i2c::run() {}
|
||||
|
||||
#endif // OGXM_BOARD == ESP32_BLUEPAD32_I2C
|
||||
8
Firmware/RP2040/src/OGXMini/Board/ESP32_Bluepad32_I2C.h
Normal file
8
Firmware/RP2040/src/OGXMini/Board/ESP32_Bluepad32_I2C.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace esp32_bp32_i2c {
|
||||
void initialize();
|
||||
void run();
|
||||
} // namespace esp32_bp32_i2c
|
||||
200
Firmware/RP2040/src/OGXMini/Board/ESP32_Blueretro_I2C.cpp
Normal file
200
Firmware/RP2040/src/OGXMini/Board/ESP32_Blueretro_I2C.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
#include "Board/Config.h"
|
||||
#include "OGXMini/Board/ESP32_Blueretro_I2C.h"
|
||||
#if (OGXM_BOARD == ESP32_BLUERETRO_I2C)
|
||||
|
||||
#include <pico/multicore.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/i2c.h>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "bsp/board_api.h"
|
||||
|
||||
#include "UserSettings/UserSettings.h"
|
||||
#include "USBDevice/DeviceManager.h"
|
||||
#include "Board/board_api.h"
|
||||
#include "Board/esp32_api.h"
|
||||
#include "Gamepad/Gamepad.h"
|
||||
#include "TaskQueue/TaskQueue.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PacketIn {
|
||||
uint8_t len{sizeof(PacketIn)};
|
||||
uint8_t index{0};
|
||||
DeviceDriverType device_type{DeviceDriverType::NONE};
|
||||
uint8_t gp_data[13]{0};
|
||||
};
|
||||
static_assert(sizeof(PacketIn) == 16, "i2c_driver_esp::PacketIn size mismatch");
|
||||
|
||||
struct PacketOut {
|
||||
uint8_t len{sizeof(PacketOut)};
|
||||
uint8_t rumble_l{0};
|
||||
uint8_t rumble_r{0};
|
||||
uint8_t reserved[5]{0};
|
||||
};
|
||||
static_assert(sizeof(PacketOut) == 8, "i2c_driver_esp::PacketOut size mismatch");
|
||||
#pragma pack(pop)
|
||||
|
||||
constexpr uint8_t SLAVE_ADDR = 0x50;
|
||||
constexpr uint32_t FEEDBACK_DELAY_MS = 250;
|
||||
|
||||
static Gamepad _gamepads[MAX_GAMEPADS];
|
||||
static bool _uart_bridge_mode = false;
|
||||
|
||||
static void core1_task() {
|
||||
i2c_init(I2C_PORT, I2C_BAUDRATE);
|
||||
|
||||
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(I2C_SCL_PIN);
|
||||
gpio_pull_up(I2C_SDA_PIN);
|
||||
|
||||
bool slave_ready = false;
|
||||
PacketIn packet_in;
|
||||
PacketOut packet_out;
|
||||
Gamepad::PadIn pad_in;
|
||||
Gamepad& gamepad = _gamepads[0];
|
||||
uint32_t tid = TaskQueue::Core1::get_new_task_id();
|
||||
|
||||
sleep_ms(500); // Wait for ESP32 to start
|
||||
|
||||
TaskQueue::Core1::queue_delayed_task(tid, FEEDBACK_DELAY_MS, true,
|
||||
[&packet_out, &gamepad, &slave_ready] {
|
||||
if (!slave_ready) { // Check if slave present
|
||||
uint8_t addr = SLAVE_ADDR;
|
||||
int result = i2c_read_blocking(I2C_PORT, SLAVE_ADDR, &addr, 1, false);
|
||||
slave_ready = (result == 1);
|
||||
|
||||
} else { // Update rumble
|
||||
Gamepad::PadOut pad_out = gamepad.get_pad_out();
|
||||
packet_out.rumble_l = pad_out.rumble_l;
|
||||
packet_out.rumble_r = pad_out.rumble_r;
|
||||
int result = i2c_write_blocking(I2C_PORT, SLAVE_ADDR,
|
||||
reinterpret_cast<const uint8_t*>(&packet_out),
|
||||
sizeof(PacketOut), false);
|
||||
|
||||
if (result != sizeof(PacketOut)) {
|
||||
OGXM_LOG("I2C write failed\n");
|
||||
} else {
|
||||
OGXM_LOG("I2C sent rumble, L: %02X, R: %02X\n",
|
||||
packet_out.rumble_l, packet_out.rumble_r);
|
||||
sleep_ms(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
OGXM_LOG("I2C Driver initialized\n");
|
||||
|
||||
//Wait for slave to be detected
|
||||
while (!slave_ready) {
|
||||
TaskQueue::Core1::process_tasks();
|
||||
sleep_ms(100);
|
||||
}
|
||||
|
||||
OGXM_LOG("I2C Slave ready\n");
|
||||
|
||||
while (true) {
|
||||
TaskQueue::Core1::process_tasks();
|
||||
int result = i2c_read_blocking( I2C_PORT, SLAVE_ADDR,
|
||||
reinterpret_cast<uint8_t*>(&packet_in),
|
||||
sizeof(PacketIn), false);
|
||||
|
||||
if (result == sizeof(PacketIn)) {
|
||||
std::memcpy(reinterpret_cast<uint8_t*>(&pad_in),
|
||||
packet_in.gp_data,
|
||||
sizeof(packet_in.gp_data));
|
||||
gamepad.set_pad_in(pad_in);
|
||||
|
||||
} else {
|
||||
OGXM_LOG("I2C read failed\n");
|
||||
return;
|
||||
}
|
||||
sleep_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
void set_gp_check_timer(uint32_t task_id) {
|
||||
UserSettings& user_settings = UserSettings::get_instance();
|
||||
|
||||
TaskQueue::Core0::queue_delayed_task(task_id, UserSettings::GP_CHECK_DELAY_MS, true,
|
||||
[&user_settings] {
|
||||
//Check gamepad inputs for button combo to change usb device driver
|
||||
if (user_settings.check_for_driver_change(_gamepads[0])) {
|
||||
OGXM_LOG("Driver change detected, storing new driver.\n");
|
||||
//This will store the new mode and reboot the pico
|
||||
user_settings.store_driver_type(user_settings.get_current_driver());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void run_uart_bridge() {
|
||||
esp32_api::enter_programming_mode();
|
||||
|
||||
OGXM_LOG("Entering UART Bridge mode\n");
|
||||
|
||||
//Runs UART Bridge task, doesn't return unless programming is complete
|
||||
DeviceManager::get_instance().get_driver()->process(0, _gamepads[0]);
|
||||
|
||||
OGXM_LOG("Exiting UART Bridge mode\n");
|
||||
|
||||
board_api::usb::disconnect_all();
|
||||
UserSettings::get_instance().write_datetime();
|
||||
board_api::reboot();
|
||||
}
|
||||
|
||||
bool update_needed(UserSettings& user_settings) {
|
||||
#if defined(OGXM_RETAIL)
|
||||
return !user_settings.verify_datetime();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void esp32_br_i2c::initialize() {
|
||||
board_api::init_board();
|
||||
esp32_api::init();
|
||||
|
||||
UserSettings& user_settings = UserSettings::get_instance();
|
||||
user_settings.initialize_flash();
|
||||
|
||||
//MODE_SEL_PIN is used to determine if UART bridge should be run
|
||||
_uart_bridge_mode =
|
||||
(esp32_api::uart_bridge_mode() || update_needed(user_settings));
|
||||
|
||||
DeviceDriverType driver_type =
|
||||
_uart_bridge_mode ?
|
||||
DeviceDriverType::UART_BRIDGE : user_settings.get_current_driver();
|
||||
|
||||
DeviceManager::get_instance().initialize_driver(driver_type, _gamepads);
|
||||
}
|
||||
|
||||
void esp32_br_i2c::run() {
|
||||
if (_uart_bridge_mode) {
|
||||
run_uart_bridge();
|
||||
return;
|
||||
}
|
||||
|
||||
esp32_api::reset();
|
||||
|
||||
multicore_reset_core1();
|
||||
multicore_launch_core1(core1_task);
|
||||
|
||||
uint32_t tid_gp_check = TaskQueue::Core0::get_new_task_id();
|
||||
set_gp_check_timer(tid_gp_check);
|
||||
|
||||
DeviceDriver* device_driver = DeviceManager::get_instance().get_driver();
|
||||
|
||||
tud_init(BOARD_TUD_RHPORT);
|
||||
|
||||
while (true) {
|
||||
TaskQueue::Core0::process_tasks();
|
||||
device_driver->process(0, _gamepads[0]);
|
||||
tud_task();
|
||||
sleep_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
// #else // OGXM_BOARD == ESP32_BLUERETRO_I2C
|
||||
|
||||
// void esp32_br_i2c::initialize() {}
|
||||
// void esp32_br_i2c::run() {}
|
||||
|
||||
#endif // OGXM_BOARD == ESP32_BLUERETRO_I2C
|
||||
8
Firmware/RP2040/src/OGXMini/Board/ESP32_Blueretro_I2C.h
Normal file
8
Firmware/RP2040/src/OGXMini/Board/ESP32_Blueretro_I2C.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace esp32_br_i2c {
|
||||
void initialize();
|
||||
void run();
|
||||
} // namespace esp32_br_i2c
|
||||
470
Firmware/RP2040/src/OGXMini/Board/Four_Channel_I2C.cpp
Normal file
470
Firmware/RP2040/src/OGXMini/Board/Four_Channel_I2C.cpp
Normal file
@@ -0,0 +1,470 @@
|
||||
#include "Board/Config.h"
|
||||
#include "OGXMini/Board/Four_Channel_I2C.h"
|
||||
#if ((OGXM_BOARD == INTERNAL_4CH_I2C) || (OGXM_BOARD == EXTERNAL_4CH_I2C))
|
||||
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <pico/multicore.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/i2c.h>
|
||||
#include <pico/i2c_slave.h>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "bsp/board_api.h"
|
||||
#include "pio_usb.h"
|
||||
|
||||
#include "USBDevice/DeviceManager.h"
|
||||
#include "USBHost/HostManager.h"
|
||||
#include "Board/board_api.h"
|
||||
#include "Board/ogxm_log.h"
|
||||
#include "UserSettings/UserSettings.h"
|
||||
#include "Gamepad/Gamepad.h"
|
||||
#include "TaskQueue/TaskQueue.h"
|
||||
|
||||
constexpr uint32_t FEEDBACK_DELAY_MS = 250;
|
||||
|
||||
Gamepad _gamepads[MAX_GAMEPADS];
|
||||
|
||||
namespace I2C {
|
||||
enum class Role {
|
||||
SLAVE = 0,
|
||||
MASTER
|
||||
};
|
||||
enum class PacketID : uint8_t {
|
||||
UNKNOWN = 0,
|
||||
PAD,
|
||||
COMMAND
|
||||
};
|
||||
enum class Command : uint8_t {
|
||||
UNKNOWN = 0,
|
||||
STATUS,
|
||||
DISABLE
|
||||
};
|
||||
enum class Status : uint8_t {
|
||||
UNKNOWN = 0,
|
||||
NC,
|
||||
ERROR,
|
||||
OK,
|
||||
READY,
|
||||
NOT_READY
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PacketIn {
|
||||
uint8_t packet_len{sizeof(PacketIn)};
|
||||
PacketID packet_id{PacketID::PAD};
|
||||
Gamepad::PadIn pad_in{Gamepad::PadIn()};
|
||||
Gamepad::ChatpadIn chatpad_in{0};
|
||||
uint8_t reserved[4]{0};
|
||||
};
|
||||
static_assert(sizeof(PacketIn) == 32, "I2CDriver::PacketIn is misaligned");
|
||||
|
||||
struct PacketOut {
|
||||
uint8_t packet_len{sizeof(PacketOut)};
|
||||
PacketID packet_id{PacketID::PAD};
|
||||
Gamepad::PadOut pad_out{Gamepad::PadOut()};
|
||||
uint8_t reserved[4]{0};
|
||||
};
|
||||
static_assert(sizeof(PacketOut) == 8, "I2CDriver::PacketOut is misaligned");
|
||||
|
||||
struct PacketCMD {
|
||||
uint8_t packet_len{sizeof(PacketCMD)};
|
||||
PacketID packet_id{PacketID::COMMAND};
|
||||
Command command{Command::UNKNOWN};
|
||||
Status status{Status::UNKNOWN};
|
||||
uint8_t reserved[4]{0};
|
||||
};
|
||||
static_assert(sizeof(PacketCMD) == 8, "I2CDriver::PacketCMD is misaligned");
|
||||
#pragma pack(pop)
|
||||
|
||||
constexpr size_t MAX_PACKET_SIZE = sizeof(PacketIn);
|
||||
|
||||
static Role _i2c_role = Role::SLAVE;
|
||||
|
||||
namespace Slave {
|
||||
static inline PacketID get_packet_id(uint8_t* buffer_in) {
|
||||
switch (static_cast<PacketID>(buffer_in[1])) {
|
||||
case PacketID::PAD:
|
||||
if (buffer_in[0] == sizeof(PacketIn)) {
|
||||
return PacketID::PAD;
|
||||
}
|
||||
break;
|
||||
case PacketID::COMMAND:
|
||||
if (buffer_in[0] == sizeof(PacketCMD)) {
|
||||
return PacketID::COMMAND;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return PacketID::UNKNOWN;
|
||||
}
|
||||
|
||||
static void slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event) {
|
||||
static size_t count = 0;
|
||||
static bool enabled = false;
|
||||
static uint8_t buffer_in[MAX_PACKET_SIZE];
|
||||
static uint8_t buffer_out[MAX_PACKET_SIZE];
|
||||
|
||||
PacketIn *packet_in_p = reinterpret_cast<PacketIn*>(buffer_in);
|
||||
PacketOut *packet_out_p = reinterpret_cast<PacketOut*>(buffer_out);
|
||||
PacketCMD *packet_cmd_in_p = reinterpret_cast<PacketCMD*>(buffer_in);
|
||||
PacketCMD *packet_cmd_out_p = reinterpret_cast<PacketCMD*>(buffer_out);
|
||||
|
||||
switch (event) {
|
||||
case I2C_SLAVE_RECEIVE: // master has written
|
||||
if (count < MAX_PACKET_SIZE) {
|
||||
buffer_in[count] = i2c_read_byte_raw(i2c);
|
||||
++count;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_FINISH:
|
||||
// Each master write has an ID indicating the type of data to send back on the next read request
|
||||
// Every write has an associated read
|
||||
switch (get_packet_id(buffer_in)) {
|
||||
case PacketID::PAD:
|
||||
// instance_->packet_in_ = *packet_in_p;
|
||||
// *packet_out_p = instance_->packet_out_;
|
||||
// instance_->new_pad_in_.store(true);
|
||||
_gamepads[0].set_pad_in(packet_in_p->pad_in);
|
||||
if (_gamepads[0].new_pad_out()) {
|
||||
packet_out_p->pad_out = _gamepads[0].get_pad_out();
|
||||
}
|
||||
break;
|
||||
|
||||
case PacketID::COMMAND:
|
||||
switch (packet_cmd_in_p->command) {
|
||||
case Command::DISABLE:
|
||||
packet_cmd_out_p->packet_len = sizeof(PacketCMD);
|
||||
packet_cmd_out_p->packet_id = PacketID::COMMAND;
|
||||
packet_cmd_out_p->command = Command::DISABLE;
|
||||
packet_cmd_out_p->status = Status::OK;
|
||||
|
||||
if (!tuh_mounted(BOARD_TUH_RHPORT)) {
|
||||
four_ch_i2c::host_mounted(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case Command::STATUS:
|
||||
packet_cmd_out_p->packet_len = sizeof(PacketCMD);
|
||||
packet_cmd_out_p->packet_id = PacketID::COMMAND;
|
||||
packet_cmd_out_p->command = Command::STATUS;
|
||||
packet_cmd_out_p->status =
|
||||
tuh_mounted(BOARD_TUH_RHPORT) ? Status::NOT_READY : Status::READY;
|
||||
|
||||
if (!tuh_mounted(BOARD_TUH_RHPORT) && !enabled) {
|
||||
enabled = true;
|
||||
four_ch_i2c::host_mounted(true);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
count = 0;
|
||||
std::memset(buffer_in, 0, sizeof(buffer_in));
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_REQUEST:
|
||||
i2c_write_raw_blocking(i2c, buffer_out, buffer_out[0]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace Slave
|
||||
|
||||
namespace Master {
|
||||
struct Slave {
|
||||
uint8_t address{0xFF};
|
||||
Status status{Status::NC};
|
||||
bool enabled{false};
|
||||
};
|
||||
|
||||
static constexpr size_t NUM_SLAVES = MAX_GAMEPADS - 1;
|
||||
static_assert(NUM_SLAVES > 0, "I2CMaster::NUM_SLAVES must be greater than 0 to use I2C");
|
||||
|
||||
std::array<Slave, NUM_SLAVES> _slaves;
|
||||
|
||||
static inline bool read_blocking(uint8_t address, void* buffer, size_t len) {
|
||||
return (i2c_read_blocking( I2C_PORT, address, reinterpret_cast<uint8_t*>(buffer),
|
||||
len, false) == static_cast<int>(len));
|
||||
}
|
||||
|
||||
static inline bool write_blocking(uint8_t address, void* buffer, size_t len) {
|
||||
return (i2c_write_blocking( I2C_PORT, address, reinterpret_cast<uint8_t*>(buffer),
|
||||
len, false) == static_cast<int>(len));
|
||||
}
|
||||
|
||||
static inline bool slave_detected(uint8_t address) {
|
||||
uint8_t dummy = 0;
|
||||
int result = i2c_write_timeout_us(I2C_PORT, address, &dummy, 0, false, 1000);
|
||||
return (result >= 0);
|
||||
}
|
||||
|
||||
static void notify_disable(uint8_t address) {
|
||||
if (!slave_detected(address)) {
|
||||
return;
|
||||
}
|
||||
int retries = 10;
|
||||
|
||||
while (retries--) {
|
||||
PacketCMD packet_cmd;
|
||||
packet_cmd.packet_id = PacketID::COMMAND;
|
||||
packet_cmd.command = Command::DISABLE;
|
||||
|
||||
if (write_blocking(address, &packet_cmd, sizeof(PacketCMD))) {
|
||||
if (read_blocking(address, &packet_cmd, sizeof(PacketCMD))) {
|
||||
if (packet_cmd.status == Status::OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sleep_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void process() {
|
||||
for (uint8_t i = 0; i < NUM_SLAVES; ++i) {
|
||||
Slave& slave = _slaves[i];
|
||||
|
||||
if (!slave.enabled || !slave_detected(slave.address)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PacketCMD packet_cmd;
|
||||
packet_cmd.packet_id = PacketID::COMMAND;
|
||||
packet_cmd.command = Command::STATUS;
|
||||
|
||||
if (!write_blocking(slave.address, &packet_cmd, sizeof(PacketCMD)) ||
|
||||
!read_blocking(slave.address, &packet_cmd, sizeof(PacketCMD))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (packet_cmd.status == Status::READY) {
|
||||
Gamepad& gamepad = _gamepads[i + 1];
|
||||
PacketIn packet_in;
|
||||
packet_in.pad_in = gamepad.get_pad_in();
|
||||
packet_in.chatpad_in = gamepad.get_chatpad_in();
|
||||
|
||||
if (write_blocking(slave.address, &packet_in, sizeof(PacketIn))) {
|
||||
PacketOut packet_out;
|
||||
if (read_blocking(slave.address, &packet_out, sizeof(PacketOut))) {
|
||||
gamepad.set_pad_out(packet_out.pad_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
sleep_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void xbox360w_connect(bool connected, uint8_t idx) {
|
||||
if (idx < 1 || idx >= MAX_GAMEPADS) {
|
||||
return;
|
||||
}
|
||||
// This function can be called from core1
|
||||
// so queue on core0 (i2c thread)
|
||||
TaskQueue::Core0::queue_task(
|
||||
[&slave = _slaves[idx - 1], connected]() {
|
||||
slave.enabled = connected;
|
||||
if (!connected) {
|
||||
notify_disable(slave.address);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void tuh_connect(bool connected, HostDriverType host_type) {
|
||||
if (host_type != HostDriverType::XBOX360W) {
|
||||
return;
|
||||
}
|
||||
if (!connected) {
|
||||
//Called from core1 so queue on core0
|
||||
TaskQueue::Core0::queue_task(
|
||||
[]() {
|
||||
for (auto& slave : _slaves) {
|
||||
slave.enabled = false;
|
||||
notify_disable(slave.address);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} // namespace Master
|
||||
|
||||
Role role() {
|
||||
return _i2c_role;
|
||||
}
|
||||
|
||||
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) && gpio_get(SLAVE_ADDR_PIN_2)) {
|
||||
return 0x00;
|
||||
}
|
||||
else if (gpio_get(SLAVE_ADDR_PIN_1) && !gpio_get(SLAVE_ADDR_PIN_2)) {
|
||||
return 0x01;
|
||||
}
|
||||
else if (!gpio_get(SLAVE_ADDR_PIN_1) && gpio_get(SLAVE_ADDR_PIN_2)) {
|
||||
return 0x02;
|
||||
}
|
||||
else if (!gpio_get(SLAVE_ADDR_PIN_1) && !gpio_get(SLAVE_ADDR_PIN_2)) {
|
||||
return 0x03;
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
uint8_t i2c_address = get_address();
|
||||
_i2c_role = (i2c_address == 0xFF) ? Role::MASTER : _i2c_role = Role::SLAVE;
|
||||
|
||||
i2c_init(I2C_PORT, I2C_BAUDRATE);
|
||||
|
||||
gpio_init(I2C_SDA_PIN);
|
||||
gpio_init(I2C_SCL_PIN);
|
||||
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(I2C_SDA_PIN);
|
||||
gpio_pull_up(I2C_SCL_PIN);
|
||||
|
||||
if (_i2c_role == Role::SLAVE) {
|
||||
i2c_slave_init(I2C_PORT, i2c_address, &Slave::slave_handler);
|
||||
}
|
||||
}
|
||||
} // namespace I2C
|
||||
|
||||
void core1_task() {
|
||||
HostManager& host_manager = HostManager::get_instance();
|
||||
host_manager.initialize(_gamepads);
|
||||
|
||||
//Pico-PIO-USB will not reliably detect a hot plug on some boards,
|
||||
//so monitor pins and init host stack after connection
|
||||
while(!board_api::usb::host_connected()) {
|
||||
sleep_ms(100);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
uint32_t tid_feedback = TaskQueue::Core1::get_new_task_id();
|
||||
TaskQueue::Core1::queue_delayed_task(tid_feedback, FEEDBACK_DELAY_MS, true,
|
||||
[&host_manager] {
|
||||
host_manager.send_feedback();
|
||||
});
|
||||
|
||||
while (true) {
|
||||
TaskQueue::Core1::process_tasks();
|
||||
tuh_task();
|
||||
}
|
||||
}
|
||||
|
||||
void set_gp_check_timer(uint32_t task_id) {
|
||||
UserSettings& user_settings = UserSettings::get_instance();
|
||||
TaskQueue::Core0::queue_delayed_task(task_id, UserSettings::GP_CHECK_DELAY_MS, true,
|
||||
[&user_settings] {
|
||||
//Check gamepad inputs for button combo to change usb device driver
|
||||
if (user_settings.check_for_driver_change(_gamepads[0]))
|
||||
{
|
||||
//This will store the new mode and reboot the pico
|
||||
user_settings.store_driver_type(user_settings.get_current_driver());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void four_ch_i2c::wireless_connected(bool connected, uint8_t idx) {
|
||||
if (I2C::role() == I2C::Role::MASTER) {
|
||||
I2C::Master::xbox360w_connect(connected, idx);
|
||||
}
|
||||
}
|
||||
|
||||
void four_ch_i2c::host_mounted(bool mounted) {
|
||||
static std::atomic<bool> tud_is_inited = false;
|
||||
|
||||
board_api::set_led(mounted);
|
||||
|
||||
if (!mounted && tud_is_inited.load()) {
|
||||
TaskQueue::Core0::queue_task([]() {
|
||||
OGXM_LOG("Disconnecting USB and rebooting.\n");
|
||||
board_api::usb::disconnect_all();
|
||||
board_api::reboot();
|
||||
});
|
||||
} else if (!tud_is_inited.load() && mounted) {
|
||||
TaskQueue::Core0::queue_task([]() {
|
||||
OGXM_LOG("Initializing USB device stack.\n");
|
||||
tud_init(BOARD_TUD_RHPORT);
|
||||
tud_is_inited.store(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void four_ch_i2c::host_mounted_w_type(bool mounted, HostDriverType host_type) {
|
||||
host_mounted(mounted);
|
||||
if (I2C::role() == I2C::Role::MASTER) {
|
||||
I2C::Master::tuh_connect(mounted, host_type);
|
||||
}
|
||||
}
|
||||
|
||||
void four_ch_i2c::initialize() {
|
||||
UserSettings& user_settings = UserSettings::get_instance();
|
||||
user_settings.initialize_flash();
|
||||
|
||||
board_api::init_board();
|
||||
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i) {
|
||||
_gamepads[i].set_profile(user_settings.get_profile_by_index(i));
|
||||
}
|
||||
|
||||
DeviceManager::get_instance().initialize_driver(user_settings.get_current_driver(), _gamepads);
|
||||
}
|
||||
|
||||
void four_ch_i2c::run() {
|
||||
I2C::initialize();
|
||||
|
||||
multicore_reset_core1();
|
||||
multicore_launch_core1(core1_task);
|
||||
|
||||
//Wait for something to call tud_init
|
||||
while (!tud_inited()) {
|
||||
TaskQueue::Core0::process_tasks();
|
||||
sleep_ms(100);
|
||||
}
|
||||
|
||||
uint32_t tid_gp_check = TaskQueue::Core0::get_new_task_id();
|
||||
set_gp_check_timer(tid_gp_check);
|
||||
|
||||
DeviceDriver* device_driver = DeviceManager::get_instance().get_driver();
|
||||
|
||||
if (I2C::role() == I2C::Role::MASTER) {
|
||||
while (true) {
|
||||
TaskQueue::Core0::process_tasks();
|
||||
I2C::Master::process();
|
||||
device_driver->process(0, _gamepads[0]);
|
||||
tud_task();
|
||||
sleep_ms(1);
|
||||
}
|
||||
} else {
|
||||
while (true) {
|
||||
TaskQueue::Core0::process_tasks();
|
||||
device_driver->process(0, _gamepads[0]);
|
||||
tud_task();
|
||||
sleep_ms(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #else // OGXM_BOARD == INTERNAL_4CH_I2C || OGXM_BOARD == EXTERNAL_4CH_I2C
|
||||
|
||||
// void four_ch_i2c::initialize() {}
|
||||
// void four_ch_i2c::run() {}
|
||||
// void four_ch_i2c::host_mounted_w_type(bool mounted, HostDriverType host_type) {}
|
||||
// void four_ch_i2c::host_mounted(bool mounted) {}
|
||||
// void four_ch_i2c::wireless_connected(bool connected, uint8_t idx) {}
|
||||
|
||||
#endif // OGXM_BOARD == INTERNAL_4CH || OGXM_BOARD == EXTERNAL_4CH
|
||||
13
Firmware/RP2040/src/OGXMini/Board/Four_Channel_I2C.h
Normal file
13
Firmware/RP2040/src/OGXMini/Board/Four_Channel_I2C.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "USBHost/HostDriver/HostDriverTypes.h"
|
||||
|
||||
namespace four_ch_i2c {
|
||||
void initialize();
|
||||
void run();
|
||||
void host_mounted_w_type(bool mounted, HostDriverType host_type);
|
||||
void host_mounted(bool mounted);
|
||||
void wireless_connected(bool connected, uint8_t idx);
|
||||
} // namespace ext_4ch_i2c
|
||||
81
Firmware/RP2040/src/OGXMini/Board/PicoW.cpp
Normal file
81
Firmware/RP2040/src/OGXMini/Board/PicoW.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "Board/Config.h"
|
||||
#include "OGXMini/Board/PicoW.h"
|
||||
#if (OGXM_BOARD == PI_PICOW)
|
||||
|
||||
#include <hardware/clocks.h>
|
||||
#include <pico/multicore.h>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "bsp/board_api.h"
|
||||
|
||||
#include "USBDevice/DeviceManager.h"
|
||||
#include "UserSettings/UserSettings.h"
|
||||
#include "Board/board_api.h"
|
||||
#include "Bluepad32/Bluepad32.h"
|
||||
#include "BLEServer/BLEServer.h"
|
||||
#include "Gamepad/Gamepad.h"
|
||||
#include "TaskQueue/TaskQueue.h"
|
||||
|
||||
Gamepad _gamepads[MAX_GAMEPADS];
|
||||
|
||||
void core1_task() {
|
||||
board_api::init_bluetooth();
|
||||
board_api::set_led(true);
|
||||
BLEServer::init_server(_gamepads);
|
||||
bluepad32::run_task(_gamepads);
|
||||
}
|
||||
|
||||
void set_gp_check_timer(uint32_t task_id) {
|
||||
UserSettings& user_settings = UserSettings::get_instance();
|
||||
TaskQueue::Core0::queue_delayed_task(task_id, UserSettings::GP_CHECK_DELAY_MS, true,
|
||||
[&user_settings] {
|
||||
//Check gamepad inputs for button combo to change usb device driver
|
||||
if (user_settings.check_for_driver_change(_gamepads[0])) {
|
||||
//This will store the new mode and reboot the pico
|
||||
user_settings.store_driver_type(user_settings.get_current_driver());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void pico_w::initialize() {
|
||||
board_api::init_board();
|
||||
|
||||
UserSettings& user_settings = UserSettings::get_instance();
|
||||
user_settings.initialize_flash();
|
||||
|
||||
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(), _gamepads);
|
||||
}
|
||||
|
||||
void pico_w::run() {
|
||||
multicore_reset_core1();
|
||||
multicore_launch_core1(core1_task);
|
||||
|
||||
uint32_t tid_gp_check = TaskQueue::Core0::get_new_task_id();
|
||||
set_gp_check_timer(tid_gp_check);
|
||||
|
||||
DeviceDriver* device_driver = DeviceManager::get_instance().get_driver();
|
||||
|
||||
tud_init(BOARD_TUD_RHPORT);
|
||||
|
||||
while (true) {
|
||||
TaskQueue::Core0::process_tasks();
|
||||
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i) {
|
||||
device_driver->process(i, _gamepads[i]);
|
||||
tud_task();
|
||||
}
|
||||
sleep_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
// #else // (OGXM_BOARD == PI_PICOW)
|
||||
|
||||
// void pico_w::initialize() {}
|
||||
// void pico_w::run() {}
|
||||
|
||||
#endif // (OGXM_BOARD == PI_PICOW)
|
||||
8
Firmware/RP2040/src/OGXMini/Board/PicoW.h
Normal file
8
Firmware/RP2040/src/OGXMini/Board/PicoW.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace pico_w {
|
||||
void initialize();
|
||||
void run();
|
||||
} // namespace pico_w
|
||||
134
Firmware/RP2040/src/OGXMini/Board/Standard.cpp
Normal file
134
Firmware/RP2040/src/OGXMini/Board/Standard.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "Board/Config.h"
|
||||
#include "OGXMini/Board/Standard.h"
|
||||
#if ((OGXM_BOARD == PI_PICO) || (OGXM_BOARD == RP2040_ZERO) || (OGXM_BOARD == ADAFRUIT_FEATHER))
|
||||
|
||||
#include <pico/multicore.h>
|
||||
|
||||
#include "tusb.h"
|
||||
#include "bsp/board_api.h"
|
||||
#include "pio_usb.h"
|
||||
|
||||
#include "USBHost/HostManager.h"
|
||||
#include "USBDevice/DeviceManager.h"
|
||||
#include "TaskQueue/TaskQueue.h"
|
||||
#include "Gamepad/Gamepad.h"
|
||||
#include "Board/board_api.h"
|
||||
#include "Board/ogxm_log.h"
|
||||
|
||||
constexpr uint32_t FEEDBACK_DELAY_MS = 200;
|
||||
|
||||
Gamepad _gamepads[MAX_GAMEPADS];
|
||||
|
||||
void core1_task() {
|
||||
HostManager& host_manager = HostManager::get_instance();
|
||||
host_manager.initialize(_gamepads);
|
||||
|
||||
//Pico-PIO-USB will not reliably detect a hot plug on some boards,
|
||||
//monitor and init host stack after connection
|
||||
while(!board_api::usb::host_connected()) {
|
||||
sleep_ms(100);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
uint32_t tid_feedback = TaskQueue::Core1::get_new_task_id();
|
||||
TaskQueue::Core1::queue_delayed_task(tid_feedback, FEEDBACK_DELAY_MS, true,
|
||||
[&host_manager] {
|
||||
host_manager.send_feedback();
|
||||
});
|
||||
|
||||
while (true) {
|
||||
TaskQueue::Core1::process_tasks();
|
||||
tuh_task();
|
||||
}
|
||||
}
|
||||
|
||||
void set_gp_check_timer(uint32_t task_id) {
|
||||
UserSettings& user_settings = UserSettings::get_instance();
|
||||
|
||||
TaskQueue::Core0::queue_delayed_task(task_id, UserSettings::GP_CHECK_DELAY_MS, true,
|
||||
[&user_settings] {
|
||||
//Check gamepad inputs for button combo to change usb device driver
|
||||
if (user_settings.check_for_driver_change(_gamepads[0])) {
|
||||
OGXM_LOG("Driver change detected, storing new driver.\n");
|
||||
//This will store the new mode and reboot the pico
|
||||
user_settings.store_driver_type(user_settings.get_current_driver());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//Called by tusb host so we know to connect or disconnect usb
|
||||
void standard::host_mounted(bool host_mounted) {
|
||||
static std::atomic<bool> tud_is_inited = false;
|
||||
board_api::set_led(host_mounted);
|
||||
|
||||
if (!host_mounted && tud_is_inited.load()) {
|
||||
TaskQueue::Core0::queue_task([]() {
|
||||
OGXM_LOG("USB disconnected, rebooting.\n");
|
||||
board_api::usb::disconnect_all();
|
||||
board_api::reboot();
|
||||
});
|
||||
} else if (!tud_is_inited.load()) {
|
||||
TaskQueue::Core0::queue_task([]() {
|
||||
tud_init(BOARD_TUD_RHPORT);
|
||||
tud_is_inited.store(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void standard::initialize() {
|
||||
board_api::init_board();
|
||||
|
||||
UserSettings& user_settings = UserSettings::get_instance();
|
||||
user_settings.initialize_flash();
|
||||
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i) {
|
||||
_gamepads[i].set_profile(user_settings.get_profile_by_index(i));
|
||||
}
|
||||
|
||||
DeviceManager::get_instance().initialize_driver(user_settings.get_current_driver(), _gamepads);
|
||||
}
|
||||
|
||||
void standard::run() {
|
||||
multicore_reset_core1();
|
||||
multicore_launch_core1(core1_task);
|
||||
|
||||
DeviceDriverType current_driver = UserSettings::get_instance().get_current_driver();
|
||||
|
||||
if (current_driver != DeviceDriverType::WEBAPP) {
|
||||
// Wait for something to call host_mounted()
|
||||
while (!tud_inited()) {
|
||||
TaskQueue::Core0::process_tasks();
|
||||
sleep_ms(100);
|
||||
}
|
||||
} else {
|
||||
//Connect immediately in WebApp mode
|
||||
host_mounted(true);
|
||||
}
|
||||
|
||||
uint32_t tid_gp_check = TaskQueue::Core0::get_new_task_id();
|
||||
set_gp_check_timer(tid_gp_check);
|
||||
|
||||
DeviceDriver* device_driver = DeviceManager::get_instance().get_driver();
|
||||
|
||||
while (true) {
|
||||
TaskQueue::Core0::process_tasks();
|
||||
|
||||
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i) {
|
||||
device_driver->process(i, _gamepads[i]);
|
||||
}
|
||||
tud_task();
|
||||
sleep_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
// #else // OGXM_BOARD == PI_PICO || OGXM_BOARD == RP2040_ZERO || OGXM_BOARD == ADAFRUIT_FEATHER
|
||||
|
||||
// void standard::host_mounted(bool host_mounted) {}
|
||||
// void standard::initialize() {}
|
||||
// void standard::run() {}
|
||||
|
||||
#endif // OGXM_BOARD == PI_PICO || OGXM_BOARD == RP2040_ZERO || OGXM_BOARD == ADAFRUIT_FEATHER
|
||||
9
Firmware/RP2040/src/OGXMini/Board/Standard.h
Normal file
9
Firmware/RP2040/src/OGXMini/Board/Standard.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace standard {
|
||||
void initialize();
|
||||
void run();
|
||||
void host_mounted(bool mounted);
|
||||
} // namespace standard
|
||||
103
Firmware/RP2040/src/OGXMini/OGXMini.cpp
Normal file
103
Firmware/RP2040/src/OGXMini/OGXMini.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "Board/Config.h"
|
||||
#include "OGXMini/Board/Standard.h"
|
||||
#include "OGXMini/Board/PicoW.h"
|
||||
#include "OGXMini/Board/Four_Channel_I2C.h"
|
||||
#include "OGXMini/Board/ESP32_Blueretro_I2C.h"
|
||||
#include "OGXMini/Board/ESP32_Bluepad32_I2C.h"
|
||||
#include "OGXMini/OGXMini.h"
|
||||
|
||||
namespace OGXMini {
|
||||
typedef void (*InitFunc)();
|
||||
typedef void (*RunFunc)();
|
||||
typedef void (*HostMountedFunc)(bool mounted);
|
||||
typedef void (*HostMountedWTypeFunc)(bool mounted, HostDriverType host_type);
|
||||
typedef void (*WirelessConnectedFunc)(bool connected, uint8_t idx);
|
||||
|
||||
static constexpr InitFunc init_func[BOARDS_COUNT] = {
|
||||
standard::initialize, // PI_PICO
|
||||
standard::initialize, // RP2040_ZERO
|
||||
standard::initialize, // ADAFRUIT_FEATHER
|
||||
pico_w::initialize, // PI_PICOW
|
||||
esp32_bp32_i2c::initialize, // ESP32_BLUEPAD32_I2C
|
||||
esp32_br_i2c::initialize, // ESP32_BLUERETRO_I2C
|
||||
four_ch_i2c::initialize, // EXTERNAL_4CH_I2C
|
||||
four_ch_i2c::initialize, // INTERNAL_4CH_I2C
|
||||
};
|
||||
|
||||
static constexpr RunFunc run_func[BOARDS_COUNT] = {
|
||||
standard::run, // PI_PICO
|
||||
standard::run, // RP2040_ZERO
|
||||
standard::run, // ADAFRUIT_FEATHER
|
||||
pico_w::run, // PI_PICOW
|
||||
esp32_bp32_i2c::run, // ESP32_BLUEPAD32_I2C
|
||||
esp32_br_i2c::run, // ESP32_BLUERETRO_I2C
|
||||
four_ch_i2c::run, // EXTERNAL_4CH_I2C
|
||||
four_ch_i2c::run, // INTERNAL_4CH_I2C
|
||||
};
|
||||
|
||||
static constexpr HostMountedFunc host_mount_func[BOARDS_COUNT] = {
|
||||
standard::host_mounted, // PI_PICO
|
||||
standard::host_mounted, // RP2040_ZERO
|
||||
standard::host_mounted, // ADAFRUIT_FEATHER
|
||||
nullptr, // PI_PICOW
|
||||
nullptr, // ESP32_BLUEPAD32_I2C
|
||||
nullptr, // ESP32_BLUERETRO_I2C
|
||||
four_ch_i2c::host_mounted, // EXTERNAL_4CH_I2C
|
||||
four_ch_i2c::host_mounted, // INTERNAL_4CH_I2C
|
||||
};
|
||||
|
||||
static constexpr HostMountedWTypeFunc host_mount_w_type_func[BOARDS_COUNT] = {
|
||||
nullptr, // PI_PICO
|
||||
nullptr, // RP2040_ZERO
|
||||
nullptr, // ADAFRUIT_FEATHER
|
||||
nullptr, // PI_PICOW
|
||||
nullptr, // ESP32_BLUEPAD32_I2C
|
||||
nullptr, // ESP32_BLUERETRO_I2C
|
||||
four_ch_i2c::host_mounted_w_type, // EXTERNAL_4CH_I2C
|
||||
four_ch_i2c::host_mounted_w_type, // INTERNAL_4CH_I2C
|
||||
};
|
||||
|
||||
static constexpr WirelessConnectedFunc wl_conn_func[BOARDS_COUNT] = {
|
||||
nullptr, // PI_PICO
|
||||
nullptr, // RP2040_ZERO
|
||||
nullptr, // ADAFRUIT_FEATHER
|
||||
nullptr, // PI_PICOW
|
||||
nullptr, // ESP32_BLUEPAD32_I2C
|
||||
nullptr, // ESP32_BLUERETRO_I2C
|
||||
four_ch_i2c::wireless_connected, // EXTERNAL_4CH_I2C
|
||||
four_ch_i2c::wireless_connected, // INTERNAL_4CH_I2C
|
||||
};
|
||||
|
||||
void initialize() {
|
||||
if (init_func[OGXM_BOARD] != nullptr) {
|
||||
init_func[OGXM_BOARD]();
|
||||
}
|
||||
}
|
||||
|
||||
void run() {
|
||||
if (run_func[OGXM_BOARD] != nullptr) {
|
||||
run_func[OGXM_BOARD]();
|
||||
}
|
||||
}
|
||||
|
||||
void host_mounted(bool mounted, HostDriverType host_type) {
|
||||
if (host_mount_w_type_func[OGXM_BOARD] != nullptr) {
|
||||
host_mount_w_type_func[OGXM_BOARD](mounted, host_type);
|
||||
} else if (host_mount_func[OGXM_BOARD] != nullptr) {
|
||||
host_mount_func[OGXM_BOARD](mounted);
|
||||
}
|
||||
}
|
||||
|
||||
void host_mounted(bool mounted) {
|
||||
if (host_mount_func[OGXM_BOARD] != nullptr) {
|
||||
host_mount_func[OGXM_BOARD](mounted);
|
||||
}
|
||||
}
|
||||
|
||||
void wireless_connected(bool connected, uint8_t idx) {
|
||||
if (wl_conn_func[OGXM_BOARD] != nullptr) {
|
||||
wl_conn_func[OGXM_BOARD](connected, idx);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OGXMini
|
||||
13
Firmware/RP2040/src/OGXMini/OGXMini.h
Normal file
13
Firmware/RP2040/src/OGXMini/OGXMini.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "USBHost/HostDriver/HostDriverTypes.h"
|
||||
|
||||
namespace OGXMini {
|
||||
void initialize();
|
||||
void run();
|
||||
void host_mounted(bool mounted, HostDriverType host_type);
|
||||
void host_mounted(bool mounted);
|
||||
void wireless_connected(bool connected, uint8_t idx);
|
||||
} // namespace OGXMini
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user