v1.0.0-alpha

This commit is contained in:
wiredopposite
2024-12-08 16:28:56 -07:00
parent a87ed08a63
commit 3eb0b56080
204 changed files with 20512 additions and 4210 deletions

View File

@@ -1,212 +0,0 @@
---
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: true
AlignConsecutiveBitFields: true
AlignConsecutiveDeclarations: true
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: true
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 160
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: true
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 3
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: true
IndentCaseBlocks: true
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentRequires: false
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: Signature
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PenaltyIndentedWhitespace: 0
PointerAlignment: Left
PPIndentWidth: -1
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
- ParseTestProto
- ParsePartialTestProto
CanonicalDelimiter: pb
BasedOnStyle: google
ReferenceAlignment: Pointer
ReflowComments: true
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: Auto
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...

16
.gitignore vendored
View File

@@ -1,6 +1,10 @@
build
.vscode
release
generated
tools
.ignore
Firmware/RP2040/build
Firmware/RP2040/.ignore
Firmware/RP2040/src/USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid_xremote_rom.h
Firmware/ESP32/.ignore
Firmware/ESP32/build
Firmware/ESP32/components/btstack
Firmware/ESP32/sdkconfig.old
Tools/dvd-dongle-rom.bin

22
.gitmodules vendored
View File

@@ -1,12 +1,12 @@
[submodule "lib/Pico-PIO-USB"]
path = lib/Pico-PIO-USB
url = https://github.com/sekigon-gonnoc/Pico-PIO-USB.git
[submodule "lib/tinyusb"]
path = lib/tinyusb
[submodule "Firmware/external/bluepad32"]
path = Firmware/external/bluepad32
url = https://github.com/ricardoquesada/bluepad32.git
[submodule "Firmware/external/tinyusb"]
path = Firmware/external/tinyusb
url = https://github.com/hathach/tinyusb.git
[submodule "src/usbd"]
path = src/usbd
url = https://github.com/wiredopposite/usbd.git
[submodule "lib/tusb_xinput"]
path = lib/tusb_xinput
url = https://github.com/Ryzee119/tusb_xinput.git
[submodule "Firmware/external/Pico-PIO-USB"]
path = Firmware/external/Pico-PIO-USB
url = https://github.com/sekigon-gonnoc/Pico-PIO-USB.git
[submodule "Tools/dump-dvd-kit"]
path = Tools/dump-dvd-kit
url = https://github.com/XboxDev/dump-dvd-kit.git

View File

@@ -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})

View File

@@ -0,0 +1,23 @@
{
"configurations": [
{
"name": "ESP-IDF",
"compilerPath": "${default}",
"compileCommands": "${config:idf.buildPath}/compile_commands.json",
"includePath": [
"${config:idf.espIdfPath}/components/**",
"${config:idf.espIdfPathWin}/components/**",
"${workspaceFolder}/main/**"
],
"browse": {
"path": [
"${config:idf.espIdfPath}/components",
"${config:idf.espIdfPathWin}/components",
"${workspaceFolder}/main"
],
"limitSymbolsToIncludedHeaders": true
}
}
],
"version": 4
}

104
Firmware/ESP32/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,104 @@
{
"idf.adapterTargetName": "esp32",
"cmake.configureOnOpen": false,
"files.associations": {
"freertos.h": "c",
"stdint.h": "c",
"stdlib.h": "c",
"atomic": "cpp",
"array": "cpp",
"task.h": "c",
"bit": "cpp",
"*.tcc": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"list": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"netfwd": "cpp",
"numeric": "cpp",
"random": "cpp",
"ratio": "cpp",
"regex": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp",
"c_wrapper.h": "c",
"queue": "cpp",
"charconv": "cpp",
"format": "cpp",
"forward_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"locale": "cpp",
"optional": "cpp",
"ranges": "cpp",
"span": "cpp",
"stack": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstring": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp"
},
"idf.portWin": "COM26",
"idf.flashType": "UART",
"C_Cpp.intelliSenseEngine": "default"
}

View File

@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.5)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(EXTERNAL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../external)
include(${EXTERNAL_DIR}/patch_libs.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/integrate_btstack.cmake)
apply_lib_patches(${EXTERNAL_DIR})
integrate_btstack(${EXTERNAL_DIR})
set(BLUEPAD32_ROOT ${EXTERNAL_DIR}/bluepad32/src/components/bluepad32)
if(NOT EXISTS ${BLUEPAD32_ROOT})
message(FATAL_ERROR "External directory not found: ${BLUEPAD32_ROOT}")
endif()
set(EXTRA_COMPONENT_DIRS
${BLUEPAD32_ROOT}/..
${CMAKE_CURRENT_SOURCE_DIR}/components
)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(OGX-Mini-ESP32)

View 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()

View 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)

View 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

View File

@@ -0,0 +1,644 @@
#include <cstdint>
#include <atomic>
#include <cstring>
#include <array>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/timers.h>
#include <freertos/queue.h>
#include <esp_log.h>
#include "btstack_port_esp32.h"
#include "btstack_run_loop.h"
#include "btstack_stdio_esp32.h"
#include "uni.h"
#include "sdkconfig.h"
#include "Board/board_api.h"
#include "I2CDriver/I2CDriver.h"
#include "Bluepad32/Gamepad.h"
#include "Bluepad32/Bluepad32.h"
namespace bluepad32 {
static constexpr uint32_t FEEDBACK_TIME_MS = 200;
static constexpr uint32_t LED_TIME_MS = 500;
struct Device
{
std::atomic<bool> connected{false};
std::atomic<ReportIn> report_in;
std::atomic<ReportOut> report_out;
};
std::array<Device, CONFIG_BLUEPAD32_MAX_DEVICES> devices_;
static inline void send_feedback_cb(btstack_timer_source *ts)
{
uni_hid_device_t* bp_device;
for (uint8_t i = 0; i < CONFIG_BLUEPAD32_MAX_DEVICES; ++i)
{
bp_device = uni_hid_device_get_instance_for_idx(i);
if (!bp_device || !bp_device->report_parser.play_dual_rumble)
{
continue;
}
ReportOut report_out = devices_[i].report_out.load();
bp_device->report_parser.play_dual_rumble(bp_device, 0, FEEDBACK_TIME_MS, report_out.rumble_l, report_out.rumble_r);
}
// btstack_run_loop_remove_timer(ts);
btstack_run_loop_set_timer(ts, FEEDBACK_TIME_MS);
btstack_run_loop_add_timer(ts);
}
static inline void check_led_cb(btstack_timer_source *ts)
{
static bool led_state = false;
led_state = !led_state;
if constexpr (board_api::NUM_LEDS == 1)
{
board_api::set_led(any_connected() ? 1 : (led_state ? 1 : 0));
}
else
{
for (uint8_t i = 0; i < board_api::NUM_LEDS; ++i)
{
board_api::set_led(i, devices_[i].connected.load() ? 1 : (led_state ? 1 : 0));
}
}
// btstack_run_loop_remove_timer(ts);
btstack_run_loop_set_timer(ts, LED_TIME_MS);
btstack_run_loop_add_timer(ts);
}
//BT Driver
static void init(int argc, const char** arg_V)
{
}
static void init_complete_cb(void)
{
uni_bt_enable_new_connections_unsafe(true);
// Based on runtime condition, you can delete or list the stored BT keys.
if (1)
{
uni_bt_del_keys_unsafe();
}
else
{
uni_bt_list_keys_unsafe();
}
uni_property_dump_all();
}
static uni_error_t device_discovered_cb(bd_addr_t addr, const char* name, uint16_t cod, uint8_t rssi)
{
if (!((cod & UNI_BT_COD_MINOR_MASK) & UNI_BT_COD_MINOR_GAMEPAD))
{
return UNI_ERROR_IGNORE_DEVICE;
}
return UNI_ERROR_SUCCESS;
}
static void device_connected_cb(uni_hid_device_t* device)
{
#ifdef CONFIG_BLUEPAD32_USB_CONSOLE_ENABLE
logd("BP32", "Device connected, addr: %p, index: %i\n", device, uni_hid_device_get_idx_for_instance(device));
#endif
int idx = uni_hid_device_get_idx_for_instance(device);
if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0)
{
return;
}
devices_[idx].connected.store(true);
}
static void device_disconnected_cb(uni_hid_device_t* device)
{
#ifdef CONFIG_BLUEPAD32_USB_CONSOLE_ENABLE
logd("BP32", "Device disconnected, addr: %p, index: %i\n", device, uni_hid_device_get_idx_for_instance(device));
#endif
int idx = uni_hid_device_get_idx_for_instance(device);
if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0)
{
return;
}
ReportIn report_in;
report_in.index = static_cast<uint8_t>(idx);
devices_[idx].report_in.store(report_in);
devices_[idx].connected.store(false);
}
static uni_error_t device_ready_cb(uni_hid_device_t* device)
{
return UNI_ERROR_SUCCESS;
}
static void oob_event_cb(uni_platform_oob_event_t event, void* data)
{
return;
}
static void controller_data_cb(uni_hid_device_t* device, uni_controller_t* controller)
{
static uni_gamepad_t prev_uni_gp[CONFIG_BLUEPAD32_MAX_DEVICES] = {};
if (controller->klass != UNI_CONTROLLER_CLASS_GAMEPAD)
{
return;
}
uni_gamepad_t *uni_gp = &controller->gamepad;
int idx = uni_hid_device_get_idx_for_instance(device);
if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0 || std::memcmp(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t)) == 0)
{
return;
}
ReportIn report_in;
report_in.index = static_cast<uint8_t>(idx);
switch (uni_gp->dpad)
{
case DPAD_UP:
report_in.dpad = Gamepad::DPad::UP;
break;
case DPAD_DOWN:
report_in.dpad = Gamepad::DPad::DOWN;
break;
case DPAD_LEFT:
report_in.dpad = Gamepad::DPad::LEFT;
break;
case DPAD_RIGHT:
report_in.dpad = Gamepad::DPad::RIGHT;
break;
case DPAD_UP | DPAD_RIGHT:
report_in.dpad = Gamepad::DPad::UP_RIGHT;
break;
case DPAD_DOWN | DPAD_RIGHT:
report_in.dpad = Gamepad::DPad::DOWN_RIGHT;
break;
case DPAD_DOWN | DPAD_LEFT:
report_in.dpad = Gamepad::DPad::DOWN_LEFT;
break;
case DPAD_UP | DPAD_LEFT:
report_in.dpad = Gamepad::DPad::UP_LEFT;
break;
default:
break;
}
if (uni_gp->buttons & BUTTON_A) report_in.buttons |= Gamepad::Button::A;
if (uni_gp->buttons & BUTTON_B) report_in.buttons |= Gamepad::Button::B;
if (uni_gp->buttons & BUTTON_X) report_in.buttons |= Gamepad::Button::X;
if (uni_gp->buttons & BUTTON_Y) report_in.buttons |= Gamepad::Button::Y;
if (uni_gp->buttons & BUTTON_SHOULDER_L) report_in.buttons |= Gamepad::Button::LB;
if (uni_gp->buttons & BUTTON_SHOULDER_R) report_in.buttons |= Gamepad::Button::RB;
if (uni_gp->buttons & BUTTON_THUMB_L) report_in.buttons |= Gamepad::Button::L3;
if (uni_gp->buttons & BUTTON_THUMB_R) report_in.buttons |= Gamepad::Button::R3;
if (uni_gp->misc_buttons & MISC_BUTTON_BACK) report_in.buttons |= Gamepad::Button::BACK;
if (uni_gp->misc_buttons & MISC_BUTTON_START) report_in.buttons |= Gamepad::Button::START;
if (uni_gp->misc_buttons & MISC_BUTTON_SYSTEM) report_in.buttons |= Gamepad::Button::SYS;
if (uni_gp->misc_buttons & MISC_BUTTON_CAPTURE) report_in.buttons |= Gamepad::Button::MISC;
report_in.trigger_l = Gamepad::scale_trigger(uni_gp->brake);
report_in.trigger_r = Gamepad::scale_trigger(uni_gp->throttle);
report_in.joystick_lx = static_cast<int16_t>(uni_gp->axis_x);
report_in.joystick_ly = static_cast<int16_t>(uni_gp->axis_y);
report_in.joystick_rx = static_cast<int16_t>(uni_gp->axis_rx);
report_in.joystick_ry = static_cast<int16_t>(uni_gp->axis_ry);
devices_[idx].report_in.store(report_in);
std::memcpy(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t));
}
const uni_property_t* get_property_cb(uni_property_idx_t idx)
{
return nullptr;
}
uni_platform* get_driver()
{
static uni_platform driver =
{
.name = "OGXMiniW",
.init = init,
.on_init_complete = init_complete_cb,
.on_device_discovered = device_discovered_cb,
.on_device_connected = device_connected_cb,
.on_device_disconnected = device_disconnected_cb,
.on_device_ready = device_ready_cb,
.on_controller_data = controller_data_cb,
.get_property = get_property_cb,
.on_oob_event = oob_event_cb,
};
return &driver;
}
//Public
ReportIn get_report_in(uint8_t index)
{
// if (index >= CONFIG_BLUEPAD32_MAX_DEVICES)
// {
// return ReportIn();
// }
return devices_[index].report_in.load();
}
void set_report_out(const ReportOut& report_out)
{
if (report_out.index >= CONFIG_BLUEPAD32_MAX_DEVICES)
{
return;
}
devices_[report_out.index].report_out.store(report_out);
}
void run_task()
{
board_api::init_pins();
btstack_init();
uni_platform_set_custom(get_driver());
uni_init(0, nullptr);
btstack_timer_source_t feedback_timer;
feedback_timer.process = send_feedback_cb;
feedback_timer.context = nullptr;
btstack_run_loop_set_timer(&feedback_timer, FEEDBACK_TIME_MS);
btstack_run_loop_add_timer(&feedback_timer);
btstack_timer_source_t led_timer;
led_timer.process = check_led_cb;
led_timer.context = nullptr;
btstack_run_loop_set_timer(&led_timer, LED_TIME_MS);
btstack_run_loop_add_timer(&led_timer);
btstack_run_loop_execute();
}
//Thread safe
bool any_connected()
{
for (auto& device : devices_)
{
if (device.connected.load())
{
return true;
}
}
return false;
}
bool connected(uint8_t index)
{
return devices_[index].connected.load();
}
} // namespace bluepad32
// #include <cstdint>
// #include <atomic>
// #include <cstring>
// #include <array>
// #include <freertos/FreeRTOS.h>
// #include <freertos/task.h>
// #include <freertos/timers.h>
// #include <freertos/queue.h>
// #include <esp_log.h>
// #include "btstack_port_esp32.h"
// #include "btstack_run_loop.h"
// #include "btstack_stdio_esp32.h"
// #include "uni.h"
// #include "sdkconfig.h"
// #include "Board/board_api.h"
// #include "I2CDriver/I2CDriver.h"
// #include "Bluepad32/Gamepad.h"
// #include "Bluepad32/Bluepad32.h"
// namespace bluepad32 {
// static constexpr uint32_t FEEDBACK_TIME_MS = 200;
// static constexpr uint32_t LED_TIME_MS = 500;
// struct Device
// {
// std::atomic<bool> connected{false};
// std::atomic<ReportIn> report_in;
// std::atomic<bool> new_report_in{false};
// std::atomic<ReportOut> report_out;
// };
// std::array<Device, CONFIG_BLUEPAD32_MAX_DEVICES> devices_;
// static inline void send_feedback_cb(btstack_timer_source *ts)
// {
// uni_hid_device_t* bp_device;
// for (uint8_t i = 0; i < CONFIG_BLUEPAD32_MAX_DEVICES; ++i)
// {
// bp_device = uni_hid_device_get_instance_for_idx(i);
// if (!bp_device || !bp_device->report_parser.play_dual_rumble)
// {
// continue;
// }
// ReportOut report_out = devices_[i].report_out.load();
// bp_device->report_parser.play_dual_rumble(bp_device, 0, FEEDBACK_TIME_MS, report_out.rumble_l, report_out.rumble_r);
// }
// btstack_run_loop_remove_timer(ts);
// btstack_run_loop_set_timer(ts, FEEDBACK_TIME_MS);
// btstack_run_loop_add_timer(ts);
// }
// static inline void check_led_cb(btstack_timer_source *ts)
// {
// static bool led_state = false;
// led_state = !led_state;
// if constexpr (board_api::NUM_LEDS == 1)
// {
// board_api::set_led(any_connected() ? 1 : (led_state ? 1 : 0));
// }
// else
// {
// for (uint8_t i = 0; i < board_api::NUM_LEDS; ++i)
// {
// board_api::set_led(i, devices_[i].connected.load() ? 1 : (led_state ? 1 : 0));
// }
// }
// btstack_run_loop_remove_timer(ts);
// btstack_run_loop_set_timer(ts, LED_TIME_MS);
// btstack_run_loop_add_timer(ts);
// }
// //BT Driver
// static void init(int argc, const char** arg_V)
// {
// }
// static void init_complete_cb(void)
// {
// uni_bt_enable_new_connections_unsafe(true);
// // Based on runtime condition, you can delete or list the stored BT keys.
// if (1)
// {
// uni_bt_del_keys_unsafe();
// }
// else
// {
// uni_bt_list_keys_unsafe();
// }
// uni_property_dump_all();
// }
// static uni_error_t device_discovered_cb(bd_addr_t addr, const char* name, uint16_t cod, uint8_t rssi)
// {
// if (!((cod & UNI_BT_COD_MINOR_MASK) & UNI_BT_COD_MINOR_GAMEPAD))
// {
// return UNI_ERROR_IGNORE_DEVICE;
// }
// return UNI_ERROR_SUCCESS;
// }
// static void device_connected_cb(uni_hid_device_t* device)
// {
// #ifdef CONFIG_BLUEPAD32_USB_CONSOLE_ENABLE
// logd("BP32", "Device connected, addr: %p, index: %i\n", device, uni_hid_device_get_idx_for_instance(device));
// #endif
// int idx = uni_hid_device_get_idx_for_instance(device);
// if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0)
// {
// return;
// }
// devices_[idx].connected.store(true);
// }
// static void device_disconnected_cb(uni_hid_device_t* device)
// {
// #ifdef CONFIG_BLUEPAD32_USB_CONSOLE_ENABLE
// logd("BP32", "Device disconnected, addr: %p, index: %i\n", device, uni_hid_device_get_idx_for_instance(device));
// #endif
// int idx = uni_hid_device_get_idx_for_instance(device);
// if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0)
// {
// return;
// }
// ReportIn report_in = ReportIn();
// report_in.index = static_cast<uint8_t>(idx);
// devices_[idx].report_in.store(report_in);
// devices_[idx].connected.store(false);
// }
// static uni_error_t device_ready_cb(uni_hid_device_t* device)
// {
// return UNI_ERROR_SUCCESS;
// }
// static void oob_event_cb(uni_platform_oob_event_t event, void* data)
// {
// return;
// }
// static void controller_data_cb(uni_hid_device_t* device, uni_controller_t* controller)
// {
// static uni_gamepad_t prev_uni_gp[CONFIG_BLUEPAD32_MAX_DEVICES] = {};
// if (!device || !controller || controller->klass != UNI_CONTROLLER_CLASS_GAMEPAD)
// {
// return;
// }
// uni_gamepad_t *uni_gp = &controller->gamepad;
// int idx = uni_hid_device_get_idx_for_instance(device);
// if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0 || std::memcmp(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t)) == 0)
// {
// return;
// }
// #ifdef CONFIG_BLUEPAD32_USB_CONSOLE_ENABLE
// logi("BP32 Stack", "Remaining stack: %d\n", uxTaskGetStackHighWaterMark(NULL));
// #endif
// ReportIn report_in;
// report_in.index = static_cast<uint8_t>(idx);
// switch (uni_gp->dpad)
// {
// case DPAD_UP:
// report_in.dpad = Gamepad::DPad::UP;
// break;
// case DPAD_DOWN:
// report_in.dpad = Gamepad::DPad::DOWN;
// break;
// case DPAD_LEFT:
// report_in.dpad = Gamepad::DPad::LEFT;
// break;
// case DPAD_RIGHT:
// report_in.dpad = Gamepad::DPad::RIGHT;
// break;
// case DPAD_UP | DPAD_RIGHT:
// report_in.dpad = Gamepad::DPad::UP_RIGHT;
// break;
// case DPAD_DOWN | DPAD_RIGHT:
// report_in.dpad = Gamepad::DPad::DOWN_RIGHT;
// break;
// case DPAD_DOWN | DPAD_LEFT:
// report_in.dpad = Gamepad::DPad::DOWN_LEFT;
// break;
// case DPAD_UP | DPAD_LEFT:
// report_in.dpad = Gamepad::DPad::UP_LEFT;
// break;
// default:
// break;
// }
// if (uni_gp->buttons & BUTTON_A) report_in.buttons |= Gamepad::Button::A;
// if (uni_gp->buttons & BUTTON_B) report_in.buttons |= Gamepad::Button::B;
// if (uni_gp->buttons & BUTTON_X) report_in.buttons |= Gamepad::Button::X;
// if (uni_gp->buttons & BUTTON_Y) report_in.buttons |= Gamepad::Button::Y;
// if (uni_gp->buttons & BUTTON_SHOULDER_L) report_in.buttons |= Gamepad::Button::LB;
// if (uni_gp->buttons & BUTTON_SHOULDER_R) report_in.buttons |= Gamepad::Button::RB;
// if (uni_gp->buttons & BUTTON_THUMB_L) report_in.buttons |= Gamepad::Button::L3;
// if (uni_gp->buttons & BUTTON_THUMB_R) report_in.buttons |= Gamepad::Button::R3;
// if (uni_gp->misc_buttons & MISC_BUTTON_BACK) report_in.buttons |= Gamepad::Button::BACK;
// if (uni_gp->misc_buttons & MISC_BUTTON_START) report_in.buttons |= Gamepad::Button::START;
// if (uni_gp->misc_buttons & MISC_BUTTON_SYSTEM) report_in.buttons |= Gamepad::Button::SYS;
// if (uni_gp->misc_buttons & MISC_BUTTON_CAPTURE) report_in.buttons |= Gamepad::Button::MISC;
// report_in.trigger_l = Gamepad::scale_trigger(uni_gp->brake);
// report_in.trigger_r = Gamepad::scale_trigger(uni_gp->throttle);
// report_in.joystick_lx = static_cast<int16_t>(uni_gp->axis_x);
// report_in.joystick_ly = static_cast<int16_t>(uni_gp->axis_y);
// report_in.joystick_rx = static_cast<int16_t>(uni_gp->axis_rx);
// report_in.joystick_ry = static_cast<int16_t>(uni_gp->axis_ry);
// devices_[idx].report_in.store(report_in);
// devices_[idx].new_report_in.store(true);
// std::memcpy(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t));
// }
// const uni_property_t* get_property_cb(uni_property_idx_t idx)
// {
// return nullptr;
// }
// uni_platform* get_driver()
// {
// static uni_platform driver =
// {
// .name = "OGXMiniW",
// .init = init,
// .on_init_complete = init_complete_cb,
// .on_device_discovered = device_discovered_cb,
// .on_device_connected = device_connected_cb,
// .on_device_disconnected = device_disconnected_cb,
// .on_device_ready = device_ready_cb,
// .on_controller_data = controller_data_cb,
// .get_property = get_property_cb,
// .on_oob_event = oob_event_cb,
// };
// return &driver;
// }
// //Public
// void run_task()
// {
// board_api::init_pins();
// btstack_init();
// uni_platform_set_custom(get_driver());
// uni_init(0, nullptr);
// btstack_timer_source_t feedback_timer;
// feedback_timer.process = send_feedback_cb;
// feedback_timer.context = nullptr;
// btstack_run_loop_set_timer(&feedback_timer, FEEDBACK_TIME_MS);
// btstack_run_loop_add_timer(&feedback_timer);
// btstack_timer_source_t led_timer;
// led_timer.process = check_led_cb;
// led_timer.context = nullptr;
// btstack_run_loop_set_timer(&led_timer, LED_TIME_MS);
// btstack_run_loop_add_timer(&led_timer);
// btstack_run_loop_execute();
// }
// //Thread safe
// bool any_connected()
// {
// for (auto& device : devices_)
// {
// if (device.connected.load())
// {
// return true;
// }
// }
// return false;
// }
// bool connected(uint8_t index)
// {
// return devices_[index].connected.load();
// }
// bool get_report_in(uint8_t index, ReportIn& report)
// {
// if (index >= CONFIG_BLUEPAD32_MAX_DEVICES)
// {
// return false;
// }
// if (devices_[index].new_report_in.exchange(false))
// {
// report = devices_[index].report_in.load();
// return true;
// }
// return false;
// }
// void set_report_out(const ReportOut& report)
// {
// if (report.index >= CONFIG_BLUEPAD32_MAX_DEVICES)
// {
// return;
// }
// devices_[report.index].report_out.store(report);
// }
// } // namespace bluepad32

View File

@@ -0,0 +1,19 @@
#ifndef _BLUEPAD32_DRIVER_H_
#define _BLUEPAD32_DRIVER_H_
#include <cstdint>
#include "sdkconfig.h"
#include "I2CDriver/I2CDriver.h"
#include "Reports.h"
namespace bluepad32
{
void run_task();
bool connected(uint8_t index);
bool any_connected();
ReportIn get_report_in(uint8_t index);
void set_report_out(const ReportOut& report);
}
#endif // _BLUEPAD32_DRIVER_H_

View File

@@ -0,0 +1,82 @@
#ifndef GAMEPAD_H
#define GAMEPAD_H
#include <cstdint>
namespace Gamepad
{
namespace DPad
{
static constexpr uint8_t UP = 0x01;
static constexpr uint8_t DOWN = 0x02;
static constexpr uint8_t LEFT = 0x04;
static constexpr uint8_t RIGHT = 0x08;
static constexpr uint8_t UP_LEFT = UP | LEFT;
static constexpr uint8_t UP_RIGHT = UP | RIGHT;
static constexpr uint8_t DOWN_LEFT = DOWN | LEFT;
static constexpr uint8_t DOWN_RIGHT = DOWN | RIGHT;
static constexpr uint8_t NONE = 0x00;
}
namespace Button
{
static constexpr uint16_t A = 0x0001;
static constexpr uint16_t B = 0x0002;
static constexpr uint16_t X = 0x0004;
static constexpr uint16_t Y = 0x0008;
static constexpr uint16_t L3 = 0x0010;
static constexpr uint16_t R3 = 0x0020;
static constexpr uint16_t BACK = 0x0040;
static constexpr uint16_t START = 0x0080;
static constexpr uint16_t LB = 0x0100;
static constexpr uint16_t RB = 0x0200;
static constexpr uint16_t SYS = 0x0400;
static constexpr uint16_t MISC = 0x0800;
}
namespace UINT_8
{
static constexpr uint8_t MID = 0x80;
static constexpr uint8_t MAX = 0xFF;
}
namespace INT_10
{
static constexpr int32_t MIN = -512;
static constexpr int32_t MAX = 511;
}
namespace UINT_10
{
static constexpr int32_t MAX = 1023;
}
static inline uint8_t scale_joystick(int32_t value)
{
value = value - INT_10::MIN;
if (value >= UINT_10::MAX)
{
return UINT_8::MAX;
}
else if (value <= 0)
{
return 0;
}
return static_cast<uint8_t>(value >> 2);
}
static inline uint8_t scale_trigger(int32_t value)
{
if (value >= UINT_10::MAX)
{
return UINT_8::MAX;
}
else if (value <= 0)
{
return 0;
}
return static_cast<uint8_t>(value >> 2);
}
} // namespace Gamepad
#endif // GAMEPAD_H

View File

@@ -0,0 +1,76 @@
#include <driver/gpio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#include "board/board_api.h"
namespace board_api {
SemaphoreHandle_t leds_mutex_ = nullptr;
SemaphoreHandle_t reset_mutex_ = nullptr;
void init_pins()
{
if (leds_mutex_ == nullptr)
{
leds_mutex_ = xSemaphoreCreateMutex();
}
if (reset_mutex_ == nullptr)
{
reset_mutex_ = xSemaphoreCreateMutex();
}
if (xSemaphoreTake(leds_mutex_, portMAX_DELAY))
{
for (const auto& LED_PIN : LED_PINS)
{
gpio_reset_pin(LED_PIN);
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(LED_PIN, 0);
}
xSemaphoreGive(leds_mutex_);
}
// if (xSemaphoreTake(reset_mutex_, portMAX_DELAY))
// {
// gpio_reset_pin(RESET_PIN);
// gpio_set_direction(RESET_PIN, GPIO_MODE_INPUT);
// gpio_set_level(RESET_PIN, 1);
// xSemaphoreGive(reset_mutex_);
// }
}
//Set LED by index
void set_led(uint8_t index, bool state)
{
if constexpr (NUM_LEDS < 1)
{
return;
}
if (index >= NUM_LEDS)
{
return;
}
if (xSemaphoreTake(leds_mutex_, portMAX_DELAY))
{
gpio_set_level(LED_PINS[index], state ? 1 : 0);
xSemaphoreGive(leds_mutex_);
}
}
//Set first LED
void set_led(bool state)
{
if constexpr (NUM_LEDS < 1)
{
return;
}
if (xSemaphoreTake(leds_mutex_, portMAX_DELAY))
{
gpio_set_level(LED_PINS[0], state ? 1 : 0);
xSemaphoreGive(leds_mutex_);
}
}
} // namespace board_api

View File

@@ -0,0 +1,44 @@
#ifndef _BOARD_API_H_
#define _BOARD_API_H_
#include <cstdint>
#include <driver/gpio.h>
#include "sdkconfig.h"
#define MAX_GPIO_NUM 40
namespace board_api
{
static constexpr gpio_num_t RESET_PIN = static_cast<gpio_num_t>(CONFIG_RESET_PIN);
static_assert(RESET_PIN < MAX_GPIO_NUM, "Invalid RESET_PIN");
static constexpr gpio_num_t LED_PINS[] =
{
#if defined(CONFIG_ENABLE_LED_1) && (CONFIG_LED_PIN_1 < MAX_GPIO_NUM) && (CONFIG_BLUEPAD32_MAX_DEVICES > 0)
static_cast<gpio_num_t>(CONFIG_LED_PIN_1),
#if defined(CONFIG_ENABLE_LED_2) && (CONFIG_LED_PIN_2 < MAX_GPIO_NUM) && (CONFIG_BLUEPAD32_MAX_DEVICES > 1)
static_cast<gpio_num_t>(CONFIG_LED_PIN_2),
#if defined(CONFIG_ENABLE_LED_3) && (CONFIG_LED_PIN_3 < MAX_GPIO_NUM) && (CONFIG_BLUEPAD32_MAX_DEVICES > 2)
static_cast<gpio_num_t>(CONFIG_LED_PIN_3),
#if defined(CONFIG_ENABLE_LED_4) && (CONFIG_LED_PIN_4 < MAX_GPIO_NUM) && (CONFIG_BLUEPAD32_MAX_DEVICES > 3)
static_cast<gpio_num_t>(CONFIG_LED_PIN_4),
#endif // CONFIG_ENABLE_LED_4
#endif // CONFIG_ENABLE_LED_3
#endif // CONFIG_ENABLE_LED_2
#endif // CONFIG_ENABLE_LED_1
};
static constexpr uint8_t NUM_LEDS = sizeof(LED_PINS) / sizeof(LED_PINS[0]);
void init_pins();
void set_led(uint8_t index, bool state);
void set_led(bool state);
void check_reset_pin();
}
#endif // _BOARD_API_H_

View File

@@ -0,0 +1,15 @@
idf_component_register(
SRCS
"main.c"
"c_wrapper.cpp"
"Board/board_api.cpp"
"Bluepad32/Bluepad32.cpp"
"I2CDriver/I2CDriver.cpp"
INCLUDE_DIRS
"."
REQUIRES
bluepad32
btstack
driver
nvs_flash
)

View File

@@ -0,0 +1,210 @@
// #include <cstring>
// #include <atomic>
// #include <array>
// #include <freertos/FreeRTOS.h>
// #include <freertos/task.h>
// #include <freertos/timers.h>
// #include <driver/i2c.h>
// #include <driver/gpio.h>
// #include <esp_log.h>
// #include "sdkconfig.h"
// #include "Board/board_api.h"
// #include "I2CDriver/I2CDriver.h"
// #include "Bluepad32/Bluepad32.h"
// namespace i2c_driver {
// static constexpr bool MULTI_SLAVE =
// #if CONFIG_MULTI_SLAVE_MODE == 0
// false;
// #else
// true;
// #endif
// std::atomic<bool> poll_rumble_{false};
// void i2c_initialize()
// {
// i2c_config_t conf;
// std::memset(&conf, 0, sizeof(i2c_config_t));
// conf.mode = I2C_MODE_MASTER;
// conf.sda_io_num = GPIO_NUM_21;
// conf.scl_io_num = GPIO_NUM_22;
// conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
// conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
// conf.master.clk_speed = 400 * 1000;
// i2c_param_config(I2C_NUM_0, &conf);
// i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0);
// ESP_LOGD("I2C", "I2C initialized at pins {SDA: %d, SCL: %d}", conf.sda_io_num, conf.scl_io_num);
// }
// static inline esp_err_t i2c_write_blocking(uint8_t slave_address, uint8_t* data, size_t data_len)
// {
// i2c_cmd_handle_t cmd = i2c_cmd_link_create();
// i2c_master_start(cmd);
// i2c_master_write_byte(cmd, (slave_address << 1) | I2C_MASTER_WRITE, true);
// i2c_master_write(cmd, data, data_len, true);
// i2c_master_stop(cmd);
// esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(2));
// i2c_cmd_link_delete(cmd);
// return ret;
// }
// static inline esp_err_t i2c_read_blocking(uint8_t slave_address, uint8_t* data, size_t data_len)
// {
// i2c_cmd_handle_t cmd = i2c_cmd_link_create();
// i2c_master_start(cmd);
// i2c_master_write_byte(cmd, (slave_address << 1) | I2C_MASTER_READ, true);
// if (data_len > 1)
// {
// i2c_master_read(cmd, data, data_len - 1, I2C_MASTER_ACK);
// }
// i2c_master_read_byte(cmd, data + data_len - 1, I2C_MASTER_NACK);
// i2c_master_stop(cmd);
// esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(2));
// i2c_cmd_link_delete(cmd);
// return ret;
// }
// void run_loop(void* queue)
// {
// ReportIn report_in = ReportIn();
// ReportOut report_out = ReportOut();
// i2c_initialize();
// ESP_LOGD("I2C Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL));
// while (true)
// {
// for (uint8_t i = 0; i < CONFIG_BLUEPAD32_MAX_DEVICES; ++i)
// {
// vTaskDelay(1);
// if (!bluepad32::connected(i))
// {
// continue;
// }
// report_in = bluepad32::get_report_in(i);
// if (i2c_write_blocking(MULTI_SLAVE ? (report_in.index + 1) : 0x01, reinterpret_cast<uint8_t*>(&report_in), sizeof(ReportIn)) != ESP_OK)
// {
// ESP_LOGD("I2C", "Failed to write report_in");
// continue;
// }
// vTaskDelay(1);
// if (i2c_read_blocking(MULTI_SLAVE ? (report_in.index + 1) : 0x01, reinterpret_cast<uint8_t*>(&report_out), sizeof(ReportOut)) != ESP_OK)
// {
// ESP_LOGD("I2C", "Failed to read report_out");
// continue;
// }
// bluepad32::set_report_out(report_out);
// }
// }
// }
// // void run_loop()
// // {
// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL));
// // ReportIn report_in = ReportIn();
// // ESP_LOGD("I2C", "ReportIn 1 created: size: %d", sizeof(report_in));
// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL));
// // ReportIn different_rep = ReportIn();
// // ESP_LOGD("I2C", "ReportIn 2 created: size: %d", sizeof(different_rep));
// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL));
// // ReportOut report_out = ReportOut();
// // ESP_LOGD("I2C", "ReportOut created: size: %d", sizeof(report_out));
// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL));
// // i2c_initialize();
// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL));
// // while (true)
// // {
// // i2c_write_blocking(MULTI_SLAVE ? (report_in.index + 1) : 0x01, reinterpret_cast<uint8_t*>(&report_in), sizeof(ReportIn));
// // vTaskDelay(1);
// // }
// // }
// } // namespace I2CDriver
#include <array>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/timers.h>
#include <driver/gpio.h>
#include <esp_log.h>
#include "I2CDriver/I2CDriver.h"
#include "Bluepad32/Bluepad32.h"
void I2CDriver::initialize_i2c()
{
i2c_config_t conf;
std::memset(&conf, 0, sizeof(i2c_config_t));
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = GPIO_NUM_21;
conf.scl_io_num = GPIO_NUM_22;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = 400 * 1000;
i2c_param_config(I2C_NUM_0, &conf);
i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0);
}
void I2CDriver::run_task()
{
ReportIn report_in;
ReportOut report_out;
initialize_i2c();
while (true)
{
for (uint8_t i = 0; i < CONFIG_BLUEPAD32_MAX_DEVICES; ++i)
{
if (!bluepad32::connected(i))
{
vTaskDelay(1);
continue;
}
// if (bluepad32::get_report_in(i, report_in))
// {
report_in = bluepad32::get_report_in(i);
if (i2c_write_blocking(MULTI_SLAVE ? (i + 1) : 0x01, reinterpret_cast<const uint8_t*>(&report_in), sizeof(ReportIn)) != ESP_OK)
{
continue;
}
// }
vTaskDelay(1);
if (i2c_read_blocking(MULTI_SLAVE ? (i + 1) : 0x01, reinterpret_cast<uint8_t*>(&report_out), sizeof(ReportOut)) != ESP_OK)
{
continue;
}
bluepad32::set_report_out(report_out);
}
vTaskDelay(1);
}
}

View File

@@ -0,0 +1,62 @@
#ifndef _I2C_DRIVER_H_
#define _I2C_DRIVER_H_
#include <cstdint>
#include <cstring>
#include <atomic>
#include <driver/i2c.h>
#include "Reports.h"
class I2CDriver
{
public:
I2CDriver() = default;
~I2CDriver() { i2c_driver_delete(I2C_NUM_0); }
void run_task();
private:
static constexpr bool MULTI_SLAVE =
#if CONFIG_MULTI_SLAVE_MODE == 0
false;
#else
true;
#endif
void initialize_i2c();
static inline esp_err_t i2c_write_blocking(uint8_t address, const uint8_t* buffer, size_t len)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true);
i2c_master_write(cmd, buffer, len, true);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(2));
i2c_cmd_link_delete(cmd);
return ret;
}
static inline esp_err_t i2c_read_blocking(uint8_t address, uint8_t* buffer, size_t len)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_READ, true);
if (len > 1)
{
i2c_master_read(cmd, buffer, len - 1, I2C_MASTER_ACK);
}
i2c_master_read_byte(cmd, buffer + len - 1, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(2));
i2c_cmd_link_delete(cmd);
return ret;
}
};
#endif // _I2C_DRIVER_H_

View File

@@ -0,0 +1,50 @@
menu "OGXMini Options"
config RESET_PIN
int "Set reset pin"
default 15
config MULTI_SLAVE_MODE
bool "Enable multiple slave devices"
default n
config ENABLE_LED_1
bool "Enable LED 1"
default y
config LED_PIN_1
int "Set LED 1 pin"
default 15
depends on ENABLE_LED_1
config ENABLE_LED_2
bool "Enable LED 2"
default n
depends on ENABLE_LED_1
config LED_PIN_2
int "Set LED 2 pin"
default 255
depends on ENABLE_LED_2
config ENABLE_LED_3
bool "Enable LED 3"
default n
depends on ENABLE_LED_2
config LED_PIN_3
int "Set LED 3 pin"
default 255
depends on ENABLE_LED_3
config ENABLE_LED_4
bool "Enable LED 4"
default n
depends on ENABLE_LED_3
config LED_PIN_4
int "Set LED 4 pin"
default 255
depends on ENABLE_LED_4
endmenu

View File

@@ -0,0 +1,134 @@
#ifndef _REPORTS_H_
#define _REPORTS_H_
#include <cstdint>
// #include <array>
#include <cstring>
// #include "freertos/FreeRTOS.h"
// #include "freertos/semphr.h"
enum class ReportID : uint8_t { UNKNOWN = 0, SET_PAD, GET_PAD };
#pragma pack(push, 1)
struct ReportIn
{
uint8_t report_len;
uint8_t report_id;
uint8_t index;
uint8_t dpad;
uint16_t buttons;
uint8_t trigger_l;
uint8_t trigger_r;
int16_t joystick_lx;
int16_t joystick_ly;
int16_t joystick_rx;
int16_t joystick_ry;
ReportIn()
{
std::memset(this, 0, sizeof(ReportIn));
report_len = sizeof(ReportIn);
report_id = static_cast<uint8_t>(ReportID::SET_PAD);
}
};
static_assert(sizeof(ReportIn) == 16, "ReportIn is misaligned");
struct ReportOut
{
uint8_t report_len;
uint8_t report_id;
uint8_t index;
uint8_t rumble_l;
uint8_t rumble_r;
ReportOut()
{
std::memset(this, 0, sizeof(ReportOut));
report_len = sizeof(ReportOut);
report_id = static_cast<uint8_t>(ReportID::GET_PAD);
}
};
static_assert(sizeof(ReportOut) == 5, "ReportOut is misaligned");
#pragma pack(pop)
// class ReportQueue
// {
// public:
// ReportQueue()
// {
// report_in_mutex_ = xSemaphoreCreateMutex();
// report_out_mutex_ = xSemaphoreCreateMutex();
// }
// ~ReportQueue()
// {
// vSemaphoreDelete(report_in_mutex_);
// vSemaphoreDelete(report_out_mutex_);
// }
// void set_report_in(const ReportIn* report)
// {
// if (xSemaphoreTake(report_in_mutex_, portMAX_DELAY))
// {
// std::memcpy(&report_in_, report, sizeof(ReportIn));
// new_report_in_ = true;
// xSemaphoreGive(report_in_mutex_);
// }
// }
// void set_report_out(const ReportOut* report)
// {
// if (xSemaphoreTake(report_out_mutex_, portMAX_DELAY))
// {
// std::memcpy(&report_out_, report, sizeof(ReportOut));
// new_report_out_ = true;
// xSemaphoreGive(report_out_mutex_);
// }
// }
// //Return false if no new data
// bool get_report_in(ReportIn* report)
// {
// if (xSemaphoreTake(report_in_mutex_, portMAX_DELAY))
// {
// if (new_report_in_)
// {
// std::memcpy(report, &report_in_, sizeof(ReportIn));
// new_report_in_ = false;
// xSemaphoreGive(report_in_mutex_);
// return true;
// }
// xSemaphoreGive(report_in_mutex_);
// }
// return false;
// }
// //Return false if no new data
// bool get_report_out(ReportOut* report)
// {
// if (xSemaphoreTake(report_out_mutex_, portMAX_DELAY))
// {
// if (new_report_out_)
// {
// std::memcpy(report, &report_out_, sizeof(ReportOut));
// new_report_out_ = false;
// xSemaphoreGive(report_out_mutex_);
// return true;
// }
// xSemaphoreGive(report_out_mutex_);
// }
// return false;
// }
// private:
// SemaphoreHandle_t report_in_mutex_;
// SemaphoreHandle_t report_out_mutex_;
// ReportIn report_in_;
// ReportOut report_out_;
// bool new_report_in_{false};
// bool new_report_out_{false};
// };
#endif // _REPORTS_H_

View File

View File

@@ -0,0 +1,14 @@
#include "c_wrapper.h"
#include "I2CDriver/I2CDriver.h"
#include "Bluepad32/Bluepad32.h"
void run_bluepad32()
{
bluepad32::run_task();
}
void run_i2c()
{
I2CDriver i2c_driver;
i2c_driver.run_task();
}

View File

@@ -0,0 +1,17 @@
#ifndef _C_WRAPPER_H_
#define _C_WRAPPER_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void run_bluepad32();
void run_i2c();
#ifdef __cplusplus
}
#endif
#endif // _C_WRAPPER_H_

View File

@@ -0,0 +1,30 @@
#include <stdlib.h>
#include <stdint.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "sdkconfig.h"
#include "c_wrapper.h"
#define STACK_MULTIPLIER (2048 * CONFIG_BLUEPAD32_MAX_DEVICES)
void app_main(void)
{
xTaskCreatePinnedToCore(
run_bluepad32,
"bp32",
STACK_MULTIPLIER * 4,
NULL,
configMAX_PRIORITIES-6,
NULL,
0 );
xTaskCreatePinnedToCore(
run_i2c,
"i2c",
STACK_MULTIPLIER * 2,
NULL,
configMAX_PRIORITIES-8,
NULL,
1 );
}

2418
Firmware/ESP32/sdkconfig Normal file

File diff suppressed because it is too large Load Diff

View 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
#

View File

@@ -0,0 +1,20 @@
{
"configurations": [
{
"name": "Pico",
"includePath": [
"${PICO_SDK_PATH}/**"
],
"forcedInclude": [
"${PICO_SDK_PATH}/src/common/pico_base/include/pico.h",
"${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h"
],
"defines": [],
"compileCommands": "${workspaceFolder}/build/compile_commands.json",
"cStandard": "c17",
"cppStandard": "c++14",
"intelliSenseMode": "linux-gcc-arm"
}
],
"version": 4
}

120
Firmware/RP2040/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,120 @@
{
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"files.associations": {
"array": "cpp",
"any": "cpp",
"atomic": "cpp",
"barrier": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"cfenv": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"cinttypes": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"codecvt": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"coroutine": "cpp",
"csetjmp": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cuchar": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"forward_list": "cpp",
"list": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"expected": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"netfwd": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"regex": "cpp",
"source_location": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"rope": "cpp",
"slist": "cpp",
"format": "cpp",
"fstream": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"latch": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"ranges": "cpp",
"scoped_allocator": "cpp",
"semaphore": "cpp",
"shared_mutex": "cpp",
"span": "cpp",
"spanstream": "cpp",
"sstream": "cpp",
"stacktrace": "cpp",
"stdexcept": "cpp",
"stdfloat": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"syncstream": "cpp",
"thread": "cpp",
"typeindex": "cpp",
"typeinfo": "cpp",
"valarray": "cpp",
"variant": "cpp",
"xlocmon": "cpp",
"xtr1common": "cpp",
"xutility": "cpp",
"ios": "cpp",
"locale": "cpp",
"queue": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstring": "cpp",
"filesystem": "cpp",
"flash.h": "c",
"uart_bridge.h": "c",
"usbh.h": "c"
}
}

View File

@@ -0,0 +1,297 @@
cmake_minimum_required(VERSION 3.13)
set(FW_NAME "OGX-Mini")
set(FW_VERSION "v1.0.0-alpha")
set(CMAKE_C_COMPILER ${C_COMPILER})
set(CMAKE_CXX_COMPILER ${CXX_COMPILER})
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(SRC ${CMAKE_CURRENT_LIST_DIR}/src)
set(EXTERNAL_DIR ${CMAKE_CURRENT_LIST_DIR}/../external)
include(${EXTERNAL_DIR}/patch_libs.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/cmake/get_pico_sdk.cmake)
apply_lib_patches(${EXTERNAL_DIR})
get_pico_sdk(${EXTERNAL_DIR})
include(${PICO_SDK_PATH}/pico_sdk_init.cmake)
message("PICO_SDK_VERSION_STRING: ${PICO_SDK_VERSION_STRING}")
if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()
project(${FW_NAME} C CXX ASM)
set(PICO_PIO_USB_PATH ${EXTERNAL_DIR}/Pico-PIO-USB)
set(PICO_TINYUSB_PATH ${EXTERNAL_DIR}/tinyusb)
set(BLUEPAD32_ROOT ${EXTERNAL_DIR}/bluepad32)
set(BTSTACK_ROOT ${BLUEPAD32_ROOT}/external/btstack)
set(PICO_BTSTACK_PATH ${BTSTACK_ROOT})
set(SOURCES_BOARD
${SRC}/main.cpp
${SRC}/OGXMini/OGXMini_Standard.cpp
${SRC}/OGXMini/OGXMini_4Ch.cpp
${SRC}/OGXMini/OGXMini_PicoW.cpp
${SRC}/OGXMini/OGXMini_ESP32.cpp
${SRC}/Board/board_api.cpp
${SRC}/UserSettings/UserSettings.cpp
${SRC}/UserSettings/UserProfile.cpp
${SRC}/USBDevice/tud_callbacks.cpp
${SRC}/USBDevice/DeviceManager.cpp
${SRC}/USBDevice/DeviceDriver/DeviceDriver.cpp
${SRC}/USBDevice/DeviceDriver/PSClassic/PSClassic.cpp
${SRC}/USBDevice/DeviceDriver/PS3/PS3.cpp
${SRC}/USBDevice/DeviceDriver/Switch/Switch.cpp
${SRC}/USBDevice/DeviceDriver/XInput/XInput.cpp
${SRC}/USBDevice/DeviceDriver/XboxOG/XboxOG_GP.cpp
${SRC}/USBDevice/DeviceDriver/XboxOG/XboxOG_SB.cpp
${SRC}/USBDevice/DeviceDriver/XboxOG/XboxOG_XR.cpp
${SRC}/USBDevice/DeviceDriver/DInput/DInput.cpp
${SRC}/USBDevice/DeviceDriver/WebApp/WebApp.cpp
${SRC}/USBDevice/DeviceDriver/XInput/tud_xinput/tud_xinput.cpp
${SRC}/USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.cpp
)
set(LIBS_BOARD
# Base
pico_stdlib
pico_multicore
pico_rand
hardware_timer
hardware_clocks
hardware_flash
tinyusb_device
tinyusb_board
# UART
hardware_uart
hardware_irq
)
set(INC_DIRS_BOARD
)
# Config options
# Max gamepads
set(MAX_GAMEPADS 1 CACHE STRING "Set number of gamepads, 1 to 4")
if (MAX_GAMEPADS GREATER 4 OR MAX_GAMEPADS LESS 1)
message(FATAL_ERROR "MAX_GAMEPADS must be between 1 and 4")
endif()
add_definitions(-DMAX_GAMEPADS=${MAX_GAMEPADS})
# Board type
set(OGXM_BOARD "PI_PICO" CACHE STRING "Set board type, options can be found in src/board_config.h")
set(FLASH_SIZE_MB 2)
set(PICO_BOARD none)
if (OGXM_BOARD STREQUAL "PI_PICO")
set(EN_USB_HOST TRUE)
elseif(OGXM_BOARD STREQUAL "ADA_FEATHER")
set(EN_USB_HOST TRUE)
set(EN_RGB TRUE)
set(FLASH_SIZE_MB 8)
elseif(OGXM_BOARD STREQUAL "RP_ZERO")
set(EN_USB_HOST TRUE)
set(EN_RGB TRUE)
elseif(OGXM_BOARD STREQUAL "INTERNAL_4CH")
set(EN_USB_HOST TRUE)
set(EN_4CH TRUE)
elseif(OGXM_BOARD STREQUAL "EXTERNAL_4CH")
set(EN_USB_HOST TRUE)
set(EN_4CH TRUE)
set(EN_RGB TRUE)
elseif(OGXM_BOARD STREQUAL "PI_PICOW")
set(EN_BLUETOOTH TRUE)
set(PICO_BOARD pico_w)
elseif(OGXM_BOARD STREQUAL "W_ESP32")
set(EN_ESP32 TRUE)
set(EN_UART_BRIDGE TRUE)
else()
message(FATAL_ERROR "Invalid OGXM_BOARD value. See options in src/board_config.h")
endif()
add_definitions(-DOGXM_BOARD=${OGXM_BOARD})
if(EN_USB_HOST)
message(STATUS "USB host enabled.")
add_compile_definitions(CONFIG_EN_USB_HOST=1)
list(APPEND SOURCES_BOARD
${SRC}/USBHost/tuh_callbacks.cpp
${SRC}/USBHost/HostDriver/DInput/DInput.cpp
${SRC}/USBHost/HostDriver/PSClassic/PSClassic.cpp
${SRC}/USBHost/HostDriver/SwitchWired/SwitchWired.cpp
${SRC}/USBHost/HostDriver/SwitchPro/SwitchPro.cpp
${SRC}/USBHost/HostDriver/PS5/PS5.cpp
${SRC}/USBHost/HostDriver/PS4/PS4.cpp
${SRC}/USBHost/HostDriver/PS3/PS3.cpp
${SRC}/USBHost/HostDriver/XInput/XboxOG.cpp
${SRC}/USBHost/HostDriver/XInput/XboxOne.cpp
${SRC}/USBHost/HostDriver/XInput/Xbox360.cpp
${SRC}/USBHost/HostDriver/XInput/Xbox360W.cpp
${SRC}/USBHost/HostDriver/N64/N64.cpp
${SRC}/USBHost/HostDriver/XInput/tuh_xinput/tuh_xinput.cpp
${SRC}/USBHost/HostDriver/HIDGeneric/HIDGeneric.cpp
${SRC}/USBHost/HostDriver/tuh_uni/tuh_uni.cpp
${SRC}/USBHost/HIDParser/HIDJoystick.cpp
${SRC}/USBHost/HIDParser/HIDReportDescriptor.cpp
${SRC}/USBHost/HIDParser/HIDReportDescriptorElements.cpp
${SRC}/USBHost/HIDParser/HIDReportDescriptorUsages.cpp
${SRC}/USBHost/HIDParser/HIDUtils.cpp
)
list(APPEND LIBS_BOARD
tinyusb_host
tinyusb_pico_pio_usb
)
endif()
if(EN_BLUETOOTH)
add_compile_definitions(CONFIG_EN_BLUETOOTH=1)
message(STATUS "Bluetooth enabled.")
list(APPEND SOURCES_BOARD
${SRC}/Bluepad32/Bluepad32.cpp
)
list(APPEND INC_DIRS_BOARD
${SRC}
${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/include
${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/include
)
list(APPEND LIBS_BOARD
pico_cyw43_arch_none
pico_btstack_classic
pico_btstack_cyw43
bluepad32
)
endif()
if(EN_RGB)
add_compile_definitions(CONFIG_EN_RGB=1)
message(STATUS "RGB enabled.")
list(APPEND SOURCES_BOARD
${SRC}/Board/Pico_WS2812/WS2812.cpp
)
list(APPEND LIBS_BOARD
hardware_pio
)
endif()
if(EN_4CH)
add_compile_definitions(CONFIG_EN_4CH=1)
message(STATUS "4CH enabled.")
list(APPEND SOURCES_BOARD
${SRC}/I2CDriver/i2c_driver_4ch.cpp
)
list(APPEND LIBS_BOARD
hardware_i2c
pico_i2c_slave
)
endif()
if(EN_ESP32)
add_compile_definitions(CONFIG_EN_ESP32=1)
message(STATUS "ESP32 enabled.")
list(APPEND SOURCES_BOARD
${SRC}/I2CDriver/i2c_driver_esp32.cpp
)
list(APPEND LIBS_BOARD
hardware_i2c
pico_i2c_slave
)
endif()
if(EN_UART_BRIDGE)
add_compile_definitions(CONFIG_EN_UART_BRIDGE=1)
message(STATUS "UART bridge enabled.")
list(APPEND SOURCES_BOARD
${SRC}/USBDevice/DeviceDriver/UARTBridge/UARTBridge.cpp
${SRC}/USBDevice/DeviceDriver/UARTBridge/uart_bridge/uart_bridge.c
)
endif()
add_compile_definitions(PICO_FLASH_SIZE_BYTES=${FLASH_SIZE_MB}*1024*1024)
add_compile_definitions(FIRMWARE_VERSION="${FW_VERSION}")
# Check for DVD dongle firmware
if(EXISTS ${SRC}/USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid_xremote_rom.h)
message(STATUS "XRemote ROM available.")
add_definitions(-DXREMOTE_ROM_AVAILABLE)
endif()
if(NOT EN_BLUETOOTH)
add_compile_definitions(PICO_BOOT_STAGE2_CHOOSE_GENERIC_03H=1 PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64)
endif()
pico_sdk_init()
add_executable(${FW_NAME} ${SOURCES_BOARD})
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
pico_enable_stdio_uart(${FW_NAME} 1)
add_compile_definitions(LOG=2)
target_compile_options(${FW_NAME} PRIVATE
-Wall # Enable most warnings
-Wextra # Enable extra warnings
-Wconversion # Warn on type conversion issues
-Wsign-conversion # Warn on sign conversion issues
# -Werror # Treat warnings as errors
)
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
add_compile_options(
-O2 # Optimize for speed
-mcpu=cortex-m0plus # Target ARM Cortex-M0+
-mthumb # Use Thumb instruction set
-ffunction-sections # Place each function in its own section
-fdata-sections # Place each data item in its own section
-fomit-frame-pointer # Omit frame pointers
-finline-functions # Enable inlining
)
add_link_options(
-Wl,--gc-sections # Remove unused sections
-flto # Link-Time Optimization
)
endif()
pico_set_program_name(${FW_NAME} ${FW_NAME})
pico_set_program_version(${FW_NAME} ${FW_VERSION})
target_include_directories(${FW_NAME} PRIVATE ${SRC})
if(EN_RGB)
pico_generate_pio_header(${FW_NAME} ${SRC}/Board/Pico_WS2812/WS2812.pio)
endif()
include_directories(${INC_DIRS_BOARD})
if(EN_USB_HOST)
add_subdirectory(${PICO_PIO_USB_PATH} ${CMAKE_BINARY_DIR}/Pico-PIO-USB)
endif()
if(EN_BLUETOOTH)
add_subdirectory(${BLUEPAD32_ROOT}/src/components/bluepad32 libbluepad32)
endif()
target_link_libraries(${FW_NAME} PRIVATE ${LIBS_BOARD})
set(EXE_FILENAME "${FW_NAME}-${FW_VERSION}-${OGXM_BOARD}")
set_target_properties(${FW_NAME} PROPERTIES OUTPUT_NAME ${EXE_FILENAME})
pico_add_extra_outputs(${FW_NAME})

View File

@@ -0,0 +1,17 @@
function(get_pico_sdk EXTERNAL_DIR)
if(NOT DEFINED ENV{PICO_SDK_PATH})
message("PICO_SDK_PATH not set, downloading Pico SDK...")
set(PICO_SDK_PATH ${EXTERNAL_DIR}/pico-sdk PARENT_SCOPE)
if(NOT EXISTS ${PICO_SDK_PATH})
execute_process(
COMMAND git clone --recursive https://github.com/raspberrypi/pico-sdk.git
WORKING_DIRECTORY ${EXTERNAL_DIR}
)
endif()
else()
message("Using PICO_SDK_PATH from environment: $ENV{PICO_SDK_PATH}")
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH} PARENT_SCOPE)
endif()
set(PICOTOOL_FETCH_FROM_GIT_PATH ${EXTERNAL_DIR}/picotool PARENT_SCOPE)
endfunction()

View File

@@ -0,0 +1,325 @@
#include <atomic>
#include <cstring>
#include <functional>
#include <pico/mutex.h>
#include <pico/cyw43_arch.h>
#include "btstack_run_loop.h"
#include "uni.h"
#include "sdkconfig.h"
#include "Bluepad32/Bluepad32.h"
#include "Board/board_api.h"
#ifndef CONFIG_BLUEPAD32_PLATFORM_CUSTOM
#error "Pico W must use BLUEPAD32_PLATFORM_CUSTOM"
#endif
static_assert((CONFIG_BLUEPAD32_MAX_DEVICES == MAX_GAMEPADS), "Mismatch between BP32 and Gamepad max devices");
namespace bluepad32 {
static constexpr uint32_t FEEDBACK_TIME_MS = 200;
static constexpr uint32_t LED_CHECK_TIME_MS = 500;
struct Device
{
bool connected{false};
Gamepad* gamepad{nullptr};
};
std::array<Device, MAX_GAMEPADS> devices_;
//This solves a null function pointer issue with bluepad32, device->report_parser.play_dual_rumble() becomes null before the disconnect callback
void set_rumble(uni_hid_device_t* bp_device, uint16_t length, uint8_t rumble_l, uint8_t rumble_r)
{
if (!bp_device || !bp_device->report_parser.play_dual_rumble)
{
return;
}
switch (bp_device->controller_type)
{
case CONTROLLER_TYPE_XBoxOneController:
uni_hid_parser_xboxone_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
break;
case CONTROLLER_TYPE_AndroidController:
if (bp_device->vendor_id == UNI_HID_PARSER_STADIA_VID && bp_device->product_id == UNI_HID_PARSER_STADIA_PID)
{
uni_hid_parser_stadia_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
}
break;
case CONTROLLER_TYPE_PSMoveController:
uni_hid_parser_psmove_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
break;
case CONTROLLER_TYPE_PS3Controller:
uni_hid_parser_ds3_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
break;
case CONTROLLER_TYPE_PS4Controller:
uni_hid_parser_ds4_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
break;
case CONTROLLER_TYPE_PS5Controller:
uni_hid_parser_ds5_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
break;
case CONTROLLER_TYPE_WiiController:
uni_hid_parser_wii_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
break;
case CONTROLLER_TYPE_SwitchProController:
case CONTROLLER_TYPE_SwitchJoyConRight:
case CONTROLLER_TYPE_SwitchJoyConLeft:
uni_hid_parser_switch_play_dual_rumble(bp_device, 0, length, rumble_l, rumble_r);
break;
default:
break;
}
}
static void send_feedback_cb(btstack_timer_source *ts)
{
uni_hid_device_t* bp_device = nullptr;
for (uint8_t i = 0; i < devices_.size(); ++i)
{
if (!devices_[i].connected ||
!(bp_device = uni_hid_device_get_instance_for_idx(i)))
{
continue;
}
uint8_t rumble_l = devices_[i].gamepad->get_rumble_l().uint8();
uint8_t rumble_r = devices_[i].gamepad->get_rumble_r().uint8();
if (rumble_l > 0 || rumble_r > 0)
{
// bp_device->report_parser.play_dual_rumble(bp_device, 0, static_cast<uint16_t>(FEEDBACK_TIME_MS), rumble_l, rumble_r);
set_rumble(bp_device, static_cast<uint16_t>(FEEDBACK_TIME_MS), rumble_l, rumble_r);
}
}
btstack_run_loop_set_timer(ts, FEEDBACK_TIME_MS);
btstack_run_loop_add_timer(ts);
}
static void check_led_cb(btstack_timer_source *ts)
{
static bool led_state = false;
led_state = !led_state;
board_api::set_led(any_connected() ? true : led_state);
btstack_run_loop_set_timer(ts, LED_CHECK_TIME_MS);
btstack_run_loop_add_timer(ts);
}
//BT Driver
static void init(int argc, const char** arg_V)
{
}
static void init_complete_cb(void)
{
uni_bt_enable_new_connections_unsafe(true);
// Based on runtime condition, you can delete or list the stored BT keys.
if (1)
uni_bt_del_keys_unsafe();
else
uni_bt_list_keys_unsafe();
uni_property_dump_all();
}
static uni_error_t device_discovered_cb(bd_addr_t addr, const char* name, uint16_t cod, uint8_t rssi)
{
if (!((cod & UNI_BT_COD_MINOR_MASK) & UNI_BT_COD_MINOR_GAMEPAD))
{
return UNI_ERROR_IGNORE_DEVICE;
}
return UNI_ERROR_SUCCESS;
}
static void device_connected_cb(uni_hid_device_t* device)
{
}
static void device_disconnected_cb(uni_hid_device_t* device)
{
int idx = uni_hid_device_get_idx_for_instance(device);
if (idx >= MAX_GAMEPADS || idx < 0)
{
return;
}
devices_[idx].connected = false;
devices_[idx].gamepad->reset_pad();
}
static uni_error_t device_ready_cb(uni_hid_device_t* device)
{
int idx = uni_hid_device_get_idx_for_instance(device);
if (idx >= MAX_GAMEPADS || idx < 0)
{
return UNI_ERROR_SUCCESS;
}
devices_[idx].connected = true;
return UNI_ERROR_SUCCESS;
}
static void oob_event_cb(uni_platform_oob_event_t event, void* data)
{
return;
}
static void controller_data_cb(uni_hid_device_t* device, uni_controller_t* controller)
{
static uni_gamepad_t prev_uni_gp[MAX_GAMEPADS] = {};
if (controller->klass != UNI_CONTROLLER_CLASS_GAMEPAD)
{
return;
}
uni_gamepad_t *uni_gp = &controller->gamepad;
int idx = uni_hid_device_get_idx_for_instance(device);
if (idx >= MAX_GAMEPADS || idx < 0 || std::memcmp(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t)) == 0)
{
return;
}
Gamepad* gamepad = devices_[idx].gamepad;
gamepad->reset_pad();
switch (uni_gp->dpad)
{
case DPAD_UP:
gamepad->set_dpad_up();
break;
case DPAD_DOWN:
gamepad->set_dpad_down();
break;
case DPAD_LEFT:
gamepad->set_dpad_left();
break;
case DPAD_RIGHT:
gamepad->set_dpad_right();
break;
case DPAD_UP | DPAD_RIGHT:
gamepad->set_dpad_up_right();
break;
case DPAD_DOWN | DPAD_RIGHT:
gamepad->set_dpad_down_right();
break;
case DPAD_DOWN | DPAD_LEFT:
gamepad->set_dpad_down_left();
break;
case DPAD_UP | DPAD_LEFT:
gamepad->set_dpad_up_left();
break;
default:
break;
}
if (uni_gp->buttons & BUTTON_A) gamepad->set_button_a();
if (uni_gp->buttons & BUTTON_B) gamepad->set_button_b();
if (uni_gp->buttons & BUTTON_X) gamepad->set_button_x();
if (uni_gp->buttons & BUTTON_Y) gamepad->set_button_y();
if (uni_gp->buttons & BUTTON_SHOULDER_L) gamepad->set_button_lb();
if (uni_gp->buttons & BUTTON_SHOULDER_R) gamepad->set_button_rb();
if (uni_gp->buttons & BUTTON_THUMB_L) gamepad->set_button_l3();
if (uni_gp->buttons & BUTTON_THUMB_R) gamepad->set_button_r3();
if (uni_gp->misc_buttons & MISC_BUTTON_BACK) gamepad->set_button_back();
if (uni_gp->misc_buttons & MISC_BUTTON_START) gamepad->set_button_start();
if (uni_gp->misc_buttons & MISC_BUTTON_SYSTEM) gamepad->set_button_sys();
if (uni_gp->misc_buttons & MISC_BUTTON_CAPTURE) gamepad->set_button_misc();
gamepad->set_trigger_l_uint10(uni_gp->brake);
gamepad->set_trigger_r_uint10(uni_gp->throttle);
gamepad->set_joystick_lx_int10(uni_gp->axis_x);
gamepad->set_joystick_ly_int10(uni_gp->axis_y);
gamepad->set_joystick_rx_int10(uni_gp->axis_rx);
gamepad->set_joystick_ry_int10(uni_gp->axis_ry);
std::memcpy(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t));
}
const uni_property_t* get_property_cb(uni_property_idx_t idx)
{
return nullptr;
}
uni_platform* get_driver()
{
static uni_platform driver =
{
.name = "OGXMiniW",
.init = init,
.on_init_complete = init_complete_cb,
.on_device_discovered = device_discovered_cb,
.on_device_connected = device_connected_cb,
.on_device_disconnected = device_disconnected_cb,
.on_device_ready = device_ready_cb,
.on_controller_data = controller_data_cb,
.get_property = get_property_cb,
.on_oob_event = oob_event_cb,
};
return &driver;
}
//Public API
void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepads)
{
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
{
devices_[i].gamepad = &gamepads[i];
}
}
void run_loop()
{
uni_platform_set_custom(get_driver());
uni_init(0, nullptr);
btstack_timer_source_t feedback_timer;
feedback_timer.process = send_feedback_cb;
feedback_timer.context = nullptr;
btstack_run_loop_set_timer(&feedback_timer, FEEDBACK_TIME_MS);
btstack_run_loop_add_timer(&feedback_timer);
btstack_timer_source_t led_timer;
led_timer.process = check_led_cb;
led_timer.context = nullptr;
btstack_run_loop_set_timer(&led_timer, LED_CHECK_TIME_MS);
btstack_run_loop_add_timer(&led_timer);
btstack_run_loop_execute();
}
std::array<bool, MAX_GAMEPADS> get_connected_map()
{
std::array<bool, MAX_GAMEPADS> mounted_map;
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
{
mounted_map[i] = devices_[i].connected;
}
return mounted_map;
}
bool any_connected()
{
for (auto& device : devices_)
{
if (device.connected)
{
return true;
}
}
return false;
}
} // namespace bluepad32

View File

@@ -0,0 +1,23 @@
#ifndef _BLUEPAD_32_H_
#define _BLUEPAD_32_H_
#include <cstdint>
#include <array>
#include "Gamepad.h"
#include "board_config.h"
#include "UserSettings/UserProfile.h"
/* NOTE: Everything bluepad32/uni needs to be wrapped
and kept away from tinyusb due to naming conflicts */
namespace bluepad32 {
void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepads);
void run_loop();
std::array<bool, MAX_GAMEPADS> get_connected_map();
bool any_connected();
} //namespace bluepad32
#endif // _BLUEPAD_32_H_

View File

@@ -0,0 +1,122 @@
#include "Board/Pico_WS2812/WS2812.hpp"
#include "WS2812.pio.h"
//#define DEBUG
#ifdef DEBUG
#include <stdio.h>
#endif
WS2812::WS2812(uint pin, uint length, PIO pio, uint sm) {
initialize(pin, length, pio, sm, NONE, GREEN, RED, BLUE);
}
WS2812::WS2812(uint pin, uint length, PIO pio, uint sm, DataFormat format) {
switch (format) {
case FORMAT_RGB:
initialize(pin, length, pio, sm, NONE, RED, GREEN, BLUE);
break;
case FORMAT_GRB:
initialize(pin, length, pio, sm, NONE, GREEN, RED, BLUE);
break;
case FORMAT_WRGB:
initialize(pin, length, pio, sm, WHITE, RED, GREEN, BLUE);
break;
}
}
WS2812::WS2812(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3) {
initialize(pin, length, pio, sm, b1, b1, b2, b3);
}
WS2812::WS2812(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3, DataByte b4) {
initialize(pin, length, pio, sm, b1, b2, b3, b4);
}
WS2812::~WS2812() {
}
void WS2812::initialize(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3, DataByte b4) {
this->pin = pin;
this->length = length;
this->pio = pio;
this->sm = sm;
this->data.fill(0);
this->bytes[0] = b1;
this->bytes[1] = b2;
this->bytes[2] = b3;
this->bytes[3] = b4;
uint offset = pio_add_program(pio, &ws2812_program);
uint bits = (b1 == NONE ? 24 : 32);
#ifdef DEBUG
printf("WS2812 / Initializing SM %u with offset %X at pin %u and %u data bits...\n", sm, offset, pin, bits);
#endif
ws2812_program_init(pio, sm, offset, pin, 800000, bits);
}
uint32_t WS2812::convertData(uint32_t rgbw) {
uint32_t result = 0;
for (uint b = 0; b < 4; b++) {
switch (bytes[b]) {
case RED:
result |= (rgbw & 0xFF);
break;
case GREEN:
result |= (rgbw & 0xFF00) >> 8;
break;
case BLUE:
result |= (rgbw & 0xFF0000) >> 16;
break;
case WHITE:
result |= (rgbw & 0xFF000000) >> 24;
break;
}
result <<= 8;
}
return result;
}
void WS2812::setPixelColor(uint index, uint32_t color) {
if (index < length) {
data[index] = convertData(color);
}
}
void WS2812::setPixelColor(uint index, uint8_t red, uint8_t green, uint8_t blue) {
setPixelColor(index, RGB(red, green, blue));
}
void WS2812::setPixelColor(uint index, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) {
setPixelColor(index, RGBW(red, green, blue, white));
}
void WS2812::fill(uint32_t color) {
fill(color, 0, length);
}
void WS2812::fill(uint32_t color, uint first) {
fill(color, first, length-first);
}
void WS2812::fill(uint32_t color, uint first, uint count) {
uint last = (first + count);
if (last > length) {
last = length;
}
color = convertData(color);
for (uint i = first; i < last; i++) {
data[i] = color;
}
}
void WS2812::show() {
#ifdef DEBUG
for (uint i = 0; i < length; i++) {
printf("WS2812 / Put data: %08X\n", data[i]);
}
#endif
for (uint i = 0; i < length; i++) {
pio_sm_put_blocking(pio, sm, data[i]);
}
}

View File

@@ -0,0 +1,58 @@
#ifndef WS2812_H
#define WS2812_H
#include <array>
#include <pico/types.h>
#include <hardware/pio.h>
class WS2812 {
public:
enum DataByte {
NONE=0,
RED=1,
GREEN=2,
BLUE=3,
WHITE=4
};
enum DataFormat {
FORMAT_RGB=0,
FORMAT_GRB=1,
FORMAT_WRGB=2
};
WS2812(uint pin, uint length, PIO pio, uint sm);
WS2812(uint pin, uint length, PIO pio, uint sm, DataFormat format);
WS2812(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3);
WS2812(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3, DataByte b4);
~WS2812();
static uint32_t RGB(uint8_t red, uint8_t green, uint8_t blue) {
return (uint32_t)(blue) << 16 | (uint32_t)(green) << 8 | (uint32_t)(red);
};
static uint32_t RGBW(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) {
return (uint32_t)(white) << 24 | (uint32_t)(blue) << 16 | (uint32_t)(green) << 8 | (uint32_t)(red);
}
void setPixelColor(uint index, uint32_t color);
void setPixelColor(uint index, uint8_t red, uint8_t green, uint8_t blue);
void setPixelColor(uint index, uint8_t red, uint8_t green, uint8_t blue, uint8_t white);
void fill(uint32_t color);
void fill(uint32_t color, uint first);
void fill(uint32_t color, uint first, uint count);
void show();
private:
uint pin;
uint length;
PIO pio;
uint sm;
DataByte bytes[4];
std::array<uint32_t, 20> data;
void initialize(uint pin, uint length, PIO pio, uint sm, DataByte b1, DataByte b2, DataByte b3, DataByte b4);
uint32_t convertData(uint32_t rgbw);
};
#endif

View 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);
}
%}

View File

@@ -0,0 +1,189 @@
#include <pico/stdlib.h>
#include <pico/mutex.h>
#include <hardware/gpio.h>
#include <hardware/watchdog.h>
#include "Board/board_api.h"
#include "board_config.h"
#if defined(CONFIG_EN_BLUETOOTH)
#include <pico/cyw43_arch.h>
#endif // defined(CONFIG_EN_BLUETOOTH)
#if defined(CONFIG_EN_RGB)
#include "WS2812.pio.h"
#include "Board/Pico_WS2812/WS2812.hpp"
#endif // defined(CONFIG_EN_RGB)
namespace board_api {
bool inited_ = false;
mutex_t gpio_mutex_;
#if defined(CONFIG_EN_RGB)
WS2812 ws2812_ = WS2812(RGB_PXL_PIN, 1, pio1, 0, WS2812::FORMAT_GRB);
#endif // defined(CONFIG_EN_RGB)
void init_gpio()
{
if (inited_)
{
return;
}
if (!mutex_is_initialized(&gpio_mutex_))
{
mutex_init(&gpio_mutex_);
}
mutex_enter_blocking(&gpio_mutex_);
#ifdef VCC_EN_PIN
gpio_init(VCC_EN_PIN);
gpio_set_dir(VCC_EN_PIN, GPIO_OUT);
gpio_put(VCC_EN_PIN, 1);
#endif
#if defined(CONFIG_EN_RGB)
#ifdef RGB_PWR_PIN
gpio_init(RGB_PWR_PIN);
gpio_set_dir(RGB_PWR_PIN, GPIO_OUT);
gpio_put(RGB_PWR_PIN, 1);
#endif
set_led(false);
#elif defined(CONFIG_EN_BLUETOOTH)
//
#elif defined(LED_INDICATOR_PIN)
gpio_init(LED_INDICATOR_PIN);
gpio_set_dir(LED_INDICATOR_PIN, GPIO_OUT);
gpio_put(LED_INDICATOR_PIN, 0);
#endif
#if defined(CONFIG_EN_ESP32)
gpio_init(ESP_PROG_PIN);
gpio_set_dir(ESP_PROG_PIN, GPIO_OUT);
gpio_put(ESP_PROG_PIN, 1);
gpio_init(ESP_RST_PIN);
gpio_set_dir(ESP_RST_PIN, GPIO_OUT);
gpio_put(ESP_RST_PIN, 1);
#endif //defined(CONFIG_EN_ESP32)
#if defined(CONFIG_EN_UART_BRIDGE)
gpio_init(MODE_SEL_PIN);
gpio_set_dir(MODE_SEL_PIN, GPIO_IN);
gpio_pull_up(MODE_SEL_PIN);
#endif // defined(CONFIG_EN_UART_BRIDGE)
inited_ = true;
mutex_exit(&gpio_mutex_);
}
void set_led(bool state)
{
if (!inited_)
{
return;
}
mutex_enter_blocking(&gpio_mutex_);
#if defined(CONFIG_EN_RGB)
ws2812_.setPixelColor(0, state ? WS2812::RGB(0x00, 0xFF, 0x00) : WS2812::RGB(0xFF, 0, 0));
ws2812_.show();
#elif defined(CONFIG_EN_BLUETOOTH)
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, state ? 1 : 0);
#elif defined(LED_INDICATOR_PIN)
gpio_put(LED_INDICATOR_PIN, state ? 1 : 0);
#endif //defined(CONFIG_EN_RGB)
mutex_exit(&gpio_mutex_);
}
#if defined(CONFIG_EN_UART_BRIDGE)
bool uart_bridge_mode()
{
if (!inited_)
{
return false;
}
bool mode = false;
mutex_enter_blocking(&gpio_mutex_);
gpio_pull_up(MODE_SEL_PIN);
if (gpio_get(MODE_SEL_PIN) == 0)
{
mode = true;
}
mutex_exit(&gpio_mutex_);
return mode;
}
#endif // defined(CONFIG_EN_UART_BRIDGE)
#if defined(CONFIG_EN_ESP32)
void reset_esp32_unsafe()
{
gpio_put(ESP_RST_PIN, 0);
sleep_ms(500);
gpio_put(ESP_RST_PIN, 1);
sleep_ms(250);
}
void reset_esp32()
{
if (!inited_)
{
return;
}
mutex_enter_blocking(&gpio_mutex_);
reset_esp32_unsafe();
mutex_exit(&gpio_mutex_);
}
void enter_esp32_prog_mode()
{
if (!inited_)
{
return;
}
mutex_enter_blocking(&gpio_mutex_);
gpio_put(ESP_PROG_PIN, 1);
sleep_ms(250);
gpio_put(ESP_PROG_PIN, 0);
sleep_ms(250);
reset_esp32_unsafe();
gpio_put(ESP_PROG_PIN, 1);
mutex_exit(&gpio_mutex_);
}
#endif // defined(CONFIG_EN_ESP32)
void reboot()
{
#define AIRCR_REG (*((volatile uint32_t *)(0xE000ED0C)))
#define AIRCR_SYSRESETREQ (1 << 2)
#define AIRCR_VECTKEY (0x5FA << 16)
AIRCR_REG = AIRCR_VECTKEY | AIRCR_SYSRESETREQ;
while(1);
// watchdog_enable(500, true); // 10-second timeout
// while (true)
// {
// // Your main loop logic
// watchdog_update(); // Reset the watchdog timer
// }
}
} // namespace board_api

View File

@@ -0,0 +1,17 @@
#ifndef _BOARD_API_H_
#define _BOARD_API_H_
#include <cstdint>
namespace board_api
{
void init_gpio();
void set_led(bool state);
void reboot();
bool uart_bridge_mode();
void reset_esp32();
void enter_esp32_prog_mode();
}
#endif // _BOARD_API_H_

View File

@@ -0,0 +1,81 @@
#ifndef _CDC_DEV_DESCRIPTORS_H_
#define _CDC_DEV_DESCRIPTORS_H_
#include <cstdint>
#include <cstring>
#include "tusb.h"
namespace CDCDesc
{
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
// #define USB_VID 0xCafe
// #define USB_BCD 0x0200
static const tusb_desc_device_t DEVICE_DESCRIPTORS =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
enum Itf
{
NUM_CDC = 0,
NUM_CDC_DATA,
NUM_TOTAL
};
#define EPNUM_CDC_NOTIF 0x81
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
static constexpr int CONFIG_LEN = (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN);
static const uint8_t CONFIGURATION_DESCRIPTORS[] =
{
TUD_CONFIG_DESCRIPTOR(1, Itf::NUM_TOTAL, 0, CONFIG_LEN, 0x00, 500),
TUD_CDC_DESCRIPTOR(Itf::NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
};
enum
{
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
};
static const char *STRING_DESCRIPTORS[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
NULL, // 3: Serials will use unique ID if possible
"TinyUSB CDC", // 4: CDC Interface
};
static uint16_t _desc_str[32 + 1];
}; // namespace CDCDesc
#endif // _CDC_DEV_DESCRIPTORS_H_

View File

@@ -0,0 +1,297 @@
#ifndef _DINPUT_DESCRIPTORS_H_
#define _DINPUT_DESCRIPTORS_H_
#include <cstdint>
#include <cstring>
#include "tusb.h"
#include "board_config.h"
namespace DInput
{
static constexpr uint8_t DPAD_MASK = 0x0F;
static constexpr uint8_t AXIS_MIN = 0x00;
static constexpr uint8_t AXIS_MID = 0x80;
static constexpr uint8_t AXIS_MAX = 0xFF;
namespace Buttons0
{
static constexpr uint8_t SQUARE = 0x01;
static constexpr uint8_t CROSS = 0x02;
static constexpr uint8_t CIRCLE = 0x04;
static constexpr uint8_t TRIANGLE = 0x08;
static constexpr uint8_t L1 = 0x10;
static constexpr uint8_t R1 = 0x20;
static constexpr uint8_t L2 = 0x40;
static constexpr uint8_t R2 = 0x80;
};
namespace Buttons1
{
static constexpr uint8_t SELECT = 0x01;
static constexpr uint8_t START = 0x02;
static constexpr uint8_t L3 = 0x04;
static constexpr uint8_t R3 = 0x08;
static constexpr uint8_t PS = 0x10;
static constexpr uint8_t TP = 0x20;
};
namespace DPad
{
static constexpr uint8_t UP = 0x00;
static constexpr uint8_t UP_RIGHT = 0x01;
static constexpr uint8_t RIGHT = 0x02;
static constexpr uint8_t DOWN_RIGHT = 0x03;
static constexpr uint8_t DOWN = 0x04;
static constexpr uint8_t DOWN_LEFT = 0x05;
static constexpr uint8_t LEFT = 0x06;
static constexpr uint8_t UP_LEFT = 0x07;
static constexpr uint8_t CENTER = 0x08;
};
#pragma pack(push, 1)
struct InReport
{
uint8_t buttons[2];
uint8_t dpad;
uint8_t joystick_lx;
uint8_t joystick_ly;
uint8_t joystick_rx;
uint8_t joystick_ry;
uint8_t right_axis;
uint8_t left_axis;
uint8_t up_axis;
uint8_t down_axis;
uint8_t triangle_axis;
uint8_t circle_axis;
uint8_t cross_axis;
uint8_t square_axis;
uint8_t l1_axis;
uint8_t r1_axis;
uint8_t l2_axis;
uint8_t r2_axis;
};
#pragma pack(pop)
static_assert(sizeof(InReport) == 19, "DInput::InReport is misaligned");
static const uint8_t STRING_DESC_LANGUAGE[] = { 0x09, 0x04 };
static const uint8_t STRING_MANUFACTURER[] = "SHANWAN";
static const uint8_t STRING_PRODUCT[] = "2In1 USB Joystick";
static const uint8_t STRING_VERSION[] = "1.0";
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
{
STRING_DESC_LANGUAGE,
STRING_MANUFACTURER,
STRING_PRODUCT,
STRING_VERSION
};
static const uint8_t DEVICE_DESCRIPTORS[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x10, 0x01, // bcdUSB 1.10
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 64
0x63, 0x25, // idVendor 0x2563
0x75, 0x05, // idProduct 0x0575
0x00, 0x02, // bcdDevice 4.00
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x00, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t REPORT_DESCRIPTORS[] =
{
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x01, // Physical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0D, // Report Count (13)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0D, // Usage Maximum (0x0D)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x03, // Report Count (3)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x25, 0x07, // Logical Maximum (7)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x65, 0x00, // Unit (None)
0x95, 0x01, // Report Count (1)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0x20, // Usage (0x20)
0x09, 0x21, // Usage (0x21)
0x09, 0x22, // Usage (0x22)
0x09, 0x23, // Usage (0x23)
0x09, 0x24, // Usage (0x24)
0x09, 0x25, // Usage (0x25)
0x09, 0x26, // Usage (0x26)
0x09, 0x27, // Usage (0x27)
0x09, 0x28, // Usage (0x28)
0x09, 0x29, // Usage (0x29)
0x09, 0x2A, // Usage (0x2A)
0x09, 0x2B, // Usage (0x2B)
0x95, 0x0C, // Report Count (12)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x0A, 0x21, 0x26, // Usage (0x2621)
0x95, 0x08, // Report Count (8)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x0A, 0x21, 0x26, // Usage (0x2621)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x26, 0xFF, 0x03, // Logical Maximum (1023)
0x46, 0xFF, 0x03, // Physical Maximum (1023)
0x09, 0x2C, // Usage (0x2C)
0x09, 0x2D, // Usage (0x2D)
0x09, 0x2E, // Usage (0x2E)
0x09, 0x2F, // Usage (0x2F)
0x75, 0x10, // Report Size (16)
0x95, 0x04, // Report Count (4)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
};
// uint8_t const CONFIGURATION_DESCRIPTORS[] =
// {
// 0x09, // bLength
// 0x02, // bDescriptorType (Configuration)
// 0x29, 0x00, // wTotalLength 41
// 0x01, // bNumInterfaces 1
// 0x01, // bConfigurationValue
// 0x00, // iConfiguration (String Index)
// 0x80, // bmAttributes
// 0xFA, // bMaxPower 500mA
// 0x09, // bLength
// 0x04, // bDescriptorType (Interface)
// 0x00, // bInterfaceNumber 0
// 0x00, // bAlternateSetting
// 0x02, // bNumEndpoints 2
// 0x03, // bInterfaceClass
// 0x00, // bInterfaceSubClass
// 0x00, // bInterfaceProtocol
// 0x00, // iInterface (String Index)
// 0x09, // bLength
// 0x21, // bDescriptorType (HID)
// 0x10, 0x01, // bcdHID 1.10
// 0x00, // bCountryCode
// 0x01, // bNumDescriptors
// 0x22, // bDescriptorType[0] (HID)
// 0x89, 0x00, // wDescriptorLength[0] 137
// 0x07, // bLength
// 0x05, // bDescriptorType (Endpoint)
// 0x02, // bEndpointAddress (OUT/H2D)
// 0x03, // bmAttributes (Interrupt)
// 0x20, 0x00, // wMaxPacketSize 32
// 0x0A, // bInterval 10 (unit depends on device speed)
// 0x07, // bLength
// 0x05, // bDescriptorType (Endpoint)
// 0x81, // bEndpointAddress (IN/D2H)
// 0x03, // bmAttributes (Interrupt)
// 0x20, 0x00, // wMaxPacketSize 32
// 0x0A, // bInterval 10 (unit depends on device speed)
// };
enum Itf
{
NUM_HID1 = 0,
#if MAX_GAMEPADS > 1
NUM_HID2,
#endif
#if MAX_GAMEPADS > 2
NUM_HID3,
#endif
#if MAX_GAMEPADS > 3
NUM_HID4,
#endif
NUM_TOTAL
};
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + (TUD_HID_DESC_LEN * MAX_GAMEPADS))
// #define EPNUM_HID1 0x81
// #define EPNUM_HID2 0x82
// #define EPNUM_HID3 0x83
// #define EPNUM_HID4 0x84
uint8_t const CONFIGURATION_DESCRIPTORS[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR( 1,
Itf::NUM_TOTAL,
0,
CONFIG_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
500),
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
TUD_HID_DESCRIPTOR( Itf::NUM_HID1,
0,
HID_ITF_PROTOCOL_NONE,
sizeof(REPORT_DESCRIPTORS),
(0x80 | (Itf::NUM_HID1 + 1)),
CFG_TUD_HID_EP_BUFSIZE,
1),
#if MAX_GAMEPADS > 1
TUD_HID_DESCRIPTOR( Itf::NUM_HID2,
0,
HID_ITF_PROTOCOL_NONE,
sizeof(REPORT_DESCRIPTORS),
(0x80 | (Itf::NUM_HID2 + 1)),
CFG_TUD_HID_EP_BUFSIZE,
1),
#endif
#if MAX_GAMEPADS > 2
TUD_HID_DESCRIPTOR( Itf::NUM_HID3,
0,
HID_ITF_PROTOCOL_NONE,
sizeof(REPORT_DESCRIPTORS),
(0x80 | (Itf::NUM_HID3 + 1)),
CFG_TUD_HID_EP_BUFSIZE,
1),
#endif
#if MAX_GAMEPADS > 3
TUD_HID_DESCRIPTOR( Itf::NUM_HID4,
0,
HID_ITF_PROTOCOL_NONE,
sizeof(REPORT_DESCRIPTORS),
(0x80 | (Itf::NUM_HID4 + 1)),
CFG_TUD_HID_EP_BUFSIZE,
1)
#endif
};
}; // namespace DInput
#endif // _DINPUT_DESCRIPTORS_H_

View 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_

View File

@@ -0,0 +1,495 @@
#ifndef _PS3_DESCRIPTORS_H_
#define _PS3_DESCRIPTORS_H_
#include <stdint.h>
#include <cstring>
#include <pico/rand.h>
#include "tusb.h"
namespace PS3
{
static constexpr uint8_t MAGIC_BYTES[8] = { 0x21, 0x26, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
static constexpr uint8_t JOYSTICK_MID = 0x80;
static constexpr uint16_t SIXAXIS_MID = 0xFF01;
namespace ReportID
{
static constexpr uint8_t FEATURE_01 = 0x01;
static constexpr uint8_t FEATURE_EF = 0xEF;
static constexpr uint8_t GET_PAIRING_INFO = 0xF2;
static constexpr uint8_t FEATURE_F4 = 0xF4;
static constexpr uint8_t FEATURE_F5 = 0xF5;
static constexpr uint8_t FEATURE_F7 = 0xF7;
static constexpr uint8_t FEATURE_F8 = 0xF8;
};
namespace PlugState
{
static constexpr uint8_t PLUGGED = 0x02;
static constexpr uint8_t UNPLUGGED = 0x03;
};
namespace PowerState
{
static constexpr uint8_t CHARGING = 0xEE;
static constexpr uint8_t NOT_CHARGING = 0xF1;
static constexpr uint8_t SHUTDOWN = 0x01;
static constexpr uint8_t DISCHARGING = 0x02;
static constexpr uint8_t LOW = 0x03;
static constexpr uint8_t HIGH = 0x04;
static constexpr uint8_t FULL = 0x05;
};
namespace RumbleState
{
static constexpr uint8_t WIRED_RUMBLE = 0x10;
static constexpr uint8_t WIRED_NO_RUMBLE = 0x12;
static constexpr uint8_t WIRELESS_RUMBLE = 0x14;
static constexpr uint8_t WIRELESS_NO_RUMBLE = 0x16;
};
namespace Buttons0
{
static constexpr uint8_t SELECT = 0x01;
static constexpr uint8_t L3 = 0x02;
static constexpr uint8_t R3 = 0x04;
static constexpr uint8_t START = 0x08;
static constexpr uint8_t DPAD_UP = 0x10;
static constexpr uint8_t DPAD_RIGHT = 0x20;
static constexpr uint8_t DPAD_DOWN = 0x40;
static constexpr uint8_t DPAD_LEFT = 0x80;
};
namespace Buttons1
{
static constexpr uint8_t L2 = 0x01;
static constexpr uint8_t R2 = 0x02;
static constexpr uint8_t L1 = 0x04;
static constexpr uint8_t R1 = 0x08;
static constexpr uint8_t TRIANGLE = 0x10;
static constexpr uint8_t CIRCLE = 0x20;
static constexpr uint8_t CROSS = 0x40;
static constexpr uint8_t SQUARE = 0x80;
};
namespace Buttons2
{
static constexpr uint8_t PS = 0x01;
static constexpr uint8_t TP = 0x02;
};
const uint8_t DEFAULT_OUT_REPORT[] =
{
0x01, 0xff, 0x00, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0x27, 0x10, 0x00, 0x32,
0xff, 0x27, 0x10, 0x00, 0x32,
0xff, 0x27, 0x10, 0x00, 0x32,
0xff, 0x27, 0x10, 0x00, 0x32,
0x00, 0x00, 0x00, 0x00, 0x00
};
#pragma pack(push, 1)
struct InReport
{
uint8_t report_id;
uint8_t reserved0;
uint8_t buttons[3];
uint8_t reserved1;
uint8_t joystick_lx;
uint8_t joystick_ly;
uint8_t joystick_rx;
uint8_t joystick_ry;
uint8_t reserved2[2];
uint8_t move_power_status;
uint8_t reserved3;
uint8_t up_axis;
uint8_t right_axis;
uint8_t down_axis;
uint8_t left_axis;
uint8_t l2_axis;
uint8_t r2_axis;
uint8_t l1_axis;
uint8_t r1_axis;
uint8_t triangle_axis;
uint8_t circle_axis;
uint8_t cross_axis;
uint8_t square_axis;
uint8_t reserved4[3];
uint8_t plugged;
uint8_t power_status;
uint8_t rumble_status;
uint8_t reserved5[9];
uint16_t acceler_x;
uint16_t acceler_y;
uint16_t acceler_z;
uint16_t velocity_z;
InReport()
{
std::memset(this, 0, sizeof(InReport));
report_id = 0x01;
joystick_lx = JOYSTICK_MID;
joystick_ly = JOYSTICK_MID;
joystick_rx = JOYSTICK_MID;
joystick_ry = JOYSTICK_MID;
plugged = PlugState::PLUGGED;
power_status = PowerState::FULL;
rumble_status = RumbleState::WIRED_RUMBLE;
acceler_x = acceler_y = acceler_z = velocity_z = SIXAXIS_MID;
}
};
static_assert(sizeof(InReport) == 49, "PS3::InReport size mismatch");
struct LEDs {
uint8_t time_enabled; /* the total time the led is active (0xff means forever) */
uint8_t duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
uint8_t enabled;
uint8_t duty_off; /* % of duty_length the led is off (0xff means 100%) */
uint8_t duty_on; /* % of duty_length the led is on (0xff mean 100%) */
};
static_assert(sizeof(LEDs) == 5, "PS3::LEDs size mismatch");
struct Rumble {
uint8_t reserved;
uint8_t right_duration; /* Right motor duration (0xff means forever) */
uint8_t right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
uint8_t left_duration; /* Left motor duration (0xff means forever) */
uint8_t left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
};
static_assert(sizeof(Rumble) == 5, "PS3::Rumble size mismatch");
struct OutReport
{
struct Rumble rumble;
uint8_t padding[4];
uint8_t leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
struct LEDs led[4]; /* LEDx at (4 - x) */
struct LEDs reserved; /* LED5, not actually soldered */
OutReport()
{
std::memcpy(this, DEFAULT_OUT_REPORT, sizeof(OutReport));
}
};
static_assert(sizeof(OutReport) == 35, "PS3::OutReport size mismatch");
static_assert(sizeof(OutReport) == sizeof(DEFAULT_OUT_REPORT));
struct BTInfo
{
uint8_t reserved0[2];
uint8_t device_address[7]; // leading zero followed by address
uint8_t host_address[7]; // leading zero followed by address
uint8_t reserved1;
BTInfo()
{
std::memset(this, 0, sizeof(BTInfo));
std::memset(reserved0, 0xFF, sizeof(reserved0));
uint8_t addr[] = { 0x00, 0x20, 0x40, 0xCE, 0x00, 0x00, 0x00 };
std::memcpy(device_address, addr, sizeof(addr));
for (uint8_t addr = 0; addr < 3; addr++)
{
device_address[4 + addr] = static_cast<uint8_t>(get_rand_32() % 0xFF);
}
for (uint8_t addr = 0; addr < 6; addr++)
{
host_address[1 + addr] = static_cast<uint8_t>(get_rand_32() % 0xFF);
}
}
};
static_assert(sizeof(BTInfo) == 17, "PS3::BTInfo size mismatch");
#pragma pack(pop)
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
static const uint8_t STRING_MANUFACTURER[] = "Sony";
static const uint8_t STRING_PRODUCT[] = "PLAYSTATION(R)3 Controller";
static const uint8_t STRING_VERSION[] = "1.0";
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
{
STRING_LANGUAGE,
STRING_MANUFACTURER,
STRING_PRODUCT,
STRING_VERSION
};
static const uint8_t DEVICE_DESCRIPTORS[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x00, 0x02, // bcdUSB 2.00
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 64
0x4C, 0x05, // idVendor 0x054C
0x68, 0x02, // idProduct 0x0268
0x00, 0x01, // bcdDevice 2.00
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x00, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t REPORT_DESCRIPTORS[] =
{
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Physical)
0xA1, 0x02, // Collection (Application)
0x85, 0x01, // Report ID (1)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// NOTE: reserved byte
0x75, 0x01, // Report Size (1)
0x95, 0x13, // Report Count (19)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x01, // Physical Maximum (1)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x13, // Usage Maximum (0x13)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01, // Report Size (1)
0x95, 0x0D, // Report Count (13)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// NOTE: 32 bit integer, where 0:18 are buttons and 19:31 are reserved
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Undefined)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// NOTE: four joysticks
0xC0, // End Collection
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x75, 0x08, // Report Size (8)
0x95, 0x27, // Report Count (39)
0x09, 0x01, // Usage (Pointer)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xA1, 0x02, // Collection (Application)
0x85, 0x02, // Report ID (2)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xA1, 0x02, // Collection (Application)
0x85, 0xEE, // Report ID (238)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xA1, 0x02, // Collection (Application)
0x85, 0xEF, // Report ID (239)
0x75, 0x08, // Report Size (8)
0x95, 0x30, // Report Count (48)
0x09, 0x01, // Usage (Pointer)
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xC0, // End Collection
// 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
// 0x09, 0x04, // Usage (Joystick)
// 0xA1, 0x01, // Collection (Application)
// 0xA1, 0x02, // Collection (Logical)
// 0x85, 0x01, // Report ID (1)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x01, // Report Count (1)
// 0x15, 0x00, // Logical Minimum (0)
// 0x26, 0xFF, 0x00, // Logical Maximum (255)
// 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x75, 0x01, // Report Size (1)
// 0x95, 0x13, // Report Count (19)
// 0x15, 0x00, // Logical Minimum (0)
// 0x25, 0x01, // Logical Maximum (1)
// 0x35, 0x00, // Physical Minimum (0)
// 0x45, 0x01, // Physical Maximum (1)
// 0x05, 0x09, // Usage Page (Button)
// 0x19, 0x01, // Usage Minimum (0x01)
// 0x29, 0x13, // Usage Maximum (0x13)
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x75, 0x01, // Report Size (1)
// 0x95, 0x0D, // Report Count (13)
// 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
// 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x15, 0x00, // Logical Minimum (0)
// 0x26, 0xFF, 0x00, // Logical Maximum (255)
// 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
// 0x09, 0x01, // Usage (Pointer)
// 0xA1, 0x00, // Collection (Physical)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x04, // Report Count (4)
// 0x35, 0x00, // Physical Minimum (0)
// 0x46, 0xFF, 0x00, // Physical Maximum (255)
// 0x09, 0x30, // Usage (X)
// 0x09, 0x31, // Usage (Y)
// 0x09, 0x32, // Usage (Z)
// 0x09, 0x35, // Usage (Rz)
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0xC0, // End Collection
// 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x27, // Report Count (39)
// 0x09, 0x01, // Usage (Pointer)
// 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x30, // Report Count (48)
// 0x09, 0x01, // Usage (Pointer)
// 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x30, // Report Count (48)
// 0x09, 0x01, // Usage (Pointer)
// 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
// 0xC0, // End Collection
// 0xA1, 0x02, // Collection (Logical)
// 0x85, 0x02, // Report ID (2)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x30, // Report Count (48)
// 0x09, 0x01, // Usage (Pointer)
// 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
// 0xC0, // End Collection
// 0xA1, 0x02, // Collection (Logical)
// 0x85, 0xEE, // Report ID (-18)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x30, // Report Count (48)
// 0x09, 0x01, // Usage (Pointer)
// 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
// 0xC0, // End Collection
// 0xA1, 0x02, // Collection (Logical)
// 0x85, 0xEF, // Report ID (-17)
// 0x75, 0x08, // Report Size (8)
// 0x95, 0x30, // Report Count (48)
// 0x09, 0x01, // Usage (Pointer)
// 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
// 0xC0, // End Collection
// 0xC0, // End Collection
};
// #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN)
static const uint8_t CONFIGURATION_DESCRIPTORS[] =
{
// 0x09, // bLength
// 0x02, // bDescriptorType (Configuration)
// 0x29, 0x00, // wTotalLength 41
// 0x01, // bNumInterfaces 1
// 0x01, // bConfigurationValue
// 0x00, // iConfiguration (String Index)
// 0x80, // bmAttributes
// 0xFA, // bMaxPower 500mA
// 0x09, // bLength
// 0x04, // bDescriptorType (Interface)
// 0x00, // bInterfaceNumber 0
// 0x00, // bAlternateSetting
// 0x02, // bNumEndpoints 2
// 0x03, // bInterfaceClass
// 0x00, // bInterfaceSubClass
// 0x00, // bInterfaceProtocol
// 0x00, // iInterface (String Index)
// 0x09, // bLength
// 0x21, // bDescriptorType (HID)
// 0x11, 0x01, // bcdHID 1.11
// 0x00, // bCountryCode
// 0x01, // bNumDescriptors
// 0x22, // bDescriptorType[0] (HID)
// 0x94, 0x00, // wDescriptorLength[0] 148
// 0x07, // bLength
// 0x05, // bDescriptorType (Endpoint)
// 0x02, // bEndpointAddress (OUT/H2D)
// 0x03, // bmAttributes (Interrupt)
// 0x40, 0x00, // wMaxPacketSize 64
// 0x0A, // bInterval 10 (unit depends on device speed)
// 0x07, // bLength
// 0x05, // bDescriptorType (Endpoint)
// 0x81, // bEndpointAddress (IN/D2H)
// 0x03, // bmAttributes (Interrupt)
// 0x40, 0x00, // wMaxPacketSize 64
// 0x0A, // bInterval 10 (unit depends on device speed)
0x09, // bLength
0x02, // bDescriptorType (Configuration)
0x29, 0x00, // wTotalLength 41
0x01, // bNumInterfaces 1
0x01, // bConfigurationValue
0x00, // iConfiguration (String Index)
0x80, // bmAttributes
0xFA, // bMaxPower 500mA
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x00, // bInterfaceNumber 0
0x00, // bAlternateSetting
0x02, // bNumEndpoints 2
0x03, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface (String Index)
0x09, // bLength
0x21, // bDescriptorType (HID)
0x11, 0x01, // bcdHID 1.17
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType[0] (HID)
0x94, 0x00, // wDescriptorLength[0] 148
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x02, // bEndpointAddress (OUT/H2D)
0x03, // bmAttributes (Interrupt)
0x40, 0x00, // wMaxPacketSize 64
0x01, // bInterval 1 (unit depends on device speed)
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x81, // bEndpointAddress (IN/D2H)
0x03, // bmAttributes (Interrupt)
0x40, 0x00, // wMaxPacketSize 64
0x01, // bInterval 1 (unit depends on device speed)
};
} // namespace PS3
#endif // _PS3_DESCRIPTORS_H_

View 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_

View File

@@ -0,0 +1,137 @@
#ifndef _PS5_DESCRIPTORS_H_
#define _PS5_DESCRIPTORS_H_
#include <cstdint>
#include <cstring>
namespace PS5
{
static constexpr uint8_t DPAD_MASK = 0x0F;
static constexpr size_t IN_REPORT_CMP_SIZE = 12;
static constexpr uint8_t JOYSTICK_MID = 0x80;
static constexpr uint8_t OUT_REPORT_ID = 5;
namespace Buttons0
{
static constexpr uint8_t DPAD_UP = 0x00;
static constexpr uint8_t DPAD_UP_RIGHT = 0x01;
static constexpr uint8_t DPAD_RIGHT = 0x02;
static constexpr uint8_t DPAD_RIGHT_DOWN = 0x03;
static constexpr uint8_t DPAD_DOWN = 0x04;
static constexpr uint8_t DPAD_DOWN_LEFT = 0x05;
static constexpr uint8_t DPAD_LEFT = 0x06;
static constexpr uint8_t DPAD_LEFT_UP = 0x07;
static constexpr uint8_t DPAD_CENTER = 0x08;
static constexpr uint8_t SQUARE = 0x10;
static constexpr uint8_t CROSS = 0x20;
static constexpr uint8_t CIRCLE = 0x40;
static constexpr uint8_t TRIANGLE = 0x80;
};
namespace Buttons1
{
static constexpr uint8_t L1 = 0x01;
static constexpr uint8_t R1 = 0x02;
static constexpr uint8_t L2 = 0x04;
static constexpr uint8_t R2 = 0x08;
static constexpr uint8_t SHARE = 0x10;
static constexpr uint8_t OPTIONS = 0x20;
static constexpr uint8_t L3 = 0x40;
static constexpr uint8_t R3 = 0x80;
};
namespace Buttons2
{
static constexpr uint8_t PS = 0x01;
static constexpr uint8_t TP = 0x02;
static constexpr uint8_t MUTE = 0x04;
};
#pragma pack(push, 1)
struct InReport
{
uint8_t report_id;
uint8_t joystick_lx;
uint8_t joystick_ly;
uint8_t joystick_rx;
uint8_t joystick_ry;
uint8_t trigger_l;
uint8_t trigger_r;
uint8_t seq_number;
uint8_t buttons[4];
// Motion sensors
uint16_t gyro[3]; // Gyroscope data for x, y, z axes
uint16_t accel[3]; // Accelerometer data for x, y, z axes
uint32_t sensor_timestamp; // Timestamp for sensor data
uint8_t reserved2;
// Touchpad
struct dualsense_touch_point {
uint8_t counter : 7; // Incremented every time a finger touches the touchpad
uint8_t touching : 1; // Indicates if a finger is currently touching the touchpad
uint16_t x : 12; // X coordinate of the touchpoint
uint16_t y : 12; // Y coordinate of the touchpoint
} points[2]; // Array of touchpoints (up to 2)
uint8_t reserved3[12];
uint8_t status; // ?
uint8_t reserved4[10];
};
// static_assert(sizeof(InReport) == 52, "PS5::InReport is not correct size");
struct OutReport
{
uint8_t report_id;
uint8_t valid_flag0;
uint8_t valid_flag1;
/* For DualShock 4 compatibility mode. */
uint8_t motor_right;
uint8_t motor_left;
/* Audio controls */
uint8_t headphone_audio_volume; /* 0-0x7f */
uint8_t speaker_audio_volume; /* 0-255 */
uint8_t internal_microphone_volume; /* 0-0x40 */
uint8_t audio_flags;
uint8_t mute_button_led;
uint8_t power_save_control;
/* right trigger motor */
uint8_t right_trigger_motor_mode;
uint8_t right_trigger_param[10];
/* right trigger motor */
uint8_t left_trigger_motor_mode;
uint8_t left_trigger_param[10];
uint8_t reserved2[4];
uint8_t reduce_motor_power;
uint8_t audio_flags2; /* 3 first bits: speaker pre-gain */
/* LEDs and lightbar */
uint8_t valid_flag2;
uint8_t reserved3[2];
uint8_t lightbar_setup;
uint8_t led_brightness;
uint8_t player_leds;
uint8_t lightbar_red;
uint8_t lightbar_green;
uint8_t lightbar_blue;
};
static_assert(sizeof(OutReport) == 48);
#pragma pack(pop)
}; // namespace PS5
#endif // _PS5_DESCRIPTORS_H_

View 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_

View 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_

View 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_

View File

@@ -0,0 +1,233 @@
#ifndef _XINPUT_DESCRIPTORS_H_
#define _XINPUT_DESCRIPTORS_H_
#include <stdint.h>
#include <cstring>
namespace XInput
{
static constexpr size_t ENDPOINT_IN_SIZE = 20;
static constexpr size_t ENDPOINT_OUT_SIZE = 32;
namespace Chatpad
{
static constexpr uint8_t CODE_1 = 23 ;
static constexpr uint8_t CODE_2 = 22 ;
static constexpr uint8_t CODE_3 = 21 ;
static constexpr uint8_t CODE_4 = 20 ;
static constexpr uint8_t CODE_5 = 19 ;
static constexpr uint8_t CODE_6 = 18 ;
static constexpr uint8_t CODE_7 = 17 ;
static constexpr uint8_t CODE_8 = 103;
static constexpr uint8_t CODE_9 = 102;
static constexpr uint8_t CODE_0 = 101;
static constexpr uint8_t CODE_Q = 39 ;
static constexpr uint8_t CODE_W = 38 ;
static constexpr uint8_t CODE_E = 37 ;
static constexpr uint8_t CODE_R = 36 ;
static constexpr uint8_t CODE_T = 35 ;
static constexpr uint8_t CODE_Y = 34 ;
static constexpr uint8_t CODE_U = 33 ;
static constexpr uint8_t CODE_I = 118;
static constexpr uint8_t CODE_O = 117;
static constexpr uint8_t CODE_P = 100;
static constexpr uint8_t CODE_A = 55;
static constexpr uint8_t CODE_S = 54;
static constexpr uint8_t CODE_D = 53;
static constexpr uint8_t CODE_F = 52;
static constexpr uint8_t CODE_G = 51;
static constexpr uint8_t CODE_H = 50;
static constexpr uint8_t CODE_J = 49;
static constexpr uint8_t CODE_K = 119;
static constexpr uint8_t CODE_L = 114;
static constexpr uint8_t CODE_COMMA = 98;
static constexpr uint8_t CODE_Z = 70;
static constexpr uint8_t CODE_X = 69;
static constexpr uint8_t CODE_C = 68;
static constexpr uint8_t CODE_V = 67;
static constexpr uint8_t CODE_B = 66;
static constexpr uint8_t CODE_N = 65;
static constexpr uint8_t CODE_M = 82;
static constexpr uint8_t CODE_PERIOD = 83;
static constexpr uint8_t CODE_ENTER = 99;
static constexpr uint8_t CODE_LEFT = 85;
static constexpr uint8_t CODE_SPACE = 84;
static constexpr uint8_t CODE_RIGHT = 81;
static constexpr uint8_t CODE_BACK = 113;
//Offset byte 25
static constexpr uint8_t CODE_SHIFT = 1;
static constexpr uint8_t CODE_GREEN = 2;
static constexpr uint8_t CODE_ORANGE = 4;
static constexpr uint8_t CODE_MESSENGER = 8;
};
namespace OutReportID
{
static constexpr uint8_t RUMBLE = 0x00;
static constexpr uint8_t LED = 0x01;
};
namespace Buttons0
{
static constexpr uint8_t DPAD_UP = (1U << 0);
static constexpr uint8_t DPAD_DOWN = (1U << 1);
static constexpr uint8_t DPAD_LEFT = (1U << 2);
static constexpr uint8_t DPAD_RIGHT = (1U << 3);
static constexpr uint8_t START = (1U << 4);
static constexpr uint8_t BACK = (1U << 5);
static constexpr uint8_t L3 = (1U << 6);
static constexpr uint8_t R3 = (1U << 7);
};
namespace Buttons1
{
static constexpr uint8_t LB = (1U << 0);
static constexpr uint8_t RB = (1U << 1);
static constexpr uint8_t HOME = (1U << 2);
static constexpr uint8_t A = (1U << 4);
static constexpr uint8_t B = (1U << 5);
static constexpr uint8_t X = (1U << 6);
static constexpr uint8_t Y = (1U << 7);
};
#pragma pack(push, 1)
struct InReport
{
uint8_t report_id;
uint8_t report_size;
uint8_t buttons[2];
uint8_t trigger_l;
uint8_t trigger_r;
int16_t joystick_lx;
int16_t joystick_ly;
int16_t joystick_rx;
int16_t joystick_ry;
uint8_t reserved[6];
};
static_assert(sizeof(InReport) == 20, "XInput::InReport is not the correct size");
struct WiredChatpadReport
{
uint8_t report_id;
uint8_t chatpad[3];
};
static_assert(sizeof(WiredChatpadReport) == 4, "XInput::WiredChatpadReport is not the correct size");
struct InReportWireless
{
uint8_t command[4];
uint8_t report_id;
uint8_t report_size;
uint8_t buttons[2];
uint8_t trigger_l;
uint8_t trigger_r;
int16_t joystick_lx;
int16_t joystick_ly;
int16_t joystick_rx;
int16_t joystick_ry; // 18
uint8_t reserved[6];
uint8_t chatpad_status;
uint8_t chatpad[3];
};
static_assert(sizeof(InReportWireless) == 28, "XInput::InReportWireless is not the correct size");
struct OutReport
{
uint8_t report_id;
uint8_t report_size;
uint8_t led;
uint8_t rumble_l;
uint8_t rumble_r;
uint8_t reserved[3];
};
static_assert(sizeof(OutReport) == 8, "XInput::OutReport is not the correct size");
#pragma pack(pop)
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
static const uint8_t STRING_MANUFACTURER[] = "Microsoft";
static const uint8_t STRING_PRODUCT[] = "XInput STANDARD GAMEPAD";
static const uint8_t STRING_VERSION[] = "1.0";
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
{
STRING_LANGUAGE,
STRING_MANUFACTURER,
STRING_PRODUCT,
STRING_VERSION
};
static const uint8_t DEVICE_DESCRIPTORS[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x00, 0x02, // bcdUSB 2.00
0xFF, // bDeviceClass
0xFF, // bDeviceSubClass
0xFF, // bDeviceProtocol
0x40, // bMaxPacketSize0 64
0x5E, 0x04, // idVendor 0x045E
0x8E, 0x02, // idProduct 0x028E
0x14, 0x01, // bcdDevice 2.14
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x03, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t CONFIGURATION_DESCRIPTORS[] =
{
0x09, // bLength
0x02, // bDescriptorType (Configuration)
0x30, 0x00, // wTotalLength 48
0x01, // bNumInterfaces 1
0x01, // bConfigurationValue
0x00, // iConfiguration (String Index)
0x80, // bmAttributes
0xFA, // bMaxPower 500mA
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x00, // bInterfaceNumber 0
0x00, // bAlternateSetting
0x02, // bNumEndpoints 2
0xFF, // bInterfaceClass
0x5D, // bInterfaceSubClass
0x01, // bInterfaceProtocol
0x00, // iInterface (String Index)
0x10, // bLength
0x21, // bDescriptorType (HID)
// 0x10, 0x01, // bcdHID 1.10
0x00, 0x01, // bcdHID 1.00
0x01, // bCountryCode
0x24, // bNumDescriptors
0x81, // bDescriptorType[0] (Unknown 0x81)
0x14, 0x03, // wDescriptorLength[0] 788
0x00, // bDescriptorType[1] (Unknown 0x00)
0x03, 0x13, // wDescriptorLength[1] 4867
0x01, // bDescriptorType[2] (Unknown 0x02)
0x00, 0x03, // wDescriptorLength[2] 768
0x00, // bDescriptorType[3] (Unknown 0x00)
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x81, // bEndpointAddress (IN/D2H)
0x03, // bmAttributes (Interrupt)
0x20, 0x00, // wMaxPacketSize 32
0x01, // bInterval 1 (unit depends on device speed)
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x01, // bEndpointAddress (OUT/H2D)
0x03, // bmAttributes (Interrupt)
0x20, 0x00, // wMaxPacketSize 32
0x08, // bInterval 8 (unit depends on device speed)
};
};
#endif // _XINPUT_DESCRIPTORS_H_

View File

@@ -0,0 +1,635 @@
#ifndef _XBOX_OG_DESCRIPTORS_H_
#define _XBOX_OG_DESCRIPTORS_H_
#include <cstdint>
#include "tusb.h"
namespace XboxOG
{
//Control request constants
static constexpr uint8_t GET_DESC_REQ_TYPE = 0xC1;
static constexpr uint8_t GET_DESC_REQ = 0x06;
static constexpr uint16_t GET_DESC_VALUE = 0x4200;
static constexpr uint8_t GET_DESC_SUBTYPE_GP_DUKE = 0x01;
static constexpr uint8_t GET_DESC_SUBTYPE_GP_S = 0x02;
static constexpr uint8_t GET_DESC_SUBTYPE_WHEEL = 0x10;
static constexpr uint8_t GET_DESC_SUBTYPE_STICK = 0x20;
static constexpr uint8_t GET_DESC_SUBTYPE_LIGHTGUN = 0x50;
static constexpr uint8_t GET_CAP_REQ_TYPE = 0xC1;
static constexpr uint8_t GET_CAP_REQ = 0x01;
static constexpr uint16_t GET_CAP_VALUE_IN = 0x0100;
static constexpr uint16_t GET_CAP_VALUE_OUT = 0x0200;
static constexpr uint8_t SET_REPORT_REQ_TYPE = 0x21;
static constexpr uint8_t SET_REPORT_REQ = 0x09;
static constexpr uint16_t SET_REPORT_VALUE = 0x0200;
static constexpr uint8_t GET_REPORT_REQ_TYPE = 0xA1;
static constexpr uint8_t GET_REPORT_REQ = 1;
static constexpr uint16_t GET_REPORT_VALUE = 0x0100;
namespace GP //Duke/S
{
namespace Buttons
{
static constexpr uint8_t DPAD_UP = (1 << 0);
static constexpr uint8_t DPAD_DOWN = (1 << 1);
static constexpr uint8_t DPAD_LEFT = (1 << 2);
static constexpr uint8_t DPAD_RIGHT = (1 << 3);
static constexpr uint8_t START = (1 << 4);
static constexpr uint8_t BACK = (1 << 5);
static constexpr uint8_t L3 = (1 << 6);
static constexpr uint8_t R3 = (1 << 7);
};
#pragma pack(push, 1)
struct InReport
{
uint8_t reserved1;
uint8_t report_len;
uint8_t buttons;
uint8_t reserved2;
uint8_t a;
uint8_t b;
uint8_t x;
uint8_t y;
uint8_t black;
uint8_t white;
uint8_t trigger_l;
uint8_t trigger_r;
int16_t joystick_lx;
int16_t joystick_ly;
int16_t joystick_rx;
int16_t joystick_ry;
};
static_assert(sizeof(InReport) == 20, "XboxOG::InReport is not the correct size");
struct OutReport
{
uint8_t reserved;
uint8_t report_len;
uint16_t rumble_l;
uint16_t rumble_r;
};
static_assert(sizeof(OutReport) == 6, "XboxOG::OutReport is not the correct size");
#pragma pack(pop)
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
static const uint8_t STRING_MANUFACTURER[] = "";
static const uint8_t STRING_PRODUCT[] = "";
static const uint8_t STRING_VERSION[] = "1.0";
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
{
STRING_LANGUAGE,
STRING_MANUFACTURER,
STRING_PRODUCT,
STRING_VERSION
};
static const tusb_desc_device_t DEVICE_DESCRIPTORS =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0110,
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x045E,
.idProduct = 0x0289,
.bcdDevice = 0x0121,
.iManufacturer = 0x00,
.iProduct = 0x00,
.iSerialNumber = 0x00,
.bNumConfigurations = 0x01
};
static constexpr uint8_t INTERFACE_CLASS = 0x58;
static constexpr uint8_t INTERFACE_SUBCLASS = 0x42;
static constexpr uint16_t DRIVER_LEN = 9+7+7;
static constexpr uint16_t CONFIG_DESC_TOTAL_LEN = DRIVER_LEN + TUD_CONFIG_DESC_LEN;
enum Interface
{
NUM_DUKE = 0,
NUM_TOTAL
};
#define TUD_XID_DUKE_DESCRIPTOR(_itfnum, _epout, _epin) \
/* Interface */\
9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, INTERFACE_CLASS, INTERFACE_SUBCLASS, 0x00, 0x00,\
/* Endpoint In */\
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(32), 4, \
/* Endpoint Out */\
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(32), 4
static const uint8_t CONFIGURATION_DESCRIPTORS[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR( 1,
Interface::NUM_TOTAL,
0,
CONFIG_DESC_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
500),
TUD_XID_DUKE_DESCRIPTOR(Interface::NUM_DUKE,
Interface::NUM_DUKE + 1,
0x80 | (Interface::NUM_DUKE + 1)),
};
static const uint8_t XID_DEVICE_DESCRIPTORS[] =
{
0x10,
0x42,
0x00, 0x01,
0x01,
0x02,
0x14,
0x06,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static const uint8_t XID_CAPABILITIES_IN[] =
{
0x00,
0x14,
0xFF,
0x00,
0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF
};
static const uint8_t XID_CAPABILITIES_OUT[] =
{
0x00,
0x06,
0xFF, 0xFF, 0xFF, 0xFF
};
}; // GP
namespace SB // Steel Battalion
{
static constexpr uint16_t AIMING_MID = 32768;
static constexpr uint16_t BUTTONS2_TOGGLE_MID = 0xFFFC;
static constexpr int16_t DEFAULT_DEADZONE = 7500;
static constexpr uint16_t DEFAULT_SENSE = 400;
namespace Buttons0
{
static constexpr uint16_t RIGHTJOYMAINWEAPON = 0x0001;
static constexpr uint16_t RIGHTJOYFIRE = 0x0002;
static constexpr uint16_t RIGHTJOYLOCKON = 0x0004;
static constexpr uint16_t EJECT = 0x0008;
static constexpr uint16_t COCKPITHATCH = 0x0010;
static constexpr uint16_t IGNITION = 0x0020;
static constexpr uint16_t START = 0x0040;
static constexpr uint16_t MULTIMONOPENCLOSE = 0x0080;
static constexpr uint16_t MULTIMONMAPZOOMINOUT = 0x0100;
static constexpr uint16_t MULTIMONMODESELECT = 0x0200;
static constexpr uint16_t MULTIMONSUBMONITOR = 0x0400;
static constexpr uint16_t MAINMONZOOMIN = 0x0800;
static constexpr uint16_t MAINMONZOOMOUT = 0x1000;
static constexpr uint16_t FUNCTIONFSS = 0x2000;
static constexpr uint16_t FUNCTIONMANIPULATOR = 0x4000;
static constexpr uint16_t FUNCTIONLINECOLORCHANGE = 0x8000;
};
namespace Buttons1
{
static constexpr uint16_t WASHING = 0x0001;
static constexpr uint16_t EXTINGUISHER = 0x0002;
static constexpr uint16_t CHAFF = 0x0004;
static constexpr uint16_t FUNCTIONTANKDETACH = 0x0008;
static constexpr uint16_t FUNCTIONOVERRIDE = 0x0010;
static constexpr uint16_t FUNCTIONNIGHTSCOPE = 0x0020;
static constexpr uint16_t FUNCTIONF1 = 0x0040;
static constexpr uint16_t FUNCTIONF2 = 0x0080;
static constexpr uint16_t FUNCTIONF3 = 0x0100;
static constexpr uint16_t WEAPONCONMAIN = 0x0200;
static constexpr uint16_t WEAPONCONSUB = 0x0400;
static constexpr uint16_t WEAPONCONMAGAZINE = 0x0800;
static constexpr uint16_t COMM1 = 0x1000;
static constexpr uint16_t COMM2 = 0x2000;
static constexpr uint16_t COMM3 = 0x4000;
static constexpr uint16_t COMM4 = 0x8000;
};
namespace Buttons2
{
static constexpr uint16_t COMM5 = 0x0001;
static constexpr uint16_t LEFTJOYSIGHTCHANGE = 0x0002;
static constexpr uint16_t TOGGLEFILTERCONTROL = 0x0004;
static constexpr uint16_t TOGGLEOXYGENSUPPLY = 0x0008;
static constexpr uint16_t TOGGLEFUELFLOWRATE = 0x0010;
static constexpr uint16_t TOGGLEBUFFREMATERIAL = 0x0020;
static constexpr uint16_t TOGGLEVTLOCATION = 0x0040;
};
namespace Axis
{
static constexpr uint16_t AIMINGX = 0x0001;
static constexpr uint16_t AIMINGY = 0x0002;
static constexpr uint16_t LEVER = 0x0004;
static constexpr uint16_t SIGHTX = 0x0008;
static constexpr uint16_t SIGHTY = 0x0010;
static constexpr uint16_t LPEDAL = 0x0020;
static constexpr uint16_t MPEDAL = 0x0040;
static constexpr uint16_t RPEDAL = 0x0080;
static constexpr uint16_t TUNER = 0x0100;
static constexpr uint16_t GEAR = 0x0200;
};
namespace Gear
{
static constexpr int8_t R = 7;
static constexpr int8_t N = 8;
static constexpr int8_t G1 = 9;
static constexpr int8_t G2 = 10;
static constexpr int8_t G3 = 11;
static constexpr int8_t G4 = 12;
static constexpr int8_t G5 = 13;
};
#pragma pack(push, 1)
struct InReport
{
uint8_t zero;
uint8_t bLength;
uint16_t dButtons[3];
uint16_t aimingX; //0 to 2^16 left to right
uint16_t aimingY; //0 to 2^16 top to bottom
int16_t rotationLever;
int16_t sightChangeX;
int16_t sightChangeY;
uint16_t leftPedal; //Sidestep, 0x0000 to 0xFF00
uint16_t middlePedal; //Brake, 0x0000 to 0xFF00
uint16_t rightPedal; //Acceleration, 0x0000 to oxFF00
int8_t tunerDial; //0-15 is from 9oclock, around clockwise
int8_t gearLever; //7-13 is gears R,1,2,3,4,5
};
static_assert(sizeof(InReport) == 26, "XboxOGSB::InReport is not the correct size");
struct OutReport
{
uint8_t zero;
uint8_t bLength;
uint8_t CockpitHatch_EmergencyEject;
uint8_t Start_Ignition;
uint8_t MapZoomInOut_OpenClose;
uint8_t SubMonitorModeSelect_ModeSelect;
uint8_t MainMonitorZoomOut_MainMonitorZoomIn;
uint8_t Manipulator_ForecastShootingSystem;
uint8_t Washing_LineColorChange;
uint8_t Chaff_Extinguisher;
uint8_t Override_TankDetach;
uint8_t F1_NightScope;
uint8_t F3_F2;
uint8_t SubWeaponControl_MainWeaponControl;
uint8_t Comm1_MagazineChange;
uint8_t Comm3_Comm2;
uint8_t Comm5_Comm4;
uint8_t GearR_;
uint8_t Gear1_GearN;
uint8_t Gear3_Gear2;
uint8_t Gear5_Gear4;
uint8_t dummy;
};
static_assert(sizeof(OutReport) == 22, "XboxOGSB::OutReport is not the correct size");
#pragma pack(pop)
static const uint8_t STRING_LANGUAGE[] = { 0x09, 0x04 };
static const uint8_t STRING_MANUFACTURER[] = "";
static const uint8_t STRING_PRODUCT[] = "";
static const uint8_t STRING_VERSION[] = "1.0";
static const uint8_t *STRING_DESCRIPTORS[] __attribute__((unused)) =
{
STRING_LANGUAGE,
STRING_MANUFACTURER,
STRING_PRODUCT,
STRING_VERSION
};
static constexpr uint8_t INTERFACE_CLASS = 0x58;
static constexpr uint8_t INTERFACE_SUBCLASS = 0x42;
static constexpr uint16_t DRIVER_LEN = 9+7+7;
static constexpr uint16_t CONFIG_DESC_TOTAL_LEN = DRIVER_LEN + TUD_CONFIG_DESC_LEN;
enum Interface
{
NUM_STEELBATTALION = 0,
NUM_TOTAL
};
#define TUD_XID_SB_DESCRIPTOR(_itfnum, _epout, _epin) \
/* Interface */\
9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, INTERFACE_CLASS, INTERFACE_SUBCLASS, 0x00, 0x00,\
/* Endpoint In */\
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(32), 4, \
/* Endpoint Out */\
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(32), 4
static uint8_t const CONFIGURATION_DESCRIPTORS[] =
{
TUD_CONFIG_DESCRIPTOR( 1,
Interface::NUM_TOTAL,
0,
CONFIG_DESC_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
500),
TUD_XID_SB_DESCRIPTOR( Interface::NUM_STEELBATTALION,
Interface::NUM_STEELBATTALION + 1,
0x80 | (Interface::NUM_STEELBATTALION + 1)),
};
static const uint8_t XID_DEVICE_DESCRIPTORS[] =
{
0x10,
0x42,
0x00, 0x01,
0x80,
0x01,
0x1A,
0x16,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static const uint8_t XID_CAPABILITIES_IN[] =
{
0x00,
0x1A,
0xFF,
0xFF,
0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF
};
static const uint8_t XID_CAPABILITIES_OUT[] =
{
0x00,
0x16,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF
};
}; // SB
namespace XR //XRemote
{
#define XID_REMOTE_INTERFACE_CLASS 0x58
#define XID_REMOTE_INTERFACE_SUBCLASS 0x42
#define XID_XREMOTE_ROM_CLASS 0x59
namespace ButtonCode
{
static constexpr uint16_t SELECT = 0x0A0B;
static constexpr uint16_t UP = 0x0AA6;
static constexpr uint16_t DOWN = 0x0AA7;
static constexpr uint16_t RIGHT = 0x0AA8;
static constexpr uint16_t LEFT = 0x0AA9;
static constexpr uint16_t INFO = 0x0AC3;
static constexpr uint16_t NINE = 0x0AC6;
static constexpr uint16_t EIGHT = 0x0AC7;
static constexpr uint16_t SEVEN = 0x0AC8;
static constexpr uint16_t SIX = 0x0AC9;
static constexpr uint16_t FIVE = 0x0ACA;
static constexpr uint16_t FOUR = 0x0ACB;
static constexpr uint16_t THREE = 0x0ACC;
static constexpr uint16_t TWO = 0x0ACD;
static constexpr uint16_t ONE = 0x0ACE;
static constexpr uint16_t ZERO = 0x0ACF;
static constexpr uint16_t DISPLAY = 0x0AD5;
static constexpr uint16_t BACK = 0x0AD8;
static constexpr uint16_t SKIP_MINUS = 0x0ADD;
static constexpr uint16_t SKIP_PLUS = 0x0ADF;
static constexpr uint16_t STOP = 0x0AE0;
static constexpr uint16_t REVERSE = 0x0AE2;
static constexpr uint16_t FORWARD = 0x0AE3;
static constexpr uint16_t TITLE = 0x0AE5;
static constexpr uint16_t PAUSE = 0x0AE6;
static constexpr uint16_t PLAY = 0x0AEA;
static constexpr uint16_t MENU = 0x0AF7;
};
#pragma pack(push, 1)
struct InReport
{
uint8_t zero;
uint8_t bLength;
uint16_t buttonCode;
uint16_t timeElapsed; // ms since last button press
};
static_assert(sizeof(InReport) == 6, "XboxOGXRemote::InReport is not the correct size");
#pragma pack(pop)
static constexpr uint16_t DRIVER_LEN = 9+7+9;
// enum xboxog_xremote_xid_interface
// {
// ITF_NUM_XID_XREMOTE = 0,
// ITF_NUM_XID_XREMOTE_ROM,
// XID_REMOTE_ITF_NUM_TOTAL
// };
// #define TUD_XID_XREMOTE_DESC_LEN (9+7+9)
// #define TUD_XID_XREMOTE_DESCRIPTOR(_itfnum, _epin) \
// /* Interface 0 (HID DATA)*/\
// 9, TUSB_DESC_INTERFACE, _itfnum, 0, 1, XID_REMOTE_INTERFACE_CLASS, XID_REMOTE_INTERFACE_SUBCLASS, 0x00, 0x00,\
// /* Endpoint In */\
// 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(8), 16, \
// /* Interface 1 (ROM DATA)*/\
// 9, TUSB_DESC_INTERFACE, _itfnum + 1, 0, 0, XID_XREMOTE_ROM_CLASS, 0x00, 0x00, 0x00
// #define XID_REMOTE_CONFIG_TOTAL_LEN \
// (TUD_CONFIG_DESC_LEN) + \
// (TUD_XID_XREMOTE_DESC_LEN)
// static const uint8_t CONFIGURATION_DESCRIPTORS[] =
// {
// // Config number, interface count, string index, total length, attribute, power in mA
// TUD_CONFIG_DESCRIPTOR( 1,
// XID_REMOTE_ITF_NUM_TOTAL,
// 0,
// XID_REMOTE_CONFIG_TOTAL_LEN,
// TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
// 500),
// TUD_XID_XREMOTE_DESCRIPTOR( ITF_NUM_XID_XREMOTE,
// 0x80 | (ITF_NUM_XID_XREMOTE + 1)),
// };
static const uint8_t DEVICE_DESCRIPTORS[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x10, 0x01, // bcdUSB 1.10
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 64
0x5E, 0x04, // idVendor 0x045E
0x84, 0x02, // idProduct 0x0284
0x30, 0x01, // bcdDevice 2.30
0x00, // iManufacturer (String Index)
0x00, // iProduct (String Index)
0x00, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t CONFIGURATION_DESCRIPTORS[] =
{
0x09, // bLength
0x02, // bDescriptorType (Configuration)
0x22, 0x00, // wTotalLength 34
0x02, // bNumInterfaces 2
0x01, // bConfigurationValue
0x00, // iConfiguration (String Index)
0x80, // bmAttributes
0xFA, // bMaxPower 500mA
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x00, // bInterfaceNumber 0
0x00, // bAlternateSetting
0x01, // bNumEndpoints 1
0x58, // bInterfaceClass
0x42, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface (String Index)
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x81, // bEndpointAddress (IN/D2H)
0x03, // bmAttributes (Interrupt)
0x08, 0x00, // wMaxPacketSize 8
0x10, // bInterval 16 (unit depends on device speed)
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x01, // bInterfaceNumber 1
0x00, // bAlternateSetting
0x00, // bNumEndpoints 0
0x59, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface (String Index)
};
static const uint8_t XID_DEVICE_DESCRIPTORS[] =
{
0x08,
0x42,
0x00, 0x01,
0x03,
0x00,
0x06,
0x00
};
}; // XR
namespace COM
{
static constexpr uint8_t INTERFACE_CLASS = 0x78;
static constexpr uint8_t INTERFACE_SUBCLASS = 0x00;
static const uint8_t DEVICE_DESCRIPTORS[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x10, 0x01, // bcdUSB 1.10
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x08, // bMaxPacketSize0 8
0x5E, 0x04, // idVendor 0x045E
0x83, 0x02, // idProduct 0x0283
0x58, 0x01, // bcdDevice 2.58
0x00, // iManufacturer (String Index)
0x00, // iProduct (String Index)
0x00, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t CONFIGURATION_DESCRIPTORS[]
{
0x09, // bLength
0x02, // bDescriptorType (Configuration)
0x2D, 0x00, // wTotalLength 45
0x02, // bNumInterfaces 2
0x01, // bConfigurationValue
0x00, // iConfiguration (String Index)
0x80, // bmAttributes
0x32, // bMaxPower 100mA
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x00, // bInterfaceNumber 0
0x00, // bAlternateSetting
0x01, // bNumEndpoints 1
0x78, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface (String Index)
0x09, // bLength
0x05, // bDescriptorType (Endpoint)
0x04, // bEndpointAddress (OUT/H2D)
0x05, // bmAttributes (Isochronous, Async, Data EP)
0x30, 0x00, // wMaxPacketSize 48
0x01, // bInterval 1 (unit depends on device speed)
0x00, 0x00,
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x01, // bInterfaceNumber 1
0x00, // bAlternateSetting
0x01, // bNumEndpoints 1
0x78, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface (String Index)
0x09, // bLength
0x05, // bDescriptorType (Endpoint)
0x85, // bEndpointAddress (IN/D2H)
0x05, // bmAttributes (Isochronous, Async, Data EP)
0x30, 0x00, // wMaxPacketSize 48
0x01, // bInterval 1 (unit depends on device speed)
0x00, 0x00,
};
}; // COM
}; // namespace XboxOG
#endif // _XBOX_OG_DESCRIPTORS_H_

View 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_

View File

@@ -0,0 +1,623 @@
#ifndef _GAMEPAD_H_
#define _GAMEPAD_H_
#include <cstdint>
#include <atomic>
#include <limits>
#include <array>
#include <cstring>
#include "UserSettings/UserProfile.h"
namespace INT_16
{
static constexpr int16_t MIN = INT16_MIN;
static constexpr int16_t MID = 0;
static constexpr int16_t MAX = INT16_MAX;
}
namespace UINT_16
{
static constexpr uint16_t MIN = 0;
static constexpr uint16_t MID = 0x8000;
static constexpr uint16_t MAX = 0xFFFF;
}
namespace UINT_8
{
static constexpr uint8_t MAX = 0xFF;
static constexpr uint8_t MID = 0x80;
static constexpr uint8_t MIN = 0x00;
}
namespace INT_10
{
static constexpr int32_t MIN = -512;
static constexpr int32_t MAX = 511;
}
namespace UINT_10
{
static constexpr int32_t MAX = 1023;
}
class Gamepad
{
public:
struct DPad
{
static constexpr uint8_t UP = 0x01;
static constexpr uint8_t DOWN = 0x02;
static constexpr uint8_t LEFT = 0x04;
static constexpr uint8_t RIGHT = 0x08;
static constexpr uint8_t UP_LEFT = UP | LEFT;
static constexpr uint8_t UP_RIGHT = UP | RIGHT;
static constexpr uint8_t DOWN_LEFT = DOWN | LEFT;
static constexpr uint8_t DOWN_RIGHT = DOWN | RIGHT;
static constexpr uint8_t NONE = 0x00;
};
struct Button
{
static constexpr uint16_t A = 0x0001;
static constexpr uint16_t B = 0x0002;
static constexpr uint16_t X = 0x0004;
static constexpr uint16_t Y = 0x0008;
static constexpr uint16_t L3 = 0x0010;
static constexpr uint16_t R3 = 0x0020;
static constexpr uint16_t BACK = 0x0040;
static constexpr uint16_t START = 0x0080;
static constexpr uint16_t LB = 0x0100;
static constexpr uint16_t RB = 0x0200;
static constexpr uint16_t SYS = 0x0400;
static constexpr uint16_t MISC = 0x0800;
};
using Chatpad = std::array<uint8_t, 3>;
Gamepad()
{
reset_pad();
reset_rumble();
setup_deadzones();
};
~Gamepad() = default;
class Values //Store and scale values
{
public:
Values() : type_(Type::UINT8), uint8_(0) {};
~Values() = default;
inline int16_t int16(const bool invert = false) const
{
int16_t result = 0;
switch (type_.load())
{
case Type::INT16:
result = int16_.load();
break;
case Type::UINT16:
result = uint16_to_int16(uint16_.load());
break;
case Type::UINT8:
result = uint8_to_int16(uint8_.load());
break;
case Type::INT8:
result = int8_to_int16(int8_.load());
break;
default:
break;
}
return invert ? invert_joy(result) : result;
}
inline uint16_t uint16(const bool invert = false) const
{
uint16_t result = 0;
switch (type_.load())
{
case Type::UINT16:
result = uint16_.load();
break;
case Type::INT16:
result = int16_to_uint16(int16_.load());
break;
case Type::UINT8:
result = uint8_to_uint16(uint8_.load());
break;
case Type::INT8:
result = int8_to_uint16(int8_.load());
break;
}
return invert ? invert_joy(result) : result;
}
inline uint8_t uint8(const bool invert = false) const
{
uint8_t result = 0;
switch (type_.load())
{
case Type::UINT8:
result = uint8_.load();
break;
case Type::INT16:
result = int16_to_uint8(int16_.load());
break;
case Type::UINT16:
result = uint16_to_uint8(uint16_.load());
break;
case Type::INT8:
result = int8_to_uint8(int8_.load());
break;
}
return invert ? invert_joy(result) : result;
}
inline int8_t int8(bool invert = false) const
{
int8_t result = 0;
switch (type_.load())
{
case Type::INT8:
result = int8_.load();
break;
case Type::INT16:
result = int16_to_int8(int16_.load());
break;
case Type::UINT16:
result = uint16_to_int8(uint16_.load());
break;
case Type::UINT8:
result = uint8_to_int8(uint8_.load());
break;
}
return invert ? invert_joy(result) : result;
}
inline void set_value(int16_t value) { type_.store(Type::INT16); int16_.store(value); }
inline void set_value(uint16_t value) { type_.store(Type::UINT16); uint16_.store(value); }
inline void set_value(int8_t value) { type_.store(Type::INT8); int8_.store(value); }
inline void set_value(uint8_t value) { type_.store(Type::UINT8); uint8_.store(value); }
inline void reset_value() { type_ = Type::INT16; uint16_ = 0; };
static inline uint8_t invert_joy(uint8_t value)
{
return static_cast<uint8_t>(UINT_8::MAX - value);
}
static inline int8_t invert_joy(int8_t value)
{
return (value == std::numeric_limits<int8_t>::min()) ? std::numeric_limits<int8_t>::max() : -value;
}
static inline uint16_t invert_joy(uint16_t value)
{
return static_cast<uint16_t>(std::numeric_limits<uint16_t>::max() - value);
}
static inline int16_t invert_joy(int16_t value)
{
return (value == std::numeric_limits<int16_t>::min()) ? std::numeric_limits<int16_t>::max() : -value;
}
static inline uint8_t int16_to_uint8(int16_t value)
{
uint16_t shifted_value = static_cast<uint16_t>(value + UINT_16::MID);
return static_cast<uint8_t>(shifted_value >> 8);
}
static inline uint16_t int16_to_uint16(int16_t value)
{
return static_cast<uint16_t>(value + UINT_16::MID);
}
static inline int8_t int16_to_int8(int16_t value)
{
return static_cast<int8_t>((value + UINT_16::MID) >> 8);
}
static inline uint8_t uint16_to_uint8(uint16_t value)
{
return static_cast<uint8_t>(value >> 8);
}
static inline int16_t uint16_to_int16(uint16_t value)
{
return static_cast<int16_t>(value - UINT_16::MID);
}
static inline int8_t uint16_to_int8(uint16_t value)
{
return static_cast<int8_t>((value >> 8) - UINT_8::MID);
}
static inline int16_t uint8_to_int16(uint8_t value)
{
return static_cast<int16_t>((static_cast<int32_t>(value) << 8) - UINT_16::MID);
}
static inline uint16_t uint8_to_uint16(uint8_t value)
{
return static_cast<uint16_t>(value) << 8;
}
static inline int8_t uint8_to_int8(uint8_t value)
{
return static_cast<int8_t>(value - UINT_8::MID);
}
static inline int16_t int8_to_int16(int8_t value)
{
return static_cast<int16_t>(value) << 8;
}
static inline uint16_t int8_to_uint16(int8_t value)
{
return static_cast<uint16_t>((value + UINT_8::MID) << 8);
}
static inline uint8_t int8_to_uint8(int8_t value)
{
return static_cast<uint8_t>(value + UINT_8::MID);
}
static inline uint8_t int10_to_uint8(int32_t value)
{
value = value - INT_10::MIN;
if (value >= UINT_10::MAX)
{
return UINT_8::MAX;
}
else if (value <= 0)
{
return 0;
}
return static_cast<uint8_t>(value >> 2);
}
static inline int16_t int10_to_int16(int32_t value)
{
constexpr int32_t scale_factor = INT_16::MAX - INT_16::MIN;
constexpr int32_t range = INT_10::MAX - INT_10::MIN;
if (value >= INT_10::MAX)
{
return INT_16::MAX;
}
else if (value <= INT_10::MIN)
{
return INT_16::MIN;
}
int32_t scaled_value = (value - INT_10::MIN) * scale_factor;
return static_cast<int16_t>(scaled_value / range + INT_16::MIN);
}
static inline uint8_t uint10_to_uint8(int32_t value)
{
if (value > UINT_10::MAX)
{
value = UINT_10::MAX;
}
else if (value < 0)
{
value = 0;
}
return static_cast<uint8_t>(value >> 2);
}
private:
enum class Type { INT16, UINT16, INT8, UINT8 };
std::atomic<Type> type_;
union {
std::atomic<int16_t> int16_;
std::atomic<uint16_t> uint16_;
std::atomic<int8_t> int8_;
std::atomic<uint8_t> uint8_;
};
}; //Values
//Get
inline bool analog_enabled() const { return analog_enabled_; }
inline uint8_t get_dpad_buttons() const { return pad_state_.dpad; }
inline uint16_t get_buttons() const { return pad_state_.buttons; }
inline bool get_dpad_up() const { return (pad_state_.dpad & DPad::UP); }
inline bool get_dpad_down() const { return (pad_state_.dpad & DPad::DOWN); }
inline bool get_dpad_left() const { return (pad_state_.dpad & DPad::LEFT); }
inline bool get_dpad_right() const { return (pad_state_.dpad & DPad::RIGHT); }
inline bool get_button_a() const { return (pad_state_.buttons & Button::A); }
inline bool get_button_b() const { return (pad_state_.buttons & Button::B); }
inline bool get_button_x() const { return (pad_state_.buttons & Button::X); }
inline bool get_button_y() const { return (pad_state_.buttons & Button::Y); }
inline bool get_button_lb() const { return (pad_state_.buttons & Button::LB); }
inline bool get_button_rb() const { return (pad_state_.buttons & Button::RB); }
inline bool get_button_l3() const { return (pad_state_.buttons & Button::L3); }
inline bool get_button_r3() const { return (pad_state_.buttons & Button::R3); }
inline bool get_button_start() const { return (pad_state_.buttons & Button::START); }
inline bool get_button_back() const { return (pad_state_.buttons & Button::BACK); }
inline bool get_button_sys() const { return (pad_state_.buttons & Button::SYS); }
inline bool get_button_misc() const { return (pad_state_.buttons & Button::MISC); }
inline const Values& get_trigger_l() const { return pad_state_.triggers.l; }
inline const Values& get_trigger_r() const { return pad_state_.triggers.r; }
inline const Values& get_joystick_lx() const { return pad_state_.joysticks.lx; }
inline const Values& get_joystick_ly() const { return pad_state_.joysticks.ly; }
inline const Values& get_joystick_rx() const { return pad_state_.joysticks.rx; }
inline const Values& get_joystick_ry() const { return pad_state_.joysticks.ry; }
inline uint8_t get_analog_up() const { return pad_state_.analog_buttons.up; }
inline uint8_t get_analog_down() const { return pad_state_.analog_buttons.down; }
inline uint8_t get_analog_left() const { return pad_state_.analog_buttons.left; }
inline uint8_t get_analog_right() const { return pad_state_.analog_buttons.right; }
inline uint8_t get_analog_a() const { return pad_state_.analog_buttons.a; }
inline uint8_t get_analog_b() const { return pad_state_.analog_buttons.b; }
inline uint8_t get_analog_x() const { return pad_state_.analog_buttons.x; }
inline uint8_t get_analog_y() const { return pad_state_.analog_buttons.y; }
inline uint8_t get_analog_lb() const { return pad_state_.analog_buttons.lb; }
inline uint8_t get_analog_rb() const { return pad_state_.analog_buttons.rb; }
inline const Values& get_rumble_l() const { return rumble_state_.l; }
inline const Values& get_rumble_r() const { return rumble_state_.r; }
inline Chatpad get_chatpad() const
{
return
{
pad_state_.chatpad[0],
pad_state_.chatpad[1],
pad_state_.chatpad[2]
};
}
//Set
void set_analog_enabled(bool value) { analog_enabled_ = value; }
void set_profile(const UserProfile& user_profile)
{
profile_ = user_profile;
setup_deadzones();
}
inline void set_dpad(uint8_t value) { pad_state_.dpad = value; }
inline void set_dpad_up() { pad_state_.dpad |= profile_.dpad_up; }
inline void set_dpad_down() { pad_state_.dpad |= profile_.dpad_down; }
inline void set_dpad_left() { pad_state_.dpad |= profile_.dpad_left; }
inline void set_dpad_right() { pad_state_.dpad |= profile_.dpad_right; }
inline void set_dpad_up_left() { pad_state_.dpad |= (profile_.dpad_up | profile_.dpad_left); }
inline void set_dpad_up_right() { pad_state_.dpad |= (profile_.dpad_up | profile_.dpad_right); }
inline void set_dpad_down_left() { pad_state_.dpad |= (profile_.dpad_down | profile_.dpad_left); }
inline void set_dpad_down_right() { pad_state_.dpad |= (profile_.dpad_down | profile_.dpad_right); }
inline void set_buttons(uint16_t value) { pad_state_.buttons = value; }
inline void set_button_a() { pad_state_.buttons |= profile_.button_a; }
inline void set_button_b() { pad_state_.buttons |= profile_.button_b; }
inline void set_button_x() { pad_state_.buttons |= profile_.button_x; }
inline void set_button_y() { pad_state_.buttons |= profile_.button_y; }
inline void set_button_lb() { pad_state_.buttons |= profile_.button_lb; }
inline void set_button_rb() { pad_state_.buttons |= profile_.button_rb; }
inline void set_button_l3() { pad_state_.buttons |= profile_.button_l3; }
inline void set_button_r3() { pad_state_.buttons |= profile_.button_r3; }
inline void set_button_start() { pad_state_.buttons |= profile_.button_start; }
inline void set_button_back() { pad_state_.buttons |= profile_.button_back; }
inline void set_button_sys() { pad_state_.buttons |= profile_.button_sys; }
inline void set_button_misc() { pad_state_.buttons |= profile_.button_misc; }
inline void set_analog_up(uint8_t value) { *analog_map_[profile_.analog_off_up] = value; }
inline void set_analog_down(uint8_t value) { *analog_map_[profile_.analog_off_down] = value; }
inline void set_analog_left(uint8_t value) { *analog_map_[profile_.analog_off_left] = value; }
inline void set_analog_right(uint8_t value) { *analog_map_[profile_.analog_off_right] = value; }
inline void set_analog_a(uint8_t value) { *analog_map_[profile_.analog_off_a] = value; }
inline void set_analog_b(uint8_t value) { *analog_map_[profile_.analog_off_b] = value; }
inline void set_analog_x(uint8_t value) { *analog_map_[profile_.analog_off_x] = value; }
inline void set_analog_y(uint8_t value) { *analog_map_[profile_.analog_off_y] = value; }
inline void set_analog_lb(uint8_t value) { *analog_map_[profile_.analog_off_lb] = value; }
inline void set_analog_rb(uint8_t value) { *analog_map_[profile_.analog_off_rb] = value; }
inline void set_trigger_l(const uint8_t value) { pad_state_.triggers.l.set_value((value > dz_trig_uint8_.l) ? value : UINT_8::MIN); }
inline void set_trigger_l(const uint16_t value) { pad_state_.triggers.l.set_value((value > dz_trig_uint16_.l) ? value : UINT_16::MIN); }
inline void set_trigger_r(const uint8_t value) { pad_state_.triggers.r.set_value((value > dz_trig_uint8_.r) ? value : UINT_8::MIN); }
inline void set_trigger_r(const uint16_t value) { pad_state_.triggers.r.set_value((value > dz_trig_uint16_.r) ? value : UINT_16::MIN); }
inline void set_joystick_lx(const int16_t value, const bool invert = false) { pad_state_.joysticks.lx.set_value((value < dz_joy_int16_.l_neg || value > dz_joy_int16_.l_pos) ? (invert ? Values::invert_joy(value) : value) : INT_16::MID); }
inline void set_joystick_lx(const uint8_t value, const bool invert = false) { pad_state_.joysticks.lx.set_value((value < dz_joy_uint8_.l_neg || value > dz_joy_uint8_.l_pos) ? (invert ? Values::invert_joy(value) : value) : UINT_8::MID); }
inline void set_joystick_ly(const int16_t value, const bool invert = false) { pad_state_.joysticks.ly.set_value((value < dz_joy_int16_.l_neg || value > dz_joy_int16_.l_pos) ? ((invert ^ profile_.invert_ly) ? Values::invert_joy(value) : value) : INT_16::MID); }
inline void set_joystick_ly(const uint8_t value, const bool invert = false) { pad_state_.joysticks.ly.set_value((value < dz_joy_uint8_.l_neg || value > dz_joy_uint8_.l_pos) ? ((invert ^ profile_.invert_ly) ? Values::invert_joy(value) : value) : UINT_8::MID); }
inline void set_joystick_rx(const int16_t value, const bool invert = false) { pad_state_.joysticks.rx.set_value((value < dz_joy_int16_.r_neg || value > dz_joy_int16_.r_pos) ? (invert ? Values::invert_joy(value) : value) : INT_16::MID); }
inline void set_joystick_rx(const uint8_t value, const bool invert = false) { pad_state_.joysticks.rx.set_value((value < dz_joy_uint8_.r_neg || value > dz_joy_uint8_.r_pos) ? (invert ? Values::invert_joy(value) : value) : UINT_8::MID); }
inline void set_joystick_ry(const int16_t value, const bool invert = false) { pad_state_.joysticks.ry.set_value((value < dz_joy_int16_.r_neg || value > dz_joy_int16_.r_pos) ? ((invert ^ profile_.invert_ry) ? Values::invert_joy(value) : value) : INT_16::MID); }
inline void set_joystick_ry(const uint8_t value, const bool invert = false) { pad_state_.joysticks.ry.set_value((value < dz_joy_uint8_.r_neg || value > dz_joy_uint8_.r_pos) ? ((invert ^ profile_.invert_ry) ? Values::invert_joy(value) : value) : UINT_8::MID); }
// 10 bit methods for weird stuff
inline void set_trigger_l_uint10(const int32_t value)
{
uint8_t new_value = Values::uint10_to_uint8(value);
pad_state_.triggers.l.set_value((new_value > dz_trig_uint8_.l) ? new_value : UINT_8::MIN);
}
inline void set_trigger_r_uint10(const int32_t value)
{
uint8_t new_value = Values::uint10_to_uint8(value);
pad_state_.triggers.r.set_value((new_value > dz_trig_uint8_.r) ? new_value : UINT_8::MIN);
}
inline void set_joystick_lx_int10(const int32_t value, const bool invert = false)
{
uint8_t new_value = Values::int10_to_uint8(value);
pad_state_.joysticks.lx.set_value((new_value < dz_joy_uint8_.l_neg || new_value > dz_joy_uint8_.l_pos) ? (invert ? Values::invert_joy(new_value) : new_value) : UINT_8::MID);
}
inline void set_joystick_ly_int10(const int32_t value, const bool invert = false)
{
uint8_t new_value = Values::int10_to_uint8(value);
pad_state_.joysticks.ly.set_value((new_value < dz_joy_uint8_.l_neg || new_value > dz_joy_uint8_.l_pos) ? ((invert ^ profile_.invert_ly) ? Values::invert_joy(new_value) : new_value) : UINT_8::MID);
}
inline void set_joystick_rx_int10(const int32_t value, const bool invert = false)
{
uint8_t new_value = Values::int10_to_uint8(value);
pad_state_.joysticks.rx.set_value((new_value < dz_joy_uint8_.r_neg || new_value > dz_joy_uint8_.r_pos) ? (invert ? Values::invert_joy(new_value) : new_value) : UINT_8::MID);
}
inline void set_joystick_ry_int10(const int32_t value, const bool invert = false)
{
uint8_t new_value = Values::int10_to_uint8(value);
pad_state_.joysticks.ry.set_value((new_value < dz_joy_uint8_.r_neg || new_value > dz_joy_uint8_.r_pos) ? ((invert ^ profile_.invert_ry) ? Values::invert_joy(new_value) : new_value) : UINT_8::MID);
}
// inline void set_joystick_lx_int10(const int32_t value, const bool invert = false)
// {
// int16_t new_value = Values::int10_to_int16(value);
// pad_state_.joysticks.lx.set_value((new_value < dz_joy_int16_.l_neg || new_value > dz_joy_int16_.l_pos) ? (invert ? Values::invert_joy(new_value) : new_value) : INT_16::MID);
// }
// inline void set_joystick_ly_int10(const int32_t value, const bool invert = false)
// {
// int16_t new_value = Values::int10_to_int16(value);
// pad_state_.joysticks.ly.set_value((new_value < dz_joy_int16_.l_neg || new_value > dz_joy_int16_.l_pos) ? ((invert ^ profile_.invert_ly) ? Values::invert_joy(new_value) : new_value) : INT_16::MID);
// }
// inline void set_joystick_rx_int10(const int32_t value, const bool invert = false)
// {
// int16_t new_value = Values::int10_to_int16(value);
// pad_state_.joysticks.rx.set_value((new_value < dz_joy_int16_.r_neg || new_value > dz_joy_int16_.r_pos) ? (invert ? Values::invert_joy(new_value) : new_value) : INT_16::MID);
// }
// inline void set_joystick_ry_int10(const int32_t value, const bool invert = false)
// {
// int16_t new_value = Values::int10_to_int16(value);
// pad_state_.joysticks.ry.set_value((new_value < dz_joy_int16_.r_neg || new_value > dz_joy_int16_.r_pos) ? ((invert ^ profile_.invert_ry) ? Values::invert_joy(new_value) : new_value) : INT_16::MID);
// }
inline void set_rumble_l(uint8_t value) { rumble_state_.l.set_value(value); }
inline void set_rumble_l(uint16_t value) { rumble_state_.l.set_value(value); }
inline void set_rumble_r(uint8_t value) { rumble_state_.r.set_value(value); }
inline void set_rumble_r(uint16_t value) { rumble_state_.r.set_value(value); }
inline void set_chatpad(const Chatpad& chatpad_array)
{
pad_state_.chatpad[0] = chatpad_array[0];
pad_state_.chatpad[1] = chatpad_array[1];
pad_state_.chatpad[2] = chatpad_array[2];
}
inline void reset_buttons() { pad_state_.dpad = 0; pad_state_.buttons = 0; }
inline void reset_pad()
{
pad_state_.dpad = 0;
pad_state_.buttons = 0;
pad_state_.triggers.l.set_value(UINT_8::MIN);
pad_state_.triggers.r.set_value(UINT_8::MIN);
pad_state_.joysticks.lx.set_value(UINT_8::MID);
pad_state_.joysticks.ly.set_value(UINT_8::MID);
pad_state_.joysticks.rx.set_value(UINT_8::MID);
pad_state_.joysticks.ry.set_value(UINT_8::MID);
std::memset(&pad_state_.analog_buttons, 0, sizeof(PadState::AnalogButtons));
std::memset(&pad_state_.chatpad, 0, sizeof(Chatpad));
}
inline void reset_rumble()
{
rumble_state_.l.set_value(UINT_8::MIN);
rumble_state_.l.set_value(UINT_8::MIN);
}
private:
struct PadState
{
uint8_t dpad{0};
uint16_t buttons{0};
struct Triggers
{
Values l;
Values r;
} triggers;
struct Joysticks
{
Values lx;
Values ly;
Values rx;
Values ry;
} joysticks;
struct AnalogButtons
{
uint8_t up{0};
uint8_t down{0};
uint8_t left{0};
uint8_t right{0};
uint8_t a{0};
uint8_t b{0};
uint8_t x{0};
uint8_t y{0};
uint8_t lb{0};
uint8_t rb{0};
} analog_buttons;
std::array<uint8_t, 3> chatpad = { 0, 0, 0 };
};
struct Rumble
{
Values l;
Values r;
};
bool analog_enabled_{false};
UserProfile profile_;
template<typename type>
struct DZTrigger
{
type l{0};
type r{0};
};
template<typename type>
struct DZJoystick
{
type l_neg;
type l_pos;
type r_neg;
type r_pos;
DZJoystick()
{
if constexpr (std::is_same_v<type, uint8_t>) { l_neg = l_pos = r_neg = r_pos = UINT_8::MID; }
else if constexpr (std::is_same_v<type, int16_t>) { l_neg = l_pos = r_neg = r_pos = 0; }
}
};
DZTrigger<uint8_t> dz_trig_uint8_;
DZTrigger<uint16_t> dz_trig_uint16_;
DZJoystick<int16_t> dz_joy_int16_;
DZJoystick<uint8_t> dz_joy_uint8_;
PadState pad_state_;
Rumble rumble_state_;
std::array<uint8_t*, sizeof(PadState::AnalogButtons)> analog_map_ =
{
&pad_state_.analog_buttons.up,
&pad_state_.analog_buttons.down,
&pad_state_.analog_buttons.left,
&pad_state_.analog_buttons.right,
&pad_state_.analog_buttons.a,
&pad_state_.analog_buttons.b,
&pad_state_.analog_buttons.x,
&pad_state_.analog_buttons.y,
&pad_state_.analog_buttons.lb,
&pad_state_.analog_buttons.rb
};
void setup_deadzones() //Deadzones in the profile are 0-255 (0-100%)
{
dz_trig_uint8_.l = profile_.dz_trigger_l;
dz_trig_uint8_.r = profile_.dz_trigger_r;
dz_trig_uint16_.l = Values::uint8_to_uint16(profile_.dz_trigger_l);
dz_trig_uint16_.r = Values::uint8_to_uint16(profile_.dz_trigger_r);
dz_joy_uint8_.l_neg = UINT_8::MID - (profile_.dz_joystick_l / 2);
dz_joy_uint8_.l_pos = UINT_8::MID + (profile_.dz_joystick_l / 2);
dz_joy_uint8_.r_neg = UINT_8::MID - (profile_.dz_joystick_r / 2);
dz_joy_uint8_.r_pos = UINT_8::MID + (profile_.dz_joystick_r / 2);
dz_joy_int16_.l_neg = Values::uint8_to_int16(dz_joy_uint8_.l_neg);
dz_joy_int16_.l_pos = Values::uint8_to_int16(dz_joy_uint8_.l_pos);
dz_joy_int16_.r_neg = Values::uint8_to_int16(dz_joy_uint8_.r_neg);
dz_joy_int16_.r_pos = Values::uint8_to_int16(dz_joy_uint8_.r_pos);
}
};
#endif // _GAMEPAD_H_

View File

@@ -0,0 +1,630 @@
#include <cstdint>
#include <array>
#include <cstring>
#include <atomic>
#include <pico/i2c_slave.h>
#include <pico/time.h>
#include <hardware/gpio.h>
#include <hardware/i2c.h>
#include "board_config.h"
#include "I2CDriver/i2c_driver_4ch.h"
namespace i2c_driver_4ch {
enum class Mode { MASTER = 0, SLAVE };
enum class ReportID : uint8_t { UNKNOWN = 0, PAD, STATUS, CONNECT, DISCONNECT };
enum class SlaveStatus : uint8_t { NC = 0, NOT_READY, READY, RESP_OK };
#pragma pack(push, 1)
struct ReportIn
{
uint8_t report_len;
uint8_t report_id;
uint16_t buttons;
uint8_t dpad;
uint8_t trigger_l;
uint8_t trigger_r;
int16_t joystick_lx;
int16_t joystick_ly;
int16_t joystick_rx;
int16_t joystick_ry;
std::array<uint8_t, 3> chatpad;
ReportIn()
{
std::memset(this, 0, sizeof(ReportIn));
report_len = sizeof(ReportIn);
report_id = static_cast<uint8_t>(ReportID::PAD);
}
};
static_assert(sizeof(ReportIn) == 18, "I2CDriver::ReportIn size mismatch");
struct ReportOut
{
uint8_t report_len;
uint8_t report_id;
uint8_t rumble_l;
uint8_t rumble_r;
ReportOut()
{
std::memset(this, 0, sizeof(ReportOut));
report_len = sizeof(ReportOut);
report_id = static_cast<uint8_t>(ReportID::PAD);
}
};
static_assert(sizeof(ReportOut) == 4, "I2CDriver::ReportOut size mismatch");
struct ReportStatus
{
uint8_t report_len;
uint8_t report_id;
uint8_t status;
ReportStatus()
{
std::memset(this, 0, sizeof(ReportStatus));
report_len = sizeof(ReportStatus);
report_id = static_cast<uint8_t>(ReportID::STATUS);
status = static_cast<uint8_t>(SlaveStatus::NC);
}
};
static_assert(sizeof(ReportStatus) == 3, "I2CDriver::ReportStatus size mismatch");
#pragma pack(pop)
static constexpr size_t MAX_BUFFER_SIZE = std::max(sizeof(ReportStatus), std::max(sizeof(ReportIn), sizeof(ReportOut)));
namespace i2c_master
{
struct Master
{
std::atomic<bool> enabled{false};
struct Slave
{
uint8_t address{0xFF};
Gamepad* gamepad{nullptr};
SlaveStatus status{SlaveStatus::NC};
std::atomic<bool> enabled{false};
uint32_t last_update{0};
};
std::array<Slave, MAX_GAMEPADS - 1> slaves;
};
Master master_;
bool slave_detected(uint8_t address);
bool send_in_report(Master::Slave& slave);
bool get_out_report(Master::Slave& slave);
bool update_slave_enabled(uint8_t address, bool connected);
void notify_tud_deinit();
void update_slave_status(Master::Slave& slave);
void notify_tuh_mounted(HostDriver::Type host_type);
void notify_tuh_unmounted(HostDriver::Type host_type);
void notify_xbox360w_connected(uint8_t idx);
void notify_xbox360w_disconnected(uint8_t idx);
bool is_active();
void process();
void init(std::array<Gamepad, MAX_GAMEPADS>& gamepads);
}
namespace i2c_slave
{
struct Slave
{
uint8_t address{0xFF};
Gamepad* gamepad{nullptr};
std::atomic<bool> i2c_enabled{false};
std::atomic<bool> tuh_enabled{false};
};
Slave slave_;
void process_in_report(ReportIn* report_in);
void fill_out_report(ReportOut* report_out);
ReportID get_report_id(uint8_t* buffer_in);
void slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event);
bool is_active();
void notify_tuh_mounted();
void notify_tuh_unmounted();
void init(uint8_t address, Gamepad& gamepad);
}
namespace i2c_master {
bool is_active()
{
return master_.enabled.load();
}
bool slave_detected(uint8_t address)
{
uint8_t dummy_data = 0;
int result = i2c_write_timeout_us(I2C_PORT, address, &dummy_data, 0, false, 1000);
return (result >= 0);
}
void notify_tuh_mounted(HostDriver::Type host_type)
{
if (host_type == HostDriver::Type::XBOX360W)
{
master_.enabled.store(true);
}
}
void notify_tuh_unmounted(HostDriver::Type host_type)
{
master_.enabled.store(false);
}
void notify_xbox360w_connected(uint8_t idx)
{
if (idx < 1 || idx >= MAX_GAMEPADS)
{
return;
}
master_.slaves[idx - 1].enabled.store(true);
}
void notify_xbox360w_disconnected(uint8_t idx)
{
if (idx < 1 || idx >= MAX_GAMEPADS)
{
return;
}
master_.slaves[idx - 1].enabled.store(false);
// bool any_connected = false;
// for (auto& slave : master_.slaves)
// {
// if (slave.enabled.load())
// {
// any_connected = true;
// break;
// }
// }
// if (!any_connected)
// {
// master_.enabled.store(false);
// }
}
bool send_in_report(Master::Slave& slave)
{
static ReportIn report_in = ReportIn();
report_in.buttons = slave.gamepad->get_buttons();
report_in.dpad = slave.gamepad->get_dpad_buttons();
report_in.trigger_l = slave.gamepad->get_trigger_l().uint8();
report_in.trigger_r = slave.gamepad->get_trigger_r().uint8();
report_in.joystick_lx = slave.gamepad->get_joystick_lx().int16();
report_in.joystick_ly = slave.gamepad->get_joystick_ly().int16();
report_in.joystick_rx = slave.gamepad->get_joystick_rx().int16();
report_in.joystick_ry = slave.gamepad->get_joystick_ry().int16();
report_in.chatpad = slave.gamepad->get_chatpad();
int count = i2c_write_blocking(I2C_PORT, slave.address, reinterpret_cast<uint8_t*>(&report_in), sizeof(report_in), false);
return (count == sizeof(ReportIn));
}
bool get_out_report(Master::Slave& slave)
{
static ReportOut report_out = ReportOut();
int count = i2c_read_blocking(I2C_PORT, slave.address, reinterpret_cast<uint8_t*>(&report_out), sizeof(ReportOut), false);
if (count != sizeof(ReportOut))
{
return false;
}
slave.gamepad->set_rumble_l(report_out.rumble_l);
slave.gamepad->set_rumble_r(report_out.rumble_r);
return true;
}
bool update_slave_enabled(uint8_t address, bool connected)
{
ReportStatus report;
report.report_id = connected ? static_cast<uint8_t>(ReportID::CONNECT) : static_cast<uint8_t>(ReportID::DISCONNECT);
int count = i2c_write_blocking(I2C_PORT, address, reinterpret_cast<uint8_t*>(&report), sizeof(ReportStatus), false);
if (count == sizeof(ReportStatus))
{
count = i2c_read_blocking(I2C_PORT, address, reinterpret_cast<uint8_t*>(&report), sizeof(ReportStatus), false);
return (static_cast<SlaveStatus>(report.status) == SlaveStatus::RESP_OK);
}
return false;
}
void notify_tud_deinit()
{
for (auto& slave : master_.slaves)
{
if (slave.status != SlaveStatus::NC)
{
int retries = 0;
while (!update_slave_enabled(slave.address, false) && retries < 6)
{
sleep_ms(1);
++retries;
}
}
}
}
void update_slave_status(Master::Slave& slave)
{
bool slave_enabled = slave.enabled.load();
if (!slave_detected(slave.address))
{
slave.status = SlaveStatus::NC;
return;
}
if (!update_slave_enabled(slave.address, slave_enabled))
{
slave.status = SlaveStatus::NOT_READY;
return;
}
if (slave_enabled)
{
ReportStatus report = ReportStatus();
int count = 0;
report.report_id = static_cast<uint8_t>(ReportID::STATUS);
count = i2c_write_blocking(I2C_PORT, slave.address, reinterpret_cast<uint8_t*>(&report), sizeof(ReportStatus), false);
if (count == sizeof(ReportStatus))
{
count = i2c_read_blocking(I2C_PORT, slave.address, reinterpret_cast<uint8_t*>(&report), sizeof(ReportStatus), false);
slave.status = (count == sizeof(ReportStatus)) ? static_cast<SlaveStatus>(report.status) : SlaveStatus::NC;
}
else
{
slave.status = SlaveStatus::NOT_READY;
}
}
else
{
slave.status = SlaveStatus::NOT_READY;
}
}
void process()
{
for (auto& slave : master_.slaves)
{
if ((time_us_32() / 1000) - slave.last_update > 1000)
{
update_slave_status(slave);
slave.last_update = time_us_32() / 1000;
}
sleep_us(100);
}
if (!master_.enabled.load())
{
return;
}
for (auto& slave : master_.slaves)
{
if (slave.status == SlaveStatus::READY)
{
if (send_in_report(slave))
{
get_out_report(slave);
}
sleep_us(100);
}
}
}
void init(std::array<Gamepad, MAX_GAMEPADS>& gamepads)
{
for (uint8_t i = 0; i < master_.slaves.size(); ++i)
{
master_.slaves[i].address = i + 1;
master_.slaves[i].gamepad = &gamepads[i + 1];
}
}
} // namespace i2c_master
namespace i2c_slave {
bool is_active()
{
return slave_.i2c_enabled.load();
}
void notify_tuh_mounted()
{
slave_.tuh_enabled.store(true);
}
void notify_tuh_unmounted()
{
slave_.tuh_enabled.store(false);
}
void process_in_report(ReportIn* report_in)
{
Gamepad& gamepad = *slave_.gamepad;
gamepad.set_buttons(report_in->buttons);
gamepad.set_dpad(report_in->dpad);
gamepad.set_trigger_l(report_in->trigger_l);
gamepad.set_trigger_r(report_in->trigger_r);
gamepad.set_joystick_lx(report_in->joystick_lx);
gamepad.set_joystick_ly(report_in->joystick_ly);
gamepad.set_joystick_rx(report_in->joystick_rx);
gamepad.set_joystick_ry(report_in->joystick_ry);
gamepad.set_chatpad(report_in->chatpad);
}
void fill_out_report(ReportOut* report_out)
{
Gamepad& gamepad = *slave_.gamepad;
report_out->report_len = sizeof(ReportOut);
report_out->report_id = static_cast<uint8_t>(ReportID::PAD);
report_out->rumble_l = gamepad.get_rumble_l().uint8();
report_out->rumble_r = gamepad.get_rumble_r().uint8();
}
ReportID get_report_id(uint8_t* buffer_in)
{
switch (static_cast<ReportID>(buffer_in[1]))
{
case ReportID::PAD:
if (buffer_in[0] == sizeof(ReportIn))
{
return ReportID::PAD;
}
break;
case ReportID::DISCONNECT:
if (buffer_in[0] == sizeof(ReportStatus))
{
return ReportID::DISCONNECT;
}
break;
case ReportID::CONNECT:
if (buffer_in[0] == sizeof(ReportStatus))
{
return ReportID::CONNECT;
}
break;
case ReportID::STATUS:
if (buffer_in[0] == sizeof(ReportStatus))
{
return ReportID::STATUS;
}
break;
default:
break;
}
return ReportID::UNKNOWN;
}
void slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event)
{
static int count = 0;
static std::array<uint8_t, MAX_BUFFER_SIZE> buffer_in{0};
static std::array<uint8_t, MAX_BUFFER_SIZE> buffer_out{0};
switch (event)
{
case I2C_SLAVE_RECEIVE: // master has written
if (count < MAX_BUFFER_SIZE)
{
buffer_in.data()[count] = i2c_read_byte_raw(i2c);
++count;
}
break;
case I2C_SLAVE_FINISH: // master signalled Stop / Restart
// Each master write has an ID indicating the type of data to send back on the next request
// Every write has an associated read
switch (get_report_id(buffer_in.data()))
{
case ReportID::PAD:
process_in_report(reinterpret_cast<ReportIn*>(buffer_in.data()));
fill_out_report(reinterpret_cast<ReportOut*>(buffer_out.data()));
break;
case ReportID::STATUS:
buffer_out.data()[0] = sizeof(ReportStatus);
buffer_out.data()[1] = static_cast<uint8_t>(ReportID::STATUS);
// if something is mounted by tuh, signal to not send gamepad data
buffer_out.data()[2] = slave_.tuh_enabled.load() ? static_cast<uint8_t>(SlaveStatus::NOT_READY) : static_cast<uint8_t>(SlaveStatus::READY);
break;
case ReportID::CONNECT:
slave_.i2c_enabled.store(true);
buffer_out.data()[0] = sizeof(ReportStatus);
buffer_out.data()[1] = static_cast<uint8_t>(ReportID::STATUS);
buffer_out.data()[2] = static_cast<uint8_t>(SlaveStatus::RESP_OK);
break;
case ReportID::DISCONNECT:
slave_.i2c_enabled.store(false);
buffer_out.data()[0] = sizeof(ReportStatus);
buffer_out.data()[1] = static_cast<uint8_t>(ReportID::STATUS);
buffer_out.data()[2] = static_cast<uint8_t>(SlaveStatus::RESP_OK);
break;
default:
break;
}
count = 0;
break;
case I2C_SLAVE_REQUEST: // master requesting data
i2c_write_raw_blocking(i2c, buffer_out.data(), buffer_out.data()[0]);
buffer_in.fill(0);
break;
default:
break;
}
}
void init(uint8_t address, Gamepad& gamepad)
{
slave_.address = address;
slave_.gamepad = &gamepad;
i2c_slave_init(I2C_PORT, slave_.address, &slave_handler);
}
} // namespace i2c_slave
Mode mode_{Mode::MASTER};
uint8_t get_address()
{
gpio_init(SLAVE_ADDR_PIN_1);
gpio_init(SLAVE_ADDR_PIN_2);
gpio_pull_up(SLAVE_ADDR_PIN_1);
gpio_pull_up(SLAVE_ADDR_PIN_2);
if (gpio_get(SLAVE_ADDR_PIN_1) == 1 && gpio_get(SLAVE_ADDR_PIN_2) == 1)
{
return 0x00;
}
else if (gpio_get(SLAVE_ADDR_PIN_1) == 1 && gpio_get(SLAVE_ADDR_PIN_2) == 0)
{
return 0x01;
}
else if (gpio_get(SLAVE_ADDR_PIN_1) == 0 && gpio_get(SLAVE_ADDR_PIN_2) == 1)
{
return 0x02;
}
else if (gpio_get(SLAVE_ADDR_PIN_1) == 0 && gpio_get(SLAVE_ADDR_PIN_2) == 0)
{
return 0x03;
}
return 0xFF;
}
//Public methods
//Call before core1 starts
void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepads)
{
uint8_t address = get_address();
if (address == 0xFF)
{
return;
}
i2c_init(I2C_PORT, I2C_BAUDRATE);
gpio_init(I2C_SDA_PIN);
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA_PIN);
gpio_init(I2C_SCL_PIN);
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SCL_PIN);
if (address < 1)
{
mode_ = Mode::MASTER;
i2c_master::init(gamepads);
}
else
{
mode_ = Mode::SLAVE;
i2c_slave::init(address, gamepads.front());
}
}
void process()
{
switch (mode_)
{
case Mode::MASTER:
i2c_master::process();
break;
case Mode::SLAVE:
return;
}
}
bool is_active()
{
switch (mode_)
{
case Mode::MASTER:
return i2c_master::is_active();
case Mode::SLAVE:
return i2c_slave::is_active();
}
return false;
}
//Called from core1
void notify_tuh_mounted(HostDriver::Type host_type)
{
switch (mode_)
{
case Mode::MASTER:
i2c_master::notify_tuh_mounted(host_type);
break;
case Mode::SLAVE:
i2c_slave::notify_tuh_mounted();
break;
}
}
//Called from core1
void notify_tuh_unmounted(HostDriver::Type host_type)
{
switch (mode_)
{
case Mode::MASTER:
i2c_master::notify_tuh_unmounted(host_type);
break;
case Mode::SLAVE:
i2c_slave::notify_tuh_unmounted();
break;
}
}
//Called from core1
void notify_xbox360w_connected(uint8_t idx)
{
switch (mode_)
{
case Mode::MASTER:
i2c_master::notify_xbox360w_connected(idx);
break;
case Mode::SLAVE:
return;
}
}
//Called from core1
void notify_xbox360w_disconnected(uint8_t idx)
{
switch (mode_)
{
case Mode::MASTER:
i2c_master::notify_xbox360w_disconnected(idx);
break;
case Mode::SLAVE:
return;
}
}
void notify_tud_deinit()
{
switch (mode_)
{
case Mode::MASTER:
i2c_master::notify_tud_deinit();
break;
case Mode::SLAVE:
return;
}
}
} // namespace I2CDriver

View File

@@ -0,0 +1,28 @@
#ifndef _I2C_DRIVER_H_
#define _I2C_DRIVER_H_
#include <cstdint>
#include "Gamepad.h"
#include "USBHost/HostManager.h"
// /* Master will only write/read slave if an Xbox360 wireless controller is connected for its corresponding slave.
// Slaves will read/write from the bus only if they don't have a device mounted by tusb host.
// Could be made to work so that a USB hub could be connected to port 1 and host 4 controllers, maybe later.
// This is run on core0 with the device stack, but some values are checked and modified by core1 (atomic types). */
namespace i2c_driver_4ch
{
void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepads);
void process();
bool is_active();
void notify_tud_deinit();
void notify_tuh_mounted(HostDriver::Type host_type = HostDriver::Type::UNKNOWN);
void notify_tuh_unmounted(HostDriver::Type host_type = HostDriver::Type::UNKNOWN);
void notify_xbox360w_connected(uint8_t idx);
void notify_xbox360w_disconnected(uint8_t idx);
}
#endif // _I2C_DRIVER_H_

View File

@@ -0,0 +1,281 @@
#include <array>
#include <cstring>
#include <pico/i2c_slave.h>
#include <hardware/gpio.h>
#include <hardware/i2c.h>
#include "Gamepad.h"
#include "board_config.h"
#include "Board/board_api.h"
#include "I2CDriver/i2c_driver_esp32.h"
namespace i2c_driver_esp32 {
//May expand commands in the future
enum class ReportID : uint8_t { UNKNOWN = 0, SET_PAD, GET_PAD };
#pragma pack(push, 1)
struct ReportIn
{
uint8_t report_len;
uint8_t report_id;
uint8_t index;
uint8_t dpad;
uint16_t buttons;
uint8_t trigger_l;
uint8_t trigger_r;
int16_t joystick_lx;
int16_t joystick_ly;
int16_t joystick_rx;
int16_t joystick_ry;
ReportIn()
{
std::memset(this, 0, sizeof(ReportIn));
report_len = sizeof(ReportIn);
report_id = static_cast<uint8_t>(ReportID::SET_PAD);
}
};
static_assert(sizeof(ReportIn) == 16, "i2c_driver_esp::ReportIn size mismatch");
struct ReportOut
{
uint8_t report_len;
uint8_t report_id;
uint8_t index;
uint8_t rumble_l;
uint8_t rumble_r;
ReportOut()
{
std::memset(this, 0, sizeof(ReportOut));
report_len = sizeof(ReportOut);
report_id = static_cast<uint8_t>(ReportID::GET_PAD);
}
};
static_assert(sizeof(ReportOut) == 5, "i2c_driver_esp::ReportOut size mismatch");
#pragma pack(pop)
static constexpr size_t MAX_BUFFER_SIZE = std::max(sizeof(ReportOut), sizeof(ReportIn));
static constexpr uint8_t I2C_ADDR = 0x01;
std::array<Gamepad*, MAX_GAMEPADS> gamepads_{nullptr};
std::atomic<bool> pad_connected_ = false;
inline void process_in_report(ReportIn* report_in)
{
if (report_in->index >= MAX_GAMEPADS)
{
return;
}
Gamepad* gamepad = gamepads_[report_in->index];
gamepad->set_buttons(report_in->buttons);
gamepad->set_dpad(report_in->dpad);
gamepad->set_trigger_l(report_in->trigger_l);
gamepad->set_trigger_r(report_in->trigger_r);
gamepad->set_joystick_lx_int10(static_cast<int32_t>(report_in->joystick_lx));
gamepad->set_joystick_ly_int10(static_cast<int32_t>(report_in->joystick_ly));
gamepad->set_joystick_rx_int10(static_cast<int32_t>(report_in->joystick_rx));
gamepad->set_joystick_ry_int10(static_cast<int32_t>(report_in->joystick_ry));
}
inline void fill_out_report(uint8_t index, ReportOut* report_out)
{
if (index >= MAX_GAMEPADS)
{
return;
}
Gamepad* gamepad = gamepads_[index];
report_out->report_len = sizeof(ReportOut);
report_out->report_id = static_cast<uint8_t>(ReportID::GET_PAD);
report_out->rumble_l = gamepad->get_rumble_l().uint8();
report_out->rumble_r = gamepad->get_rumble_r().uint8();
}
ReportID get_report_id(uint8_t* buffer_in)
{
switch (static_cast<ReportID>(buffer_in[1]))
{
case ReportID::SET_PAD:
if (buffer_in[0] == sizeof(ReportIn))
{
return ReportID::SET_PAD;
}
break;
//Unused ATM
// case ReportID::GET_PAD:
// if (buffer_in[0] == sizeof(ReportOut))
// {
// return ReportID::GET_PAD;
// }
// break;
default:
break;
}
return ReportID::UNKNOWN;
}
static inline void slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event)
{
static int count = 0;
static std::array<uint8_t, MAX_BUFFER_SIZE> buffer_in{0};
static std::array<uint8_t, MAX_BUFFER_SIZE> buffer_out{0};
switch (event)
{
case I2C_SLAVE_RECEIVE: // master has written
if (count < MAX_BUFFER_SIZE)
{
buffer_in.data()[count] = i2c_read_byte_raw(i2c);
++count;
}
break;
case I2C_SLAVE_FINISH: // master signalled Stop / Restart
if (get_report_id(buffer_in.data()) == ReportID::SET_PAD)
{
process_in_report(reinterpret_cast<ReportIn*>(buffer_in.data()));
}
count = 0;
break;
case I2C_SLAVE_REQUEST: // master requesting data
fill_out_report(reinterpret_cast<ReportIn*>(buffer_in.data())->index, reinterpret_cast<ReportOut*>(buffer_out.data()));
i2c_write_raw_blocking(i2c, buffer_out.data(), buffer_out.data()[0]);
buffer_in.fill(0);
break;
default:
break;
}
}
void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepad)
{
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
{
gamepads_[i] = &gamepad[i];
}
i2c_init(I2C_PORT, I2C_BAUDRATE);
gpio_init(I2C_SDA_PIN);
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA_PIN);
gpio_init(I2C_SCL_PIN);
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SCL_PIN);
i2c_slave_init(I2C_PORT, I2C_ADDR, &slave_handler);
}
bool pad_connected()
{
return pad_connected_.load();
}
} // namespace i2c_driver_esp
// #include <array>
// #include <cstring>
// #include <pico/i2c_slave.h>
// #include <hardware/gpio.h>
// #include <hardware/i2c.h>
// #include "Gamepad.h"
// #include "board_config.h"
// #include "Board/board_api.h"
// #include "I2CDriver/I2CDriver_ESP.h"
// namespace i2c_driver_esp {
// #pragma pack(push, 1)
// struct ReportIn
// {
// uint8_t dpad;
// uint16_t buttons;
// uint8_t trigger_l;
// uint8_t trigger_r;
// int16_t joystick_lx;
// int16_t joystick_ly;
// int16_t joystick_rx;
// int16_t joystick_ry;
// ReportIn()
// {
// std::memset(this, 0, sizeof(ReportIn));
// }
// };
// static_assert(sizeof(ReportIn) == 13, "i2c_driver_esp::ReportIn size mismatch");
// struct ReportOut
// {
// uint8_t rumble_l;
// uint8_t rumble_r;
// ReportOut()
// {
// std::memset(this, 0, sizeof(ReportOut));
// }
// };
// static_assert(sizeof(ReportOut) == 2, "i2c_driver_esp::ReportOut size mismatch");
// #pragma pack(pop)
// static constexpr uint8_t I2C_ADDR = 0x50;
// Gamepad* gamepad_ = nullptr;
// inline void get_in_report()
// {
// static ReportIn report_in = ReportIn();
// int count = i2c_read_timeout_us(I2C_PORT, I2C_ADDR, reinterpret_cast<uint8_t*>(&report_in), sizeof(ReportIn), false, 1000);
// if (count > 0)
// {
// gamepad_->set_buttons(report_in.buttons);
// gamepad_->set_dpad(report_in.dpad);
// gamepad_->set_trigger_l(report_in.trigger_l);
// gamepad_->set_trigger_r(report_in.trigger_r);
// gamepad_->set_joystick_lx_int10(report_in.joystick_lx);
// gamepad_->set_joystick_ly_int10(report_in.joystick_ly);
// gamepad_->set_joystick_rx_int10(report_in.joystick_rx);
// gamepad_->set_joystick_ry_int10(report_in.joystick_ry);
// }
// }
// inline void set_out_report()
// {
// static ReportOut report_out = ReportOut();
// report_out.rumble_l = gamepad_->get_rumble_l().uint8();
// report_out.rumble_r = gamepad_->get_rumble_r().uint8();
// i2c_write_timeout_us(I2C_PORT, I2C_ADDR, reinterpret_cast<uint8_t*>(&report_out), sizeof(ReportOut), false, 1000);
// }
// void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepad)
// {
// gamepad_ = &gamepad[0];
// i2c_init(I2C_PORT, I2C_BAUDRATE);
// gpio_init(I2C_SDA_PIN);
// gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
// gpio_pull_up(I2C_SDA_PIN);
// gpio_init(I2C_SCL_PIN);
// gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
// gpio_pull_up(I2C_SCL_PIN);
// }
// void task()
// {
// get_in_report();
// sleep_us(100);
// // set_out_report();
// // sleep_us(100);
// }
// } // namespace i2c_driver_esp

View File

@@ -0,0 +1,14 @@
#ifndef _I2CDRIVER_ESP_H_
#define _I2CDRIVER_ESP_H_
#include <cstdint>
#include "Gamepad.h"
namespace i2c_driver_esp32
{
void initialize(std::array<Gamepad, MAX_GAMEPADS>& gamepad);
bool pad_connected();
}
#endif // _I2CDRIVER_ESP_H_

View File

@@ -0,0 +1,27 @@
#ifndef _OGX_MINI_H_
#define _OGX_MINI_H_
#include <cstdint>
#include "UserSettings/UserSettings.h"
namespace OGXMini
{
enum class TUDStatus { INIT, DEINIT, NOCHANGE };
struct GPCheckContext
{
bool driver_changed;
UserSettings& user_settings;
};
static constexpr int32_t FEEDBACK_DELAY_MS = 100;
static constexpr int32_t TUD_CHECK_DELAY_MS = 500;
void run_program();
// Callback to notify main loop of tuh mounted
void update_tuh_status(bool mounted) __attribute__((weak));
}
#endif // _OGX_MINI_H_

View File

@@ -0,0 +1,139 @@
#include "board_config.h"
#if OGXM_BOARD == INTERNAL_4CH || OGXM_BOARD == EXTERNAL_4CH
#include <array>
#include <pico/multicore.h>
#include "tusb.h"
#include "bsp/board_api.h"
#include "pio_usb.h"
#include "USBDevice/DeviceManager.h"
#include "Board/board_api.h"
#include "OGXMini/OGXMini.h"
#include "I2CDriver/i2c_driver_4ch.h"
#include "Gamepad.h"
namespace OGXMini {
std::array<Gamepad, MAX_GAMEPADS> gamepads_;
bool feedback_cb(repeating_timer_t* rt)
{
static_cast<HostManager*>(rt->user_data)->send_feedback();
return true;
}
void core1_task()
{
HostManager& host_manager = HostManager::get_instance();
host_manager.initialize(gamepads_);
pio_usb_configuration_t pio_cfg = PIO_USB_CONFIG;
tuh_configure(BOARD_TUH_RHPORT, TUH_CFGID_RPI_PIO_USB_CONFIGURATION, &pio_cfg);
tuh_init(BOARD_TUH_RHPORT);
repeating_timer_t feedback_timer;
add_repeating_timer_ms(FEEDBACK_DELAY_MS, feedback_cb, &host_manager, &feedback_timer);
while (true)
{
tuh_task();
sleep_us(100);
}
}
bool gp_check_cb(repeating_timer_t* rt)
{
GPCheckContext* gp_check_ctx = static_cast<GPCheckContext*>(rt->user_data);
gp_check_ctx->driver_changed = gp_check_ctx->user_settings.check_for_driver_change(gamepads_.front());
return true;
}
bool tud_status_cb(repeating_timer_t* rt)
{
TUDStatusContext* tud_status_ctx = static_cast<TUDStatusContext*>(rt->user_data);
bool host_mounted = HostManager::get_instance().mounted() || i2c_driver::is_active();
bool device_ininted = tud_inited();
board_api::set_led(host_mounted);
if (host_mounted && !device_ininted)
{
tud_status_ctx->status = TUDStatus::INIT;
}
else if (!host_mounted && device_ininted)
{
tud_status_ctx->status = TUDStatus::DEINIT;
}
else
{
tud_status_ctx->status = TUDStatus::NOCHANGE;
}
return true;
}
void run_program()
{
UserSettings user_settings;
user_settings.initialize_flash();
board_init();
board_api::init_gpio();
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
{
gamepads_[i].set_profile(user_settings.get_profile_by_index(i));
}
DeviceManager& device_manager = DeviceManager::get_instance();
device_manager.initialize_driver(user_settings.get_current_driver());
i2c_driver::initialize(gamepads_);
multicore_reset_core1();
multicore_launch_core1(core1_task);
GPCheckContext gp_check_ctx = { false, user_settings };
repeating_timer_t gp_check_timer;
add_repeating_timer_ms(UserSettings::GP_CHECK_DELAY_MS, gp_check_cb, &gp_check_ctx, &gp_check_timer);
TUDStatusContext tud_status_ctx = { TUDStatus::NOCHANGE, &HostManager::get_instance() };
repeating_timer_t tud_status_timer;
add_repeating_timer_ms(500, tud_status_cb, &tud_status_ctx, &tud_status_timer);
DeviceDriver* device_driver = device_manager.get_driver();
while (true)
{
switch (tud_status_ctx.status)
{
case TUDStatus::NOCHANGE:
break;
case TUDStatus::INIT:
tud_init(BOARD_TUD_RHPORT);
break;
case TUDStatus::DEINIT:
tud_disconnect();
sleep_ms(300);
board_api::reboot();
break;
}
device_driver->process(0, gamepads_[0]);
tud_task();
sleep_us(100);
i2c_driver::process();
if (gp_check_ctx.driver_changed)
{
cancel_repeating_timer(&gp_check_timer);
user_settings.store_driver_type_safe(user_settings.get_current_driver());
}
}
}
} // namespace OGXMini
#endif // OGXM_BOARD == INTERNAL_4CH || OGXM_BOARD == EXTERNAL_4CH

View File

@@ -0,0 +1,172 @@
#include "board_config.h"
#if OGXM_BOARD == W_ESP32
#include <array>
#include <pico/stdlib.h>
#include <pico/multicore.h>
#include <hardware/gpio.h>
#include <hardware/sync.h>
#include "tusb.h"
#include "bsp/board_api.h"
#include "USBDevice/DeviceManager.h"
#include "Board/board_api.h"
#include "OGXMini/OGXMini.h"
#include "I2CDriver/I2CDriver_ESP.h"
#include "Gamepad.h"
namespace OGXMini {
std::array<Gamepad, MAX_GAMEPADS> gamepads_;
// void init_esp32_gpio()
// {
// gpio_init(ESP_PROG_PIN);
// gpio_set_dir(ESP_PROG_PIN, GPIO_IN);
// gpio_pull_up(ESP_PROG_PIN);
// gpio_init(ESP_RST_PIN);
// gpio_set_dir(ESP_RST_PIN, GPIO_IN);
// gpio_pull_up(ESP_RST_PIN);
// }
// bool uart_passthrough_mode()
// {
// gpio_init(MODE_SEL_PIN);
// gpio_set_dir(MODE_SEL_PIN, GPIO_IN);
// gpio_pull_up(MODE_SEL_PIN);
// if (gpio_get(MODE_SEL_PIN) == 0)
// {
// return true;
// }
// return false;
// }
// void reset_esp32()
// {
// gpio_init(ESP_RST_PIN);
// gpio_set_dir(ESP_RST_PIN, GPIO_OUT);
// gpio_put(ESP_RST_PIN, 0);
// sleep_ms(500);
// gpio_put(ESP_RST_PIN, 1);
// sleep_ms(250);
// }
// void reset_esp32()
// {
// gpio_init(ESP_PROG_PIN);
// gpio_set_dir(ESP_PROG_PIN, GPIO_OUT);
// gpio_put(ESP_PROG_PIN, 1);
// gpio_init(ESP_RST_PIN);
// gpio_set_dir(ESP_RST_PIN, GPIO_OUT);
// gpio_put(ESP_PROG_PIN, 1);
// gpio_put(ESP_PROG_PIN, 0);
// sleep_ms(250);
// gpio_put(ESP_RST_PIN, 0);
// sleep_ms(500);
// gpio_put(ESP_RST_PIN, 1);
// sleep_ms(250);
// gpio_put(ESP_PROG_PIN, 1);
// }
void run_esp32_uart_bridge()
{
DeviceManager& device_manager = DeviceManager::get_instance();
device_manager.initialize_driver(DeviceDriver::Type::UART_BRIDGE);
board_api::enter_esp32_prog_mode();
// reset_esp32();
device_manager.get_driver()->process(0, gamepads_.front()); //Runs UART Bridge task
}
void core1_task()
{
i2c_driver_esp::initialize(gamepads_);
while (true)
{
__wfi();
}
}
bool gp_check_cb(repeating_timer_t* rt)
{
GPCheckContext* gp_check_ctx = static_cast<GPCheckContext*>(rt->user_data);
gp_check_ctx->driver_changed = gp_check_ctx->user_settings.check_for_driver_change(gamepads_.front());
return true;
}
void run_program()
{
UserSettings user_settings;
user_settings.initialize_flash();
board_api::init_gpio();
// init_esp32_gpio();
if (board_api::uart_bridge_mode())
// if (uart_passthrough_mode())
{
run_esp32_uart_bridge();
return;
}
// else if (!user_settings.verify_firmware_version())
// {
// user_settings.write_firmware_version();
// }
// user_settings.initialize_flash();
board_init();
for (uint8_t i = 0; i < gamepads_.size(); ++i)
{
gamepads_[i].set_profile(user_settings.get_profile_by_index(i));
}
DeviceManager& device_manager = DeviceManager::get_instance();
// device_manager.initialize_driver(user_settings.get_current_driver());
device_manager.initialize_driver(DeviceDriver::Type::XINPUT);
multicore_reset_core1();
multicore_launch_core1(core1_task);
board_api::reset_esp32();
// reset_esp32();
// GPCheckContext gp_check_ctx = { false, user_settings };
// repeating_timer_t gp_check_timer;
// add_repeating_timer_ms(UserSettings::GP_CHECK_DELAY_MS, gp_check_cb, &gp_check_ctx, &gp_check_timer);
DeviceDriver* device_driver = device_manager.get_driver();
tud_init(BOARD_TUD_RHPORT);
while (true)
{
for (uint8_t i = 0; i < gamepads_.size(); ++i)
{
device_driver->process(i, gamepads_[i]);
tud_task();
}
// device_driver->process(0, gamepads_.front());
// tud_task();
sleep_us(100);
// if (gp_check_ctx.driver_changed)
// {
// cancel_repeating_timer(&gp_check_timer);
// user_settings.store_driver_type_safe(user_settings.get_current_driver());
// }
}
}
} // namespace OGXMini
#endif // OGXM_BOARD == W_ESP32

View File

@@ -0,0 +1,89 @@
#include "board_config.h"
#if (OGXM_BOARD == PI_PICOW)
#include <array>
#include <stdio.h>
#include <pico/multicore.h>
#include <hardware/gpio.h>
#include <hardware/clocks.h>
#include <pico/cyw43_arch.h>
#include "tusb.h"
#include "bsp/board_api.h"
#include "USBDevice/DeviceManager.h"
#include "Board/board_api.h"
#include "Bluepad32/Bluepad32.h"
#include "OGXMini/OGXMini.h"
namespace OGXMini {
std::array<Gamepad, MAX_GAMEPADS> gamepads_;
void core1_task()
{
if (cyw43_arch_init() != 0)
{
return;
}
bluepad32::initialize(gamepads_);
bluepad32::run_loop();
}
bool gp_check_cb(repeating_timer_t* rt)
{
GPCheckContext* gp_check_ctx = static_cast<GPCheckContext*>(rt->user_data);
gp_check_ctx->driver_changed = gp_check_ctx->user_settings.check_for_driver_change(gamepads_.front());
return true;
}
void run_program()
{
UserSettings user_settings;
user_settings.initialize_flash();
board_init();
board_api::init_gpio();
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
{
gamepads_[i].set_profile(user_settings.get_profile_by_index(i));
}
DeviceManager& device_manager = DeviceManager::get_instance();
device_manager.initialize_driver(user_settings.get_current_driver());
multicore_reset_core1();
multicore_launch_core1(core1_task);
GPCheckContext gp_check_ctx = { false, user_settings };
repeating_timer_t gp_check_timer;
add_repeating_timer_ms(UserSettings::GP_CHECK_DELAY_MS, gp_check_cb, &gp_check_ctx, &gp_check_timer);
DeviceDriver* device_driver = device_manager.get_driver();
tud_init(BOARD_TUD_RHPORT);
while (true)
{
for (uint8_t i = 0; i < gamepads_.size(); ++i)
{
device_driver->process(i, gamepads_[i]);
tud_task();
}
sleep_us(100);
if (gp_check_ctx.driver_changed)
{
cancel_repeating_timer(&gp_check_timer);
user_settings.store_driver_type_safe(user_settings.get_current_driver());
}
}
}
} // namespace OGXMini
#endif // (OGXM_BOARD == PI_PICOW)

View File

@@ -0,0 +1,135 @@
#include "board_config.h"
#if (OGXM_BOARD == ADA_FEATHER) || (OGXM_BOARD == RP_ZERO) || (OGXM_BOARD == PI_PICO)
#include <array>
#include <pico/multicore.h>
#include <hardware/gpio.h>
#include <hardware/clocks.h>
#include "tusb.h"
#include "bsp/board_api.h"
#include "pio_usb.h"
#include "USBHost/HostManager.h"
#include "USBDevice/DeviceManager.h"
#include "Board/board_api.h"
#include "OGXMini/OGXMini.h"
#include "Gamepad.h"
namespace OGXMini {
std::atomic<TUDStatus> tud_status_ = TUDStatus::NOCHANGE;
std::array<Gamepad, MAX_GAMEPADS> gamepads_;
void update_tuh_status(bool mounted)
{
if (mounted)
{
tud_status_.store(TUDStatus::INIT);
board_api::set_led(true);
}
else
{
tud_status_.store(TUDStatus::DEINIT);
board_api::set_led(false);
}
}
bool feedback_cb(repeating_timer_t* rt)
{
static_cast<HostManager*>(rt->user_data)->send_feedback();
return true;
}
void core1_task()
{
HostManager& host_manager = HostManager::get_instance();
host_manager.initialize(gamepads_);
pio_usb_configuration_t pio_cfg = PIO_USB_CONFIG;
tuh_configure(BOARD_TUH_RHPORT, TUH_CFGID_RPI_PIO_USB_CONFIGURATION, &pio_cfg);
tuh_init(BOARD_TUH_RHPORT);
repeating_timer_t feedback_timer;
add_repeating_timer_ms(FEEDBACK_DELAY_MS, feedback_cb, &host_manager, &feedback_timer);
while (true)
{
tuh_task();
sleep_us(100);
}
}
bool gp_check_cb(repeating_timer_t* rt)
{
GPCheckContext* gp_check_ctx = static_cast<GPCheckContext*>(rt->user_data);
gp_check_ctx->driver_changed = gp_check_ctx->user_settings.check_for_driver_change(gamepads_.front());
return true;
}
void run_program()
{
UserSettings user_settings;
user_settings.initialize_flash();
board_init();
board_api::init_gpio();
board_api::set_led(false);
for (uint8_t i = 0; i < MAX_GAMEPADS; ++i)
{
gamepads_[i].set_profile(user_settings.get_profile_by_index(i));
}
DeviceManager& device_manager = DeviceManager::get_instance();
device_manager.initialize_driver(user_settings.get_current_driver());
multicore_reset_core1();
multicore_launch_core1(core1_task);
GPCheckContext gp_check_ctx = { false, user_settings };
repeating_timer_t gp_check_timer;
add_repeating_timer_ms(UserSettings::GP_CHECK_DELAY_MS, gp_check_cb, &gp_check_ctx, &gp_check_timer);
DeviceDriver* device_driver = device_manager.get_driver();
while (true)
{
switch (tud_status_.load())
{
case TUDStatus::NOCHANGE:
break;
case TUDStatus::INIT:
tud_init(BOARD_TUD_RHPORT);
tud_status_.store(TUDStatus::NOCHANGE);
break;
case TUDStatus::DEINIT:
tud_disconnect();
sleep_ms(300);
multicore_reset_core1();
sleep_ms(300);
board_api::reboot();
break;
}
for (uint8_t i = 0; i < gamepads_.size(); ++i)
{
device_driver->process(i, gamepads_[i]);
tud_task();
}
sleep_us(100);
if (gp_check_ctx.driver_changed)
{
cancel_repeating_timer(&gp_check_timer);
user_settings.store_driver_type_safe(user_settings.get_current_driver());
}
}
}
} // namespace OGXMini
#endif // (OGXM_BOARD == ADA_FEATHER) || (OGXM_BOARD == RP_ZERO) || (OGXM_BOARD == PI_PICO)

View File

@@ -0,0 +1,171 @@
#include <cstring>
#include "class/hid/hid_device.h"
#include "Descriptors/PS3.h"
#include "USBDevice/DeviceDriver/DInput/DInput.h"
bool DInputDevice::control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
if (request->bmRequestType == 0xA1 &&
request->bRequest == HID_REQ_CONTROL_GET_REPORT &&
request->wValue == 0x0300 )
{
return tud_control_xfer(rhport, request, const_cast<void*>(static_cast<const void*>(PS3::MAGIC_BYTES)), sizeof(PS3::MAGIC_BYTES));
}
return hidd_control_xfer_cb(rhport, stage, request);
}
void DInputDevice::initialize()
{
std::memset(&in_report_, 0, sizeof(DInput::InReport));
in_report_.dpad = DInput::DPad::CENTER;
in_report_.joystick_lx = DInput::AXIS_MID;
in_report_.joystick_ly = DInput::AXIS_MID;
in_report_.joystick_rx = DInput::AXIS_MID;
in_report_.joystick_ry = DInput::AXIS_MID;
for (auto& prev_in_report : prev_in_reports_)
{
std::memcpy(&prev_in_report, &in_report_, sizeof(DInput::InReport));
}
class_driver_ = {
#if CFG_TUSB_DEBUG >= 2
.name = "HID",
#endif
.init = hidd_init,
.reset = hidd_reset,
.open = hidd_open,
.control_xfer_cb = DInputDevice::control_xfer_cb,
.xfer_cb = hidd_xfer_cb,
.sof = NULL
};
}
void DInputDevice::process(const uint8_t idx, Gamepad& gamepad)
{
switch (gamepad.get_dpad_buttons())
{
case Gamepad::DPad::UP:
in_report_.dpad = DInput::DPad::UP;
break;
case Gamepad::DPad::DOWN:
in_report_.dpad = DInput::DPad::DOWN;
break;
case Gamepad::DPad::LEFT:
in_report_.dpad = DInput::DPad::LEFT;
break;
case Gamepad::DPad::RIGHT:
in_report_.dpad = DInput::DPad::RIGHT;
break;
case Gamepad::DPad::UP_LEFT:
in_report_.dpad = DInput::DPad::UP_LEFT;
break;
case Gamepad::DPad::UP_RIGHT:
in_report_.dpad = DInput::DPad::UP_RIGHT;
break;
case Gamepad::DPad::DOWN_LEFT:
in_report_.dpad = DInput::DPad::DOWN_LEFT;
break;
case Gamepad::DPad::DOWN_RIGHT:
in_report_.dpad = DInput::DPad::DOWN_RIGHT;
break;
default:
in_report_.dpad = DInput::DPad::CENTER;
break;
}
std::memset(in_report_.buttons, 0, sizeof(in_report_.buttons));
uint16_t gamepad_buttons = gamepad.get_buttons();
if (gamepad_buttons & Gamepad::Button::A) in_report_.buttons[0] |= DInput::Buttons0::CROSS;
if (gamepad_buttons & Gamepad::Button::B) in_report_.buttons[0] |= DInput::Buttons0::CIRCLE;
if (gamepad_buttons & Gamepad::Button::X) in_report_.buttons[0] |= DInput::Buttons0::SQUARE;
if (gamepad_buttons & Gamepad::Button::Y) in_report_.buttons[0] |= DInput::Buttons0::TRIANGLE;
if (gamepad_buttons & Gamepad::Button::LB) in_report_.buttons[0] |= DInput::Buttons0::L1;
if (gamepad_buttons & Gamepad::Button::RB) in_report_.buttons[0] |= DInput::Buttons0::R1;
if (gamepad_buttons & Gamepad::Button::L3) in_report_.buttons[1] |= DInput::Buttons1::L3;
if (gamepad_buttons & Gamepad::Button::R3) in_report_.buttons[1] |= DInput::Buttons1::R3;
if (gamepad_buttons & Gamepad::Button::BACK) in_report_.buttons[1] |= DInput::Buttons1::SELECT;
if (gamepad_buttons & Gamepad::Button::START) in_report_.buttons[1] |= DInput::Buttons1::START;
if (gamepad_buttons & Gamepad::Button::SYS) in_report_.buttons[1] |= DInput::Buttons1::PS;
if (gamepad_buttons & Gamepad::Button::MISC) in_report_.buttons[1] |= DInput::Buttons1::TP;
if (gamepad.analog_enabled())
{
in_report_.up_axis = gamepad.get_analog_up();
in_report_.down_axis = gamepad.get_analog_down();
in_report_.right_axis = gamepad.get_analog_right();
in_report_.left_axis = gamepad.get_analog_left();
in_report_.triangle_axis = gamepad.get_analog_y();
in_report_.circle_axis = gamepad.get_analog_x();
in_report_.cross_axis = gamepad.get_analog_b();
in_report_.square_axis = gamepad.get_analog_a();
in_report_.r1_axis = gamepad.get_analog_rb();
in_report_.l1_axis = gamepad.get_analog_lb();
}
in_report_.joystick_lx = gamepad.get_joystick_lx().uint8();
in_report_.joystick_ly = gamepad.get_joystick_ly().uint8();
in_report_.joystick_rx = gamepad.get_joystick_rx().uint8();
in_report_.joystick_ry = gamepad.get_joystick_ry().uint8();
in_report_.l2_axis = gamepad.get_trigger_l().uint8();
in_report_.r2_axis = gamepad.get_trigger_r().uint8();
if (tud_suspended())
{
tud_remote_wakeup();
}
if (tud_hid_n_ready(idx) &&
// std::memcmp(&in_report_, &prev_in_reports_[idx], sizeof(DInput::InReport)) != 0 &&
tud_hid_n_report(idx, 0, reinterpret_cast<void*>(&in_report_), sizeof(DInput::InReport)))
{
std::memcpy(&prev_in_reports_[idx], &in_report_, sizeof(DInput::InReport));
}
}
uint16_t DInputDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
{
std::memcpy(buffer, &in_report_, sizeof(DInput::InReport));
return sizeof(DInput::InReport);
}
void DInputDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
bool DInputDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
return false;
}
const uint16_t* DInputDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
{
const char* value = reinterpret_cast<const char*>(DInput::STRING_DESCRIPTORS[index]);
return get_string_descriptor(value, index);
}
const uint8_t* DInputDevice::get_descriptor_device_cb()
{
return DInput::DEVICE_DESCRIPTORS;
}
const uint8_t* DInputDevice::get_hid_descriptor_report_cb(uint8_t itf)
{
return DInput::REPORT_DESCRIPTORS;
}
const uint8_t* DInputDevice::get_descriptor_configuration_cb(uint8_t index)
{
return DInput::CONFIGURATION_DESCRIPTORS;
}
const uint8_t* DInputDevice::get_descriptor_device_qualifier_cb()
{
return nullptr;
}

View File

@@ -0,0 +1,32 @@
#ifndef _DINPUT_DEVICE_H_
#define _DINPUT_DEVICE_H_
#include <cstdint>
#include <array>
#include "board_config.h"
#include "USBDevice/DeviceDriver/DeviceDriver.h"
#include "Descriptors/DInput.h"
class DInputDevice : public DeviceDriver
{
public:
void initialize() override;
void process(const uint8_t idx, Gamepad& gamepad) override;
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
const uint8_t* get_descriptor_device_cb() override;
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
const uint8_t* get_descriptor_device_qualifier_cb() override;
private:
DInput::InReport in_report_;
std::array<DInput::InReport, MAX_GAMEPADS> prev_in_reports_;
static bool control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
};
#endif // _DINPUT_DEVICE_H_

View File

@@ -0,0 +1,27 @@
#include "USBDevice/DeviceDriver/DeviceDriver.h"
uint16_t* DeviceDriver::get_string_descriptor(const char* value, uint8_t index)
{
static uint16_t string_desc_buffer[32];
size_t char_count;
if ( index == 0 )
{
char_count = 1;
}
else
{
char_count = strlen(value);
if (char_count > 31)
{
char_count = 31;
}
}
for (uint8_t i = 0; i < char_count; i++)
{
string_desc_buffer[i + 1] = value[i];
}
string_desc_buffer[0] = static_cast<uint16_t>((0x03 << 8) | (2 * static_cast<uint8_t>(char_count) + 2));
return string_desc_buffer;
}

View File

@@ -0,0 +1,49 @@
#ifndef _DEVICE_DRIVER_H_
#define _DEVICE_DRIVER_H_
#include <cstdint>
#include "tusb.h"
#include "class/hid/hid.h"
#include "device/usbd_pvt.h"
#include "Gamepad.h"
class DeviceDriver
{
public:
enum class Type : uint8_t
{
NONE = 0,
PS3 = 1,
DINPUT = 2,
SWITCH = 4,
PSCLASSIC = 5,
XINPUT = 6,
XBOXOG = 7,
XBOXOG_SB = 8,
XBOXOG_XR = 9,
WEBAPP = 100,
UART_BRIDGE = 101
};
virtual void initialize() = 0;
virtual void process(const uint8_t idx, Gamepad& gamepad) = 0;
virtual uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t req_len) = 0;
virtual void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t buffer_size) = 0;
virtual bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) = 0;
virtual const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) = 0;
virtual const uint8_t* get_descriptor_device_cb() = 0;
virtual const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) = 0;
virtual const uint8_t* get_descriptor_configuration_cb(uint8_t index) = 0;
virtual const uint8_t* get_descriptor_device_qualifier_cb() = 0;
const usbd_class_driver_t* get_class_driver() { return &class_driver_; };
protected:
usbd_class_driver_t class_driver_;
uint16_t* get_string_descriptor(const char* value, uint8_t index);
};
#endif // _DEVICE_DRIVER_H_

View File

@@ -0,0 +1,332 @@
#include <cstring>
#include "USBDevice/DeviceDriver/PS3/PS3.h"
void PS3Device::initialize()
{
class_driver_ =
{
#if CFG_TUSB_DEBUG >= 2
.name = "PS3",
#endif
.init = hidd_init,
.deinit = hidd_deinit,
.reset = hidd_reset,
.open = hidd_open,
.control_xfer_cb = hidd_control_xfer_cb,
.xfer_cb = hidd_xfer_cb,
.sof = NULL
};
in_report_ = PS3::InReport();
// bt_info_ =
// {
// .reserved0 = {0xFF,0xFF},
// .device_address = { 0x00, 0x20, 0x40, 0xCE, 0x00, 0x00, 0x00 },
// .host_address = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
// };
// for (uint8_t addr = 0; addr < 3; addr++)
// {
// bt_info_.device_address[4 + addr] = static_cast<uint8_t>(get_rand_32() % 0xFF);
// }
// for (uint8_t addr = 0; addr < 6; addr++)
// {
// bt_info_.host_address[1 + addr] = static_cast<uint8_t>(get_rand_32() % 0xFF);
// }
}
void PS3Device::process(const uint8_t idx, Gamepad& gamepad)
{
uint8_t gp_dpad = gamepad.get_dpad_buttons();
uint16_t gp_buttons = gamepad.get_buttons();
uint8_t gp_trigger_l = gamepad.get_trigger_l().uint8();
uint8_t gp_trigger_r = gamepad.get_trigger_r().uint8();
std::memset(in_report_.buttons, 0, sizeof(in_report_.buttons));
switch (gp_dpad)
{
case Gamepad::DPad::UP:
in_report_.buttons[0] = PS3::Buttons0::DPAD_UP;
break;
case Gamepad::DPad::DOWN:
in_report_.buttons[0] = PS3::Buttons0::DPAD_DOWN;
break;
case Gamepad::DPad::LEFT:
in_report_.buttons[0] = PS3::Buttons0::DPAD_LEFT;
break;
case Gamepad::DPad::RIGHT:
in_report_.buttons[0] = PS3::Buttons0::DPAD_RIGHT;
break;
case Gamepad::DPad::UP_LEFT:
in_report_.buttons[0] = PS3::Buttons0::DPAD_UP | PS3::Buttons0::DPAD_LEFT;
break;
case Gamepad::DPad::UP_RIGHT:
in_report_.buttons[0] = PS3::Buttons0::DPAD_UP | PS3::Buttons0::DPAD_RIGHT;
break;
case Gamepad::DPad::DOWN_LEFT:
in_report_.buttons[0] = PS3::Buttons0::DPAD_DOWN | PS3::Buttons0::DPAD_LEFT;
break;
case Gamepad::DPad::DOWN_RIGHT:
in_report_.buttons[0] = PS3::Buttons0::DPAD_DOWN | PS3::Buttons0::DPAD_RIGHT;
break;
default:
break;
}
if (gp_buttons & Gamepad::Button::X) in_report_.buttons[1] |= PS3::Buttons1::SQUARE;
if (gp_buttons & Gamepad::Button::A) in_report_.buttons[1] |= PS3::Buttons1::CROSS;
if (gp_buttons & Gamepad::Button::Y) in_report_.buttons[1] |= PS3::Buttons1::TRIANGLE;
if (gp_buttons & Gamepad::Button::B) in_report_.buttons[1] |= PS3::Buttons1::CIRCLE;
if (gp_buttons & Gamepad::Button::LB) in_report_.buttons[1] |= PS3::Buttons1::L1;
if (gp_buttons & Gamepad::Button::RB) in_report_.buttons[1] |= PS3::Buttons1::R1;
if (gp_buttons & Gamepad::Button::BACK) in_report_.buttons[0] |= PS3::Buttons0::SELECT;
if (gp_buttons & Gamepad::Button::START) in_report_.buttons[0] |= PS3::Buttons0::START;
if (gp_buttons & Gamepad::Button::L3) in_report_.buttons[0] |= PS3::Buttons0::L3;
if (gp_buttons & Gamepad::Button::R3) in_report_.buttons[0] |= PS3::Buttons0::R3;
if (gp_buttons & Gamepad::Button::SYS) in_report_.buttons[2] |= PS3::Buttons2::PS;
if (gp_buttons & Gamepad::Button::MISC) in_report_.buttons[2] |= PS3::Buttons2::TP;
if (gp_trigger_l > 0) in_report_.buttons[1] |= PS3::Buttons1::L2;
if (gp_trigger_r > 0) in_report_.buttons[1] |= PS3::Buttons1::R2;
in_report_.joystick_lx = gamepad.get_joystick_lx().uint8();
in_report_.joystick_ly = gamepad.get_joystick_ly().uint8();
in_report_.joystick_rx = gamepad.get_joystick_rx().uint8();
in_report_.joystick_ry = gamepad.get_joystick_ry().uint8();
if (gamepad.analog_enabled())
{
in_report_.up_axis = gamepad.get_analog_up();
in_report_.down_axis = gamepad.get_analog_down();
in_report_.right_axis = gamepad.get_analog_right();
in_report_.left_axis = gamepad.get_analog_left();
in_report_.triangle_axis = gamepad.get_analog_y();
in_report_.circle_axis = gamepad.get_analog_x();
in_report_.cross_axis = gamepad.get_analog_b();
in_report_.square_axis = gamepad.get_analog_a();
in_report_.r1_axis = gamepad.get_analog_rb();
in_report_.l1_axis = gamepad.get_analog_lb();
}
else
{
in_report_.up_axis = (gp_dpad & Gamepad::DPad::UP) ? 0xFF : 0;
in_report_.down_axis = (gp_dpad & Gamepad::DPad::DOWN) ? 0xFF : 0;
in_report_.right_axis = (gp_dpad & Gamepad::DPad::RIGHT) ? 0xFF : 0;
in_report_.left_axis = (gp_dpad & Gamepad::DPad::LEFT) ? 0xFF : 0;
in_report_.triangle_axis = (gp_buttons & Gamepad::Button::Y) ? 0xFF : 0;
in_report_.circle_axis = (gp_buttons & Gamepad::Button::X) ? 0xFF : 0;
in_report_.cross_axis = (gp_buttons & Gamepad::Button::B) ? 0xFF : 0;
in_report_.square_axis = (gp_buttons & Gamepad::Button::A) ? 0xFF : 0;
in_report_.r1_axis = (gp_buttons & Gamepad::Button::RB) ? 0xFF : 0;
in_report_.l1_axis = (gp_buttons & Gamepad::Button::LB) ? 0xFF : 0;
}
if (tud_suspended())
{
tud_remote_wakeup();
}
if (std::memcmp(&prev_in_report_, &in_report_, sizeof(PS3::InReport)) != 0 &&
tud_hid_ready() &&
tud_hid_report(0, reinterpret_cast<uint8_t*>(&in_report_), sizeof(PS3::InReport)))
{
std::memcpy(&prev_in_report_, &in_report_, sizeof(PS3::InReport));
}
if (new_out_report_)
{
gamepad.set_rumble_l(out_report_.rumble.left_motor_force);
gamepad.set_rumble_r(out_report_.rumble.right_motor_on);
new_out_report_ = false;
}
}
static constexpr uint8_t output_ps3_0x01[] =
{
0x01, 0x04, 0x00, 0x0b, 0x0c, 0x01, 0x02, 0x18,
0x18, 0x18, 0x18, 0x09, 0x0a, 0x10, 0x11, 0x12,
0x13, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02,
0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x04, 0x04,
0x04, 0x04, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02,
0x07, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// calibration data
static constexpr uint8_t output_ps3_0xef[] =
{
0xef, 0x04, 0x00, 0x0b, 0x03, 0x01, 0xa0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff,
0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
};
// unknown
static constexpr uint8_t output_ps3_0xf5[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // host address - must match 0xf2
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// unknown
static constexpr uint8_t output_ps3_0xf7[] =
{
0x02, 0x01, 0xf8, 0x02, 0xe2, 0x01, 0x05, 0xff,
0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// unknown
static constexpr uint8_t output_ps3_0xf8[] =
{
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/* Based on: https://github.com/OpenStickCommunity/GP2040-CE/blob/main/src/drivers/ps3/PS3Driver.cpp */
uint16_t PS3Device::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
{
if (report_type == HID_REPORT_TYPE_INPUT)
{
std::memcpy(buffer, &in_report_, sizeof(PS3::InReport));
return sizeof(PS3::InReport);
}
else if (report_type == HID_REPORT_TYPE_FEATURE)
{
uint16_t resp_len = 0;
uint8_t ctr = 0;
switch(report_id)
{
case PS3::ReportID::FEATURE_01:
resp_len = reqlen;
std::memcpy(buffer, output_ps3_0x01, resp_len);
return resp_len;
case PS3::ReportID::FEATURE_EF:
resp_len = reqlen;
std::memcpy(buffer, output_ps3_0xef, resp_len);
buffer[6] = ef_byte_;
return resp_len;
case PS3::ReportID::GET_PAIRING_INFO:
resp_len = reqlen;
std::memcpy(buffer, &bt_info_, resp_len);
return resp_len;
case PS3::ReportID::FEATURE_F5:
resp_len = reqlen;
std::memcpy(buffer, output_ps3_0xf5, resp_len);
for (ctr = 0; ctr < 6; ctr++)
{
buffer[1 + ctr] = bt_info_.host_address[ctr];
}
return resp_len;
case PS3::ReportID::FEATURE_F7:
resp_len = reqlen;
std::memcpy(buffer, output_ps3_0xf7, resp_len);
return resp_len;
case PS3::ReportID::FEATURE_F8:
resp_len = reqlen;
std::memcpy(buffer, output_ps3_0xf8, resp_len);
buffer[6] = ef_byte_;
return resp_len;
}
}
return -1;
}
void PS3Device::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
{
if (report_type == HID_REPORT_TYPE_FEATURE)
{
switch(report_id)
{
case PS3::ReportID::FEATURE_EF:
ef_byte_ = buffer[6];
break;
}
}
else if (report_type == HID_REPORT_TYPE_OUTPUT )
{
// DS3 command
uint8_t const *buf = buffer;
if (report_id == 0 && bufsize > 0)
{
report_id = buffer[0];
bufsize = bufsize - 1;
buf = &buffer[1];
}
switch(report_id)
{
case PS3::ReportID::FEATURE_01:
std::memcpy(&out_report_, buf, std::min(bufsize, static_cast<uint16_t>(sizeof(PS3::OutReport))));
new_out_report_ = true;
break;
}
}
}
bool PS3Device::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
return false;
}
const uint16_t* PS3Device::get_descriptor_string_cb(uint8_t index, uint16_t langid)
{
const char *value = reinterpret_cast<const char*>(PS3::STRING_DESCRIPTORS[index]);
return get_string_descriptor(value, index);
}
const uint8_t* PS3Device::get_descriptor_device_cb()
{
return PS3::DEVICE_DESCRIPTORS;
}
const uint8_t* PS3Device::get_hid_descriptor_report_cb(uint8_t itf)
{
return PS3::REPORT_DESCRIPTORS;
}
const uint8_t* PS3Device::get_descriptor_configuration_cb(uint8_t index)
{
return PS3::CONFIGURATION_DESCRIPTORS;
}
const uint8_t* PS3Device::get_descriptor_device_qualifier_cb()
{
return nullptr;
}

View File

@@ -0,0 +1,33 @@
#ifndef _PS3_DEVICE_H_
#define _PS3_DEVICE_H_
#include <cstdint>
#include <array>
#include "USBDevice/DeviceDriver/DeviceDriver.h"
#include "Descriptors/PS3.h"
class PS3Device : public DeviceDriver
{
public:
void initialize() override;
void process(const uint8_t idx, Gamepad& gamepad) override;
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
const uint8_t* get_descriptor_device_cb() override;
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
const uint8_t* get_descriptor_device_qualifier_cb() override;
private:
PS3::InReport in_report_;
PS3::InReport prev_in_report_;
PS3::OutReport out_report_;
PS3::BTInfo bt_info_;
uint8_t ef_byte_;
bool new_out_report_{false};
};
#endif // _PS3_DEVICE_H_

View File

@@ -0,0 +1,162 @@
#include <cstring>
#include "USBDevice/DeviceDriver/PSClassic/PSClassic.h"
void PSClassicDevice::initialize()
{
class_driver_ =
{
#if CFG_TUSB_DEBUG >= 2
.name = "PSClassic",
#endif
.init = hidd_init,
.reset = hidd_reset,
.open = hidd_open,
.control_xfer_cb = hidd_control_xfer_cb,
.xfer_cb = hidd_xfer_cb,
.sof = NULL
};
}
void PSClassicDevice::process(const uint8_t idx, Gamepad& gamepad)
{
switch (gamepad.get_dpad_buttons())
{
case Gamepad::DPad::UP:
in_report_.buttons = PSClassic::Buttons::UP;
break;
case Gamepad::DPad::DOWN:
in_report_.buttons = PSClassic::Buttons::DOWN;
break;
case Gamepad::DPad::LEFT:
in_report_.buttons = PSClassic::Buttons::LEFT;
break;
case Gamepad::DPad::RIGHT:
in_report_.buttons = PSClassic::Buttons::RIGHT;
break;
case Gamepad::DPad::UP_LEFT:
in_report_.buttons = PSClassic::Buttons::UP_LEFT;
break;
case Gamepad::DPad::UP_RIGHT:
in_report_.buttons = PSClassic::Buttons::UP_RIGHT;
break;
case Gamepad::DPad::DOWN_LEFT:
in_report_.buttons = PSClassic::Buttons::DOWN_LEFT;
break;
case Gamepad::DPad::DOWN_RIGHT:
in_report_.buttons = PSClassic::Buttons::DOWN_RIGHT;
break;
default:
in_report_.buttons = PSClassic::Buttons::CENTER;
break;
}
int16_t joy_lx = gamepad.get_joystick_lx().int16();
int16_t joy_ly = gamepad.get_joystick_ly().int16(true);
int16_t joy_rx = gamepad.get_joystick_rx().int16();
int16_t joy_ry = gamepad.get_joystick_ry().int16(true);
if (meets_pos_threshold(joy_lx, joy_rx))
{
if (meets_neg_45_threshold(joy_ly, joy_ry))
{
in_report_.buttons = PSClassic::Buttons::DOWN_RIGHT;
}
else if (meets_pos_45_threshold(joy_ly, joy_ry))
{
in_report_.buttons = PSClassic::Buttons::UP_RIGHT;
}
else
{
in_report_.buttons = PSClassic::Buttons::RIGHT;
}
}
else if (meets_neg_threshold(joy_lx, joy_rx))
{
if (meets_neg_45_threshold(joy_ly, joy_ry))
{
in_report_.buttons = PSClassic::Buttons::DOWN_LEFT;
}
else if (meets_pos_45_threshold(joy_ly, joy_ry))
{
in_report_.buttons = PSClassic::Buttons::UP_LEFT;
}
else
{
in_report_.buttons = PSClassic::Buttons::LEFT;
}
}
else if (meets_neg_threshold(joy_ly, joy_ry))
{
in_report_.buttons = PSClassic::Buttons::DOWN;
}
else if (meets_pos_threshold(joy_ly, joy_ry))
{
in_report_.buttons = PSClassic::Buttons::UP;
}
uint16_t gamepad_buttons = gamepad.get_buttons();
if (gamepad_buttons & Gamepad::Button::A) in_report_.buttons |= PSClassic::Buttons::CROSS;
if (gamepad_buttons & Gamepad::Button::B) in_report_.buttons |= PSClassic::Buttons::CIRCLE;
if (gamepad_buttons & Gamepad::Button::X) in_report_.buttons |= PSClassic::Buttons::SQUARE;
if (gamepad_buttons & Gamepad::Button::Y) in_report_.buttons |= PSClassic::Buttons::TRIANGLE;
if (gamepad_buttons & Gamepad::Button::LB) in_report_.buttons |= PSClassic::Buttons::L1;
if (gamepad_buttons & Gamepad::Button::RB) in_report_.buttons |= PSClassic::Buttons::R1;
if (gamepad_buttons & Gamepad::Button::BACK) in_report_.buttons |= PSClassic::Buttons::SELECT;
if (gamepad_buttons & Gamepad::Button::START) in_report_.buttons |= PSClassic::Buttons::START;
if (gamepad.get_trigger_l().uint8()) in_report_.buttons |= PSClassic::Buttons::L2;
if (gamepad.get_trigger_r().uint8()) in_report_.buttons |= PSClassic::Buttons::R2;
if (tud_suspended())
{
tud_remote_wakeup();
}
if (tud_hid_n_ready(idx) &&
std::memcmp(&in_report_, &prev_in_report_, sizeof(PSClassic::InReport)) != 0 &&
tud_hid_n_report(idx, 0, reinterpret_cast<uint8_t*>(&in_report_), sizeof(PSClassic::InReport)))
{
std::memcpy(&prev_in_report_, &in_report_, sizeof(PSClassic::InReport));
}
}
uint16_t PSClassicDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
{
std::memcpy(buffer, &in_report_, sizeof(PSClassic::InReport));
return sizeof(PSClassic::InReport);
}
void PSClassicDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
bool PSClassicDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
return false;
}
const uint16_t* PSClassicDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
{
const char* value = reinterpret_cast<const char*>(PSClassic::STRING_DESCRIPTORS[index]);
return get_string_descriptor(value, index);
}
const uint8_t* PSClassicDevice::get_descriptor_device_cb()
{
return PSClassic::DEVICE_DESCRIPTORS;
}
const uint8_t* PSClassicDevice::get_hid_descriptor_report_cb(uint8_t itf)
{
return PSClassic::REPORT_DESCRIPTORS;
}
const uint8_t* PSClassicDevice::get_descriptor_configuration_cb(uint8_t index)
{
return PSClassic::CONFIGURATION_DESCRIPTORS;
}
const uint8_t* PSClassicDevice::get_descriptor_device_qualifier_cb()
{
return nullptr;
}

View File

@@ -0,0 +1,41 @@
#ifndef _D_PSCLASSIC_DRIVER_H_
#define _D_PSCLASSIC_DRIVER_H_
#include <cstdint>
#include "tusb.h"
#include "Gamepad.h"
#include "Descriptors/PSClassic.h"
#include "USBDevice/DeviceDriver/DeviceDriver.h"
class PSClassicDevice : public DeviceDriver
{
public:
void initialize() override;
void process(const uint8_t idx, Gamepad& gamepad) override;
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
const uint8_t* get_descriptor_device_cb() override;
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) ;
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
const uint8_t* get_descriptor_device_qualifier_cb() override;
private:
static constexpr int16_t JOY_POS_THRESHOLD = 10000;
static constexpr int16_t JOY_NEG_THRESHOLD = -10000;
static constexpr int16_t JOY_POS_45_THRESHOLD = JOY_POS_THRESHOLD * 2;
static constexpr int16_t JOY_NEG_45_THRESHOLD = JOY_NEG_THRESHOLD * 2;
PSClassic::InReport prev_in_report_{0};
PSClassic::InReport in_report_{0};
inline bool meets_pos_threshold(int16_t joy_l, int16_t joy_r) { return (joy_l >= JOY_POS_THRESHOLD) || (joy_r >= JOY_POS_THRESHOLD); }
inline bool meets_neg_threshold(int16_t joy_l, int16_t joy_r) { return (joy_l <= JOY_NEG_THRESHOLD) || (joy_r <= JOY_NEG_THRESHOLD); }
inline bool meets_pos_45_threshold(int16_t joy_l, int16_t joy_r) { return (joy_l >= JOY_POS_45_THRESHOLD) || (joy_r >= JOY_POS_45_THRESHOLD); }
inline bool meets_neg_45_threshold(int16_t joy_l, int16_t joy_r) { return (joy_l <= JOY_NEG_45_THRESHOLD) || (joy_r <= JOY_NEG_45_THRESHOLD); }
};
#endif // _D_PSCLASSIC_DRIVER_H_

View File

@@ -0,0 +1,136 @@
#include <cstring>
#include "USBDevice/DeviceDriver/Switch/Switch.h"
void SwitchDevice::initialize()
{
class_driver_ =
{
#if CFG_TUSB_DEBUG >= 2
.name = "SWITCH",
#endif
.init = hidd_init,
.reset = hidd_reset,
.open = hidd_open,
.control_xfer_cb = hidd_control_xfer_cb,
.xfer_cb = hidd_xfer_cb,
.sof = NULL
};
in_report_ = SwitchWired::InReport();
for (auto& prev_in_report : prev_in_reports_)
{
prev_in_report = SwitchWired::InReport();
}
}
void SwitchDevice::process(const uint8_t idx, Gamepad& gamepad)
{
switch (gamepad.get_dpad_buttons())
{
case Gamepad::DPad::UP:
in_report_.dpad = SwitchWired::DPad::UP;
break;
case Gamepad::DPad::DOWN:
in_report_.dpad = SwitchWired::DPad::DOWN;
break;
case Gamepad::DPad::LEFT:
in_report_.dpad = SwitchWired::DPad::LEFT;
break;
case Gamepad::DPad::RIGHT:
in_report_.dpad = SwitchWired::DPad::RIGHT;
break;
case Gamepad::DPad::UP_LEFT:
in_report_.dpad = SwitchWired::DPad::UP_LEFT;
break;
case Gamepad::DPad::UP_RIGHT:
in_report_.dpad = SwitchWired::DPad::UP_RIGHT;
break;
case Gamepad::DPad::DOWN_LEFT:
in_report_.dpad = SwitchWired::DPad::DOWN_LEFT;
break;
case Gamepad::DPad::DOWN_RIGHT:
in_report_.dpad = SwitchWired::DPad::DOWN_RIGHT;
break;
default:
in_report_.dpad = SwitchWired::DPad::CENTER;
break;
}
in_report_.buttons = 0;
uint16_t gp_buttons = gamepad.get_buttons();
if (gp_buttons & Gamepad::Button::X) in_report_.buttons |= SwitchWired::Buttons::Y;
if (gp_buttons & Gamepad::Button::A) in_report_.buttons |= SwitchWired::Buttons::B;
if (gp_buttons & Gamepad::Button::Y) in_report_.buttons |= SwitchWired::Buttons::X;
if (gp_buttons & Gamepad::Button::B) in_report_.buttons |= SwitchWired::Buttons::A;
if (gp_buttons & Gamepad::Button::LB) in_report_.buttons |= SwitchWired::Buttons::L;
if (gp_buttons & Gamepad::Button::RB) in_report_.buttons |= SwitchWired::Buttons::R;
if (gp_buttons & Gamepad::Button::BACK) in_report_.buttons |= SwitchWired::Buttons::MINUS;
if (gp_buttons & Gamepad::Button::START) in_report_.buttons |= SwitchWired::Buttons::PLUS;
if (gp_buttons & Gamepad::Button::L3) in_report_.buttons |= SwitchWired::Buttons::L3;
if (gp_buttons & Gamepad::Button::R3) in_report_.buttons |= SwitchWired::Buttons::R3;
if (gp_buttons & Gamepad::Button::SYS) in_report_.buttons |= SwitchWired::Buttons::HOME;
if (gp_buttons & Gamepad::Button::MISC) in_report_.buttons |= SwitchWired::Buttons::CAPTURE;
if (gamepad.get_trigger_l().uint8()) in_report_.buttons |= SwitchWired::Buttons::ZL;
if (gamepad.get_trigger_r().uint8()) in_report_.buttons |= SwitchWired::Buttons::ZR;
in_report_.joystick_lx = gamepad.get_joystick_lx().uint8();
in_report_.joystick_ly = gamepad.get_joystick_ly().uint8();
in_report_.joystick_rx = gamepad.get_joystick_rx().uint8();
in_report_.joystick_ry = gamepad.get_joystick_ry().uint8();
if (tud_suspended())
{
tud_remote_wakeup();
}
if (std::memcmp(&prev_in_reports_[idx], &in_report_, sizeof(SwitchWired::InReport)) != 0 &&
tud_hid_n_ready(idx) &&
tud_hid_n_report(idx, 0, reinterpret_cast<uint8_t*>(&in_report_), sizeof(SwitchWired::InReport)))
{
std::memcpy(&prev_in_reports_[idx], &in_report_, sizeof(SwitchWired::InReport));
}
}
uint16_t SwitchDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
{
// std::memcpy(buffer, &in_report_, sizeof(SwitchWired::InReport));
// return sizeof(SwitchWired::InReport);
return 0;
}
void SwitchDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
bool SwitchDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
return false;
}
const uint16_t* SwitchDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
{
const char *value = reinterpret_cast<const char*>(SwitchWired::STRING_DESCRIPTORS[index]);
return get_string_descriptor(value, index);
}
const uint8_t* SwitchDevice::get_descriptor_device_cb()
{
return SwitchWired::DEVICE_DESCRIPTORS;
}
const uint8_t* SwitchDevice::get_hid_descriptor_report_cb(uint8_t itf)
{
return SwitchWired::REPORT_DESCRIPTORS;
}
const uint8_t* SwitchDevice::get_descriptor_configuration_cb(uint8_t index)
{
return SwitchWired::CONFIGURATION_DESCRIPTORS;
}
const uint8_t* SwitchDevice::get_descriptor_device_qualifier_cb()
{
return nullptr;
}

View File

@@ -0,0 +1,29 @@
#ifndef _SWITCH_DEVICE_H_
#define _SWITCH_DEVICE_H_
#include <cstdint>
#include <array>
#include "USBDevice/DeviceDriver/DeviceDriver.h"
#include "Descriptors/SwitchWired.h"
class SwitchDevice : public DeviceDriver
{
public:
void initialize() override;
void process(const uint8_t idx, Gamepad& gamepad) override;
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
const uint8_t* get_descriptor_device_cb() override;
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
const uint8_t* get_descriptor_device_qualifier_cb() override;
private:
SwitchWired::InReport in_report_;
std::array<SwitchWired::InReport, MAX_GAMEPADS> prev_in_reports_;
};
#endif // _SWITCH_DEVICE_H_

View File

@@ -0,0 +1,66 @@
#include "USBDevice/DeviceDriver/UARTBridge/UARTBridge.h"
#include "USBDevice/DeviceDriver/UARTBridge/uart_bridge/uart_bridge.h"
void UARTBridgeDevice::initialize()
{
class_driver_ = {
#if CFG_TUSB_DEBUG >= 2
.name = "UART",
#endif
.init = cdcd_init,
.reset = cdcd_reset,
.open = cdcd_open,
.control_xfer_cb = cdcd_control_xfer_cb,
.xfer_cb = cdcd_xfer_cb,
.sof = NULL
};
}
void UARTBridgeDevice::process(const uint8_t idx, Gamepad& gamepad)
{
if (!uart_initialized_)
{
uart_initialized_ = true;
uart_bridge_run();
}
}
uint16_t UARTBridgeDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
{
return sizeof(buffer);
}
void UARTBridgeDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
{
}
bool UARTBridgeDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
return false;
}
const uint16_t * UARTBridgeDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
{
return uart_bridge_descriptor_string_cb(index, langid);
}
const uint8_t * UARTBridgeDevice::get_descriptor_device_cb()
{
return uart_bridge_descriptor_device_cb();
}
const uint8_t * UARTBridgeDevice::get_hid_descriptor_report_cb(uint8_t itf)
{
return nullptr;
}
const uint8_t * UARTBridgeDevice::get_descriptor_configuration_cb(uint8_t index)
{
return uart_bridge_descriptor_configuration_cb(index);
}
const uint8_t * UARTBridgeDevice::get_descriptor_device_qualifier_cb()
{
return nullptr;
}

View File

@@ -0,0 +1,26 @@
#ifndef _UART_BRIDGE_DEVICE_H_
#define _UART_BRIDGE_DEVICE_H_
#include "USBDevice/DeviceDriver/DeviceDriver.h"
#include "UserSettings/UserProfile.h"
//process() only needs to be called once to start the UART bridge
class UARTBridgeDevice : public DeviceDriver
{
public:
void initialize() override;
void process(const uint8_t idx, Gamepad& gamepad) override;
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
const uint8_t* get_descriptor_device_cb() override;
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
const uint8_t* get_descriptor_device_qualifier_cb() override;
private:
bool uart_initialized_ = false;
};
#endif // _UART_BRIDGE_DEVICE_H_

View File

@@ -0,0 +1,456 @@
// SPDX-License-Identifier: MIT
/*
* Copyright (c) 2021 Álvaro Fernández Rojas <noltari@gmail.com>
*
* This file is based on a file originally part of the
* MicroPython project, http://micropython.org/
*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
* Copyright (c) 2019 Damien P. George
*/
#include <hardware/irq.h>
#include <hardware/structs/sio.h>
#include <hardware/uart.h>
#include <hardware/flash.h>
#include <pico/multicore.h>
#include <pico/stdlib.h>
#include <string.h>
#include "tusb.h"
#if !defined(MIN)
#define MIN(a, b) ((a > b) ? b : a)
#endif /* MIN */
#define BUFFER_SIZE 2560
#define DEF_BIT_RATE 115200
#define DEF_STOP_BITS 1
#define DEF_PARITY 0
#define DEF_DATA_BITS 8
typedef struct {
uart_inst_t *const inst;
uint irq;
void *irq_fn;
uint8_t tx_pin;
uint8_t rx_pin;
} uart_id_t;
typedef struct {
cdc_line_coding_t usb_lc;
cdc_line_coding_t uart_lc;
mutex_t lc_mtx;
uint8_t uart_buffer[BUFFER_SIZE];
uint32_t uart_pos;
mutex_t uart_mtx;
uint8_t usb_buffer[BUFFER_SIZE];
uint32_t usb_pos;
mutex_t usb_mtx;
} uart_data_t;
void uart0_irq_fn(void);
void uart1_irq_fn(void);
const uart_id_t UART_ID[CFG_TUD_CDC] = {
{
.inst = uart0,
.irq = UART0_IRQ,
.irq_fn = &uart0_irq_fn,
.tx_pin = UART0_TX_PIN,
.rx_pin = UART0_RX_PIN,
},
#if CFG_TUD_CDC > 1
{
.inst = uart1,
.irq = UART1_IRQ,
.irq_fn = &uart1_irq_fn,
.tx_pin = 4,
.rx_pin = 5,
}
#endif
};
uart_data_t UART_DATA[CFG_TUD_CDC];
static inline uint databits_usb2uart(uint8_t data_bits)
{
switch (data_bits) {
case 5:
return 5;
case 6:
return 6;
case 7:
return 7;
default:
return 8;
}
}
static inline uart_parity_t parity_usb2uart(uint8_t usb_parity)
{
switch (usb_parity) {
case 1:
return UART_PARITY_ODD;
case 2:
return UART_PARITY_EVEN;
default:
return UART_PARITY_NONE;
}
}
static inline uint stopbits_usb2uart(uint8_t stop_bits)
{
switch (stop_bits) {
case 2:
return 2;
default:
return 1;
}
}
void update_uart_cfg(uint8_t itf)
{
const uart_id_t *ui = &UART_ID[itf];
uart_data_t *ud = &UART_DATA[itf];
mutex_enter_blocking(&ud->lc_mtx);
if (ud->usb_lc.bit_rate != ud->uart_lc.bit_rate) {
uart_set_baudrate(ui->inst, ud->usb_lc.bit_rate);
ud->uart_lc.bit_rate = ud->usb_lc.bit_rate;
}
if ((ud->usb_lc.stop_bits != ud->uart_lc.stop_bits) ||
(ud->usb_lc.parity != ud->uart_lc.parity) ||
(ud->usb_lc.data_bits != ud->uart_lc.data_bits)) {
uart_set_format(ui->inst,
databits_usb2uart(ud->usb_lc.data_bits),
stopbits_usb2uart(ud->usb_lc.stop_bits),
parity_usb2uart(ud->usb_lc.parity));
ud->uart_lc.data_bits = ud->usb_lc.data_bits;
ud->uart_lc.parity = ud->usb_lc.parity;
ud->uart_lc.stop_bits = ud->usb_lc.stop_bits;
}
mutex_exit(&ud->lc_mtx);
}
void usb_read_bytes(uint8_t itf)
{
uart_data_t *ud = &UART_DATA[itf];
uint32_t len = tud_cdc_n_available(itf);
if (len &&
mutex_try_enter(&ud->usb_mtx, NULL)) {
len = MIN(len, BUFFER_SIZE - ud->usb_pos);
if (len) {
uint32_t count;
count = tud_cdc_n_read(itf, &ud->usb_buffer[ud->usb_pos], len);
ud->usb_pos += count;
}
mutex_exit(&ud->usb_mtx);
}
}
void usb_write_bytes(uint8_t itf)
{
uart_data_t *ud = &UART_DATA[itf];
if (ud->uart_pos &&
mutex_try_enter(&ud->uart_mtx, NULL)) {
uint32_t count;
count = tud_cdc_n_write(itf, ud->uart_buffer, ud->uart_pos);
if (count < ud->uart_pos)
memmove(ud->uart_buffer, &ud->uart_buffer[count],
ud->uart_pos - count);
ud->uart_pos -= count;
mutex_exit(&ud->uart_mtx);
if (count)
tud_cdc_n_write_flush(itf);
}
}
void usb_cdc_process(uint8_t itf)
{
uart_data_t *ud = &UART_DATA[itf];
mutex_enter_blocking(&ud->lc_mtx);
tud_cdc_n_get_line_coding(itf, &ud->usb_lc);
mutex_exit(&ud->lc_mtx);
usb_read_bytes(itf);
usb_write_bytes(itf);
}
static inline void uart_read_bytes(uint8_t itf)
{
uart_data_t *ud = &UART_DATA[itf];
const uart_id_t *ui = &UART_ID[itf];
if (uart_is_readable(ui->inst)) {
mutex_enter_blocking(&ud->uart_mtx);
while (uart_is_readable(ui->inst) &&
(ud->uart_pos < BUFFER_SIZE)) {
ud->uart_buffer[ud->uart_pos] = uart_getc(ui->inst);
ud->uart_pos++;
}
mutex_exit(&ud->uart_mtx);
}
}
void uart0_irq_fn(void)
{
uart_read_bytes(0);
}
void uart1_irq_fn(void)
{
uart_read_bytes(1);
}
void uart_write_bytes(uint8_t itf)
{
uart_data_t *ud = &UART_DATA[itf];
if (ud->usb_pos &&
mutex_try_enter(&ud->usb_mtx, NULL)) {
const uart_id_t *ui = &UART_ID[itf];
uint32_t count = 0;
while (uart_is_writable(ui->inst) &&
count < ud->usb_pos) {
uart_putc_raw(ui->inst, ud->usb_buffer[count]);
count++;
}
if (count < ud->usb_pos)
memmove(ud->usb_buffer, &ud->usb_buffer[count],
ud->usb_pos - count);
ud->usb_pos -= count;
mutex_exit(&ud->usb_mtx);
}
}
void init_uart_data(uint8_t itf)
{
const uart_id_t *ui = &UART_ID[itf];
uart_data_t *ud = &UART_DATA[itf];
/* Pinmux */
gpio_set_function(ui->tx_pin, GPIO_FUNC_UART);
gpio_set_function(ui->rx_pin, GPIO_FUNC_UART);
/* USB CDC LC */
ud->usb_lc.bit_rate = DEF_BIT_RATE;
ud->usb_lc.data_bits = DEF_DATA_BITS;
ud->usb_lc.parity = DEF_PARITY;
ud->usb_lc.stop_bits = DEF_STOP_BITS;
/* UART LC */
ud->uart_lc.bit_rate = DEF_BIT_RATE;
ud->uart_lc.data_bits = DEF_DATA_BITS;
ud->uart_lc.parity = DEF_PARITY;
ud->uart_lc.stop_bits = DEF_STOP_BITS;
/* Buffer */
ud->uart_pos = 0;
ud->usb_pos = 0;
/* Mutex */
mutex_init(&ud->lc_mtx);
mutex_init(&ud->uart_mtx);
mutex_init(&ud->usb_mtx);
/* UART start */
uart_init(ui->inst, ud->usb_lc.bit_rate);
uart_set_hw_flow(ui->inst, false, false);
uart_set_format(ui->inst, databits_usb2uart(ud->usb_lc.data_bits),
stopbits_usb2uart(ud->usb_lc.stop_bits),
parity_usb2uart(ud->usb_lc.parity));
uart_set_fifo_enabled(ui->inst, false);
/* UART RX Interrupt */
irq_set_exclusive_handler(ui->irq, ui->irq_fn);
irq_set_enabled(ui->irq, true);
uart_set_irq_enables(ui->inst, true, false);
}
void core1_entry(void)
{
for (int itf = 0; itf < CFG_TUD_CDC; itf++)
{
init_uart_data(0);
}
while (1)
{
for (int itf = 0; itf < CFG_TUD_CDC; itf++)
{
update_uart_cfg(itf);
uart_write_bytes(itf);
}
}
}
void usbd_serial_init(void);
int uart_bridge_run(void)
{
set_sys_clock_khz(125000, false);
usbd_serial_init();
tud_init(BOARD_TUD_RHPORT);
multicore_reset_core1();
multicore_launch_core1(core1_entry);
while (1)
{
tud_task();
for (int itf = 0; itf < CFG_TUD_CDC; itf++)
{
if (tud_cdc_n_connected(itf))
{
usb_cdc_process(itf);
}
}
}
return 0;
}
#define DESC_STR_MAX 20
#define USBD_VID 0x2E8A /* Raspberry Pi */
#define USBD_PID 0x000A /* Raspberry Pi Pico SDK CDC */
#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + (TUD_CDC_DESC_LEN * CFG_TUD_CDC))
#define USBD_MAX_POWER_MA 500
enum
{
USBD_ITF_CDC_0_1 = 0,
USBD_ITF_CDC_0_2,
#if CFG_TUD_CDC > 1
USBD_ITF_CDC_1_1,
USBD_ITF_CDC_1_2,
#endif
USBD_ITF_MAX,
};
#define USBD_CDC_0_EP_CMD 0x81
#define USBD_CDC_1_EP_CMD 0x83
#define USBD_CDC_0_EP_OUT 0x01
#define USBD_CDC_1_EP_OUT 0x03
#define USBD_CDC_0_EP_IN 0x82
#define USBD_CDC_1_EP_IN 0x84
#define USBD_CDC_CMD_MAX_SIZE 8
#define USBD_CDC_IN_OUT_MAX_SIZE 64
#define USBD_STR_0 0x00
#define USBD_STR_MANUF 0x01
#define USBD_STR_PRODUCT 0x02
#define USBD_STR_SERIAL 0x03
#define USBD_STR_SERIAL_LEN 17
#define USBD_STR_CDC 0x04
static const tusb_desc_device_t usbd_desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = USBD_VID,
.idProduct = USBD_PID,
.bcdDevice = 0x0100,
.iManufacturer = USBD_STR_MANUF,
.iProduct = USBD_STR_PRODUCT,
.iSerialNumber = USBD_STR_SERIAL,
.bNumConfigurations = 1,
};
static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = {
TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA),
TUD_CDC_DESCRIPTOR(USBD_ITF_CDC_0_1, USBD_STR_CDC, USBD_CDC_0_EP_CMD,
USBD_CDC_CMD_MAX_SIZE, USBD_CDC_0_EP_OUT, USBD_CDC_0_EP_IN,
USBD_CDC_IN_OUT_MAX_SIZE),
#if CFG_TUD_CDC > 1
TUD_CDC_DESCRIPTOR(USBD_ITF_CDC_1_1, USBD_STR_CDC, USBD_CDC_1_EP_CMD,
USBD_CDC_CMD_MAX_SIZE, USBD_CDC_1_EP_OUT, USBD_CDC_1_EP_IN,
USBD_CDC_IN_OUT_MAX_SIZE),
#endif
};
static char usbd_serial[USBD_STR_SERIAL_LEN] = "000000000000";
static const char *const usbd_desc_str[] = {
[USBD_STR_MANUF] = "Raspberry Pi",
[USBD_STR_PRODUCT] = "Pico",
[USBD_STR_SERIAL] = usbd_serial,
[USBD_STR_CDC] = "Board CDC",
};
const uint8_t *uart_bridge_descriptor_device_cb(void)
{
return (const uint8_t *) &usbd_desc_device;
}
const uint8_t *uart_bridge_descriptor_configuration_cb(uint8_t index)
{
return usbd_desc_cfg;
}
const uint16_t *uart_bridge_descriptor_string_cb(uint8_t index, uint16_t langid)
{
static uint16_t desc_str[DESC_STR_MAX];
uint8_t len;
if (index == 0) {
desc_str[1] = 0x0409;
len = 1;
} else {
const char *str;
char serial[USBD_STR_SERIAL_LEN];
if (index >= sizeof(usbd_desc_str) / sizeof(usbd_desc_str[0]))
return NULL;
str = usbd_desc_str[index];
for (len = 0; len < DESC_STR_MAX - 1 && str[len]; ++len)
desc_str[1 + len] = str[len];
}
desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * len + 2);
return desc_str;
}
void usbd_serial_init(void)
{
uint8_t id[8];
flash_get_unique_id(id);
snprintf(usbd_serial, USBD_STR_SERIAL_LEN, "%02X%02X%02X%02X%02X%02X%02X%02X",
id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7]);
}

View File

@@ -0,0 +1,17 @@
#ifndef _UART_BRIDGE_H_
#define _UART_BRIDGE_H_
#ifdef __cplusplus
extern "C" {
#endif
int uart_bridge_run(void);
const uint8_t *uart_bridge_descriptor_device_cb(void);
const uint8_t *uart_bridge_descriptor_configuration_cb(uint8_t index);
const uint16_t *uart_bridge_descriptor_string_cb(uint8_t index, uint16_t langid);
#ifdef __cplusplus
}
#endif
#endif // _UART_BRIDGE_H_

View File

@@ -0,0 +1,155 @@
#include "class/cdc/cdc_device.h"
#include "bsp/board_api.h"
#include "Descriptors/CDCDev.h"
#include "USBDevice/DeviceDriver/WebApp/WebApp.h"
void WebAppDevice::initialize()
{
class_driver_ =
{
#if CFG_TUSB_DEBUG >= 2
.name = "WEBAPP",
#endif
.init = cdcd_init,
.reset = cdcd_reset,
.open = cdcd_open,
.control_xfer_cb = cdcd_control_xfer_cb,
.xfer_cb = cdcd_xfer_cb,
.sof = NULL
};
}
void WebAppDevice::process(const uint8_t idx, Gamepad& gamepad)
{
if (!tud_cdc_available() || !tud_cdc_connected())
{
return;
}
tud_cdc_write_flush();
tud_cdc_read(reinterpret_cast<void*>(&in_report_), sizeof(Report));
tud_cdc_read_flush();
bool success = false;
switch (in_report_.report_id)
{
case ReportID::INIT_READ:
in_report_.input_mode = static_cast<uint8_t>(driver_type_);
in_report_.player_idx = 0;
in_report_.report_id = ReportID::RESP_OK;
in_report_.max_gamepads = MAX_GAMEPADS;
in_report_.profile.id = user_settings_.get_active_profile_id(in_report_.player_idx);
in_report_.profile = user_settings_.get_profile_by_id(in_report_.profile.id);
tud_cdc_write(reinterpret_cast<const void*>(&in_report_), sizeof(Report));
tud_cdc_write_flush();
break;
case ReportID::READ_PROFILE:
in_report_.input_mode = static_cast<uint8_t>(driver_type_);
in_report_.profile = user_settings_.get_profile_by_id(in_report_.profile.id);
in_report_.report_id = ReportID::RESP_OK;
tud_cdc_write(reinterpret_cast<const void*>(&in_report_), sizeof(Report));
tud_cdc_write_flush();
break;
case ReportID::WRITE_PROFILE:
if (in_report_.input_mode != static_cast<uint8_t>(driver_type_) && in_report_.input_mode != 0)
{
success = user_settings_.store_profile_and_driver_type_safe(static_cast<DeviceDriver::Type>(in_report_.input_mode), in_report_.player_idx, in_report_.profile);
}
else
{
success = user_settings_.store_profile_safe(in_report_.player_idx, in_report_.profile);
}
if (!success)
{
in_report_.report_id = ReportID::RESP_ERROR;
tud_cdc_write(reinterpret_cast<const void*>(&in_report_), sizeof(Report));
tud_cdc_write_flush();
}
break;
default:
return;
}
}
uint16_t WebAppDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
{
return reqlen;
}
void WebAppDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
bool WebAppDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
return false;
}
const uint16_t * WebAppDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
{
size_t chr_count;
switch ( index )
{
case CDCDesc::STRID_LANGID:
std::memcpy(&CDCDesc::_desc_str[1], CDCDesc::STRING_DESCRIPTORS[0], 2);
chr_count = 1;
break;
case CDCDesc::STRID_SERIAL:
chr_count = board_usb_get_serial(CDCDesc::_desc_str + 1, 32);
break;
default:
if ( !(index < sizeof(CDCDesc::STRING_DESCRIPTORS) / sizeof(CDCDesc::STRING_DESCRIPTORS[0])) )
{
return NULL;
}
const char *str = CDCDesc::STRING_DESCRIPTORS[index];
chr_count = strlen(str);
size_t const max_count = sizeof(CDCDesc::_desc_str) / sizeof(CDCDesc::_desc_str[0]) - 1;
if ( chr_count > max_count )
{
chr_count = max_count;
}
for ( size_t i = 0; i < chr_count; i++ )
{
CDCDesc::_desc_str[1 + i] = str[i];
}
break;
}
CDCDesc::_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return CDCDesc::_desc_str;
}
const uint8_t * WebAppDevice::get_descriptor_device_cb()
{
return reinterpret_cast<const uint8_t*>(&CDCDesc::DEVICE_DESCRIPTORS);
}
const uint8_t * WebAppDevice::get_hid_descriptor_report_cb(uint8_t itf)
{
return nullptr;
}
const uint8_t * WebAppDevice::get_descriptor_configuration_cb(uint8_t index)
{
return CDCDesc::CONFIGURATION_DESCRIPTORS;
}
const uint8_t * WebAppDevice::get_descriptor_device_qualifier_cb()
{
return nullptr;
}

View File

@@ -0,0 +1,49 @@
#ifndef _WEBAAPP_DEVICE_H_
#define _WEBAAPP_DEVICE_H_
#include "USBDevice/DeviceDriver/DeviceDriver.h"
#include "UserSettings/UserSettings.h"
#include "UserSettings/UserProfile.h"
class WebAppDevice : public DeviceDriver
{
public:
void initialize() override;
void process(const uint8_t idx, Gamepad& gamepad) override;
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
const uint8_t* get_descriptor_device_cb() override;
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
const uint8_t* get_descriptor_device_qualifier_cb() override;
private:
struct ReportID
{
static constexpr uint8_t INIT_READ = 0x88;
static constexpr uint8_t READ_PROFILE = 0x01;
static constexpr uint8_t WRITE_PROFILE = 0x02;
static constexpr uint8_t RESP_OK = 0x10;
static constexpr uint8_t RESP_ERROR = 0x11;
};
#pragma pack(push, 1)
struct Report
{
uint8_t report_id{0};
uint8_t input_mode{0};
uint8_t max_gamepads{MAX_GAMEPADS};
uint8_t player_idx{0};
UserProfile profile{UserProfile()};
};
static_assert(sizeof(Report) == 50, "WebApp report size mismatch");
#pragma pack(pop)
UserSettings user_settings_{UserSettings()};
Report in_report_{Report()};
DeviceDriver::Type driver_type_{DeviceDriver::Type::WEBAPP};
};
#endif // _WEBAAPP_DEVICE_H_

View File

@@ -0,0 +1,123 @@
#include <cstring>
#include <vector>
#include "USBDevice/DeviceDriver/XInput/tud_xinput/tud_xinput.h"
#include "USBDevice/DeviceDriver/XInput/XInput.h"
void XInputDevice::initialize()
{
class_driver_ = *tud_xinput::class_driver();
std::memset(&in_report_, 0, sizeof(XInput::InReport));
std::memset(&prev_in_report_, 0, sizeof(XInput::InReport));
in_report_.report_size = XInput::ENDPOINT_IN_SIZE;
}
void XInputDevice::process(const uint8_t idx, Gamepad& gamepad)
{
std::memset(&in_report_.buttons, 0, sizeof(in_report_.buttons));
switch (gamepad.get_dpad_buttons())
{
case Gamepad::DPad::UP:
in_report_.buttons[0] = XInput::Buttons0::DPAD_UP;
break;
case Gamepad::DPad::DOWN:
in_report_.buttons[0] = XInput::Buttons0::DPAD_DOWN;
break;
case Gamepad::DPad::LEFT:
in_report_.buttons[0] = XInput::Buttons0::DPAD_LEFT;
break;
case Gamepad::DPad::RIGHT:
in_report_.buttons[0] = XInput::Buttons0::DPAD_RIGHT;
break;
case Gamepad::DPad::UP_LEFT:
in_report_.buttons[0] = (XInput::Buttons0::DPAD_UP | XInput::Buttons0::DPAD_LEFT);
break;
case Gamepad::DPad::UP_RIGHT:
in_report_.buttons[0] = XInput::Buttons0::DPAD_UP | XInput::Buttons0::DPAD_RIGHT;
break;
case Gamepad::DPad::DOWN_LEFT:
in_report_.buttons[0] = XInput::Buttons0::DPAD_DOWN | XInput::Buttons0::DPAD_LEFT;
break;
case Gamepad::DPad::DOWN_RIGHT:
in_report_.buttons[0] = XInput::Buttons0::DPAD_DOWN | XInput::Buttons0::DPAD_RIGHT;
break;
default:
break;
}
uint16_t gp_buttons = gamepad.get_buttons();
if (gp_buttons & Gamepad::Button::BACK) in_report_.buttons[0] |= XInput::Buttons0::BACK;
if (gp_buttons & Gamepad::Button::START) in_report_.buttons[0] |= XInput::Buttons0::START;
if (gp_buttons & Gamepad::Button::L3) in_report_.buttons[0] |= XInput::Buttons0::L3;
if (gp_buttons & Gamepad::Button::R3) in_report_.buttons[0] |= XInput::Buttons0::R3;
if (gp_buttons & Gamepad::Button::X) in_report_.buttons[1] |= XInput::Buttons1::X;
if (gp_buttons & Gamepad::Button::A) in_report_.buttons[1] |= XInput::Buttons1::A;
if (gp_buttons & Gamepad::Button::Y) in_report_.buttons[1] |= XInput::Buttons1::Y;
if (gp_buttons & Gamepad::Button::B) in_report_.buttons[1] |= XInput::Buttons1::B;
if (gp_buttons & Gamepad::Button::LB) in_report_.buttons[1] |= XInput::Buttons1::LB;
if (gp_buttons & Gamepad::Button::RB) in_report_.buttons[1] |= XInput::Buttons1::RB;
if (gp_buttons & Gamepad::Button::SYS) in_report_.buttons[1] |= XInput::Buttons1::HOME;
in_report_.trigger_l = gamepad.get_trigger_l().uint8();
in_report_.trigger_r = gamepad.get_trigger_r().uint8();
in_report_.joystick_lx = gamepad.get_joystick_lx().int16();
in_report_.joystick_ly = gamepad.get_joystick_ly().int16(true);
in_report_.joystick_rx = gamepad.get_joystick_rx().int16();
in_report_.joystick_ry = gamepad.get_joystick_ry().int16(true);
if (std::memcmp(&prev_in_report_, &in_report_, sizeof(in_report_)) != 0 &&
tud_xinput::send_report(reinterpret_cast<uint8_t*>(&in_report_), sizeof(XInput::InReport)))
{
std::memcpy(&prev_in_report_, &in_report_, sizeof(XInput::InReport));
}
if (tud_xinput::receive_report(reinterpret_cast<uint8_t*>(&out_report_), sizeof(XInput::OutReport)) &&
out_report_.report_id == XInput::OutReportID::RUMBLE)
{
gamepad.set_rumble_l(out_report_.rumble_l);
gamepad.set_rumble_r(out_report_.rumble_r);
}
}
uint16_t XInputDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
{
std::memcpy(buffer, &in_report_, sizeof(in_report_));
return sizeof(XInput::InReport);
}
void XInputDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
bool XInputDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
return false;
}
const uint16_t * XInputDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
{
const char *value = (const char *)XInput::STRING_DESCRIPTORS[index];
return get_string_descriptor(value, index);
}
const uint8_t * XInputDevice::get_descriptor_device_cb()
{
return XInput::DEVICE_DESCRIPTORS;
}
const uint8_t * XInputDevice::get_hid_descriptor_report_cb(uint8_t itf)
{
return nullptr;
}
const uint8_t * XInputDevice::get_descriptor_configuration_cb(uint8_t index)
{
return XInput::CONFIGURATION_DESCRIPTORS;
}
const uint8_t * XInputDevice::get_descriptor_device_qualifier_cb()
{
return nullptr;
}

View File

@@ -0,0 +1,27 @@
#ifndef _XINPUT_DEVICE_H_
#define _XINPUT_DEVICE_H_
#include "USBDevice/DeviceDriver/DeviceDriver.h"
#include "Descriptors/XInput.h"
class XInputDevice : public DeviceDriver
{
public:
void initialize() override;
void process(const uint8_t idx, Gamepad& gamepad) override;
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
const uint8_t* get_descriptor_device_cb() override;
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
const uint8_t* get_descriptor_device_qualifier_cb() override;
private:
XInput::InReport in_report_;
XInput::InReport prev_in_report_;
XInput::OutReport out_report_;
};
#endif // _XINPUT_DEVICE_H_

View File

@@ -0,0 +1,137 @@
#include "tusb_option.h"
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XINPUT)
#include <cstring>
#include <array>
#include <algorithm>
#include "tusb.h"
#include "device/usbd_pvt.h"
#include "Descriptors/XInput.h"
#include "USBDevice/DeviceDriver/XInput/tud_xinput/tud_xinput.h"
namespace tud_xinput {
static constexpr uint8_t ENDPOINT_SIZE = 32;
uint8_t endpoint_in_ = 0xFF;
uint8_t endpoint_out_ = 0xFF;
std::array<uint8_t, ENDPOINT_SIZE> out_buffer_;
//Class Driver
static void init(void)
{
out_buffer_.fill(0);
}
static void reset(uint8_t rhport)
{
endpoint_in_ = 0xFF;
endpoint_out_ = 0xFF;
init();
}
static uint16_t open(uint8_t rhport, tusb_desc_interface_t const *itf_descriptor, uint16_t max_length)
{
uint16_t driver_length = static_cast<uint16_t>(sizeof(tusb_desc_interface_t) + (itf_descriptor->bNumEndpoints * sizeof(tusb_desc_endpoint_t)) + 16);
TU_VERIFY(max_length >= driver_length, 0);
uint8_t const *current_descriptor = tu_desc_next(itf_descriptor);
uint8_t found_endpoints = 0;
while ((found_endpoints < itf_descriptor->bNumEndpoints) && (driver_length <= max_length))
{
tusb_desc_endpoint_t const *endpoint_descriptor = reinterpret_cast<const tusb_desc_endpoint_t*>(current_descriptor);
if (TUSB_DESC_ENDPOINT == tu_desc_type(endpoint_descriptor))
{
TU_ASSERT(usbd_edpt_open(rhport, endpoint_descriptor));
if (tu_edpt_dir(endpoint_descriptor->bEndpointAddress) == TUSB_DIR_IN)
endpoint_in_ = endpoint_descriptor->bEndpointAddress;
else
endpoint_out_ = endpoint_descriptor->bEndpointAddress;
++found_endpoints;
}
current_descriptor = tu_desc_next(current_descriptor);
}
return driver_length;
}
static bool control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
return true;
}
static bool xfer_callback(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
if (ep_addr == endpoint_out_)
{
usbd_edpt_xfer(0, endpoint_out_, out_buffer_.data(), ENDPOINT_SIZE);
}
return true;
}
const usbd_class_driver_t tud_class_driver_ =
{
#if CFG_TUSB_DEBUG >= 2
.name = "XINPUT",
#endif
.init = init,
.reset = reset,
.open = open,
.control_xfer_cb = control_xfer_cb,
.xfer_cb = xfer_callback,
.sof = NULL
};
// Public API
const usbd_class_driver_t* class_driver()
{
return &tud_class_driver_;
}
bool send_report_ready()
{
return (tud_ready() && endpoint_in_ != 0xFF && !usbd_edpt_busy(BOARD_TUD_RHPORT, endpoint_in_));
}
bool send_report(const uint8_t *report, uint16_t len)
{
if (tud_suspended())
{
tud_remote_wakeup();
}
if (send_report_ready())
{
usbd_edpt_claim(BOARD_TUD_RHPORT, endpoint_in_);
usbd_edpt_xfer(BOARD_TUD_RHPORT, endpoint_in_, const_cast<uint8_t*>(report), len);
usbd_edpt_release(BOARD_TUD_RHPORT, endpoint_in_);
return true;
}
return false;
}
bool receive_report(uint8_t *report, uint16_t len)
{
if (len > ENDPOINT_SIZE || endpoint_out_ == 0xFF)
{
return false;
}
std::memcpy(report, out_buffer_.data(), len);
usbd_edpt_xfer(BOARD_TUD_RHPORT, endpoint_out_, out_buffer_.data(), ENDPOINT_SIZE);
return true;
}
}; // namespace TUDXInput
#endif // (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XINPUT)

View File

@@ -0,0 +1,17 @@
#ifndef _TUD_XINPUT_H_
#define _TUD_XINPUT_H_
#include <cstdint>
#include "tusb.h"
#include "device/usbd_pvt.h"
namespace tud_xinput
{
bool send_report_ready();
bool send_report(const uint8_t *report, uint16_t len);
bool receive_report(uint8_t *report, uint16_t len);
const usbd_class_driver_t* class_driver();
};
#endif // _TUD_XINPUT_H_

View File

@@ -0,0 +1,136 @@
#include <cstring>
#include <vector>
#include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.h"
#include "USBDevice/DeviceDriver/XboxOG/XboxOG_GP.h"
void XboxOGDevice::initialize()
{
tud_xid::initialize(tud_xid::Type::GAMEPAD);
class_driver_ = *tud_xid::class_driver();
std::memset(&in_report_, 0, sizeof(XboxOG::GP::InReport));
in_report_.report_len = sizeof(XboxOG::GP::InReport);
std::memcpy(&prev_in_report_, &in_report_, sizeof(XboxOG::GP::InReport));
}
void XboxOGDevice::process(const uint8_t idx, Gamepad& gamepad)
{
std::memset(&in_report_.buttons, 0, 8);
switch (gamepad.get_dpad_buttons())
{
case Gamepad::DPad::UP:
in_report_.buttons = XboxOG::GP::Buttons::DPAD_UP;
break;
case Gamepad::DPad::DOWN:
in_report_.buttons = XboxOG::GP::Buttons::DPAD_DOWN;
break;
case Gamepad::DPad::LEFT:
in_report_.buttons = XboxOG::GP::Buttons::DPAD_LEFT;
break;
case Gamepad::DPad::RIGHT:
in_report_.buttons = XboxOG::GP::Buttons::DPAD_RIGHT;
break;
case Gamepad::DPad::UP_LEFT:
in_report_.buttons = XboxOG::GP::Buttons::DPAD_UP | XboxOG::GP::Buttons::DPAD_LEFT;
break;
case Gamepad::DPad::UP_RIGHT:
in_report_.buttons = XboxOG::GP::Buttons::DPAD_UP | XboxOG::GP::Buttons::DPAD_RIGHT;
break;
case Gamepad::DPad::DOWN_LEFT:
in_report_.buttons = XboxOG::GP::Buttons::DPAD_DOWN | XboxOG::GP::Buttons::DPAD_LEFT;
break;
case Gamepad::DPad::DOWN_RIGHT:
in_report_.buttons = XboxOG::GP::Buttons::DPAD_DOWN | XboxOG::GP::Buttons::DPAD_RIGHT;
break;
default:
break;
}
uint16_t gp_buttons = gamepad.get_buttons();
if (gp_buttons & Gamepad::Button::BACK) in_report_.buttons |= XboxOG::GP::Buttons::BACK;
if (gp_buttons & Gamepad::Button::START) in_report_.buttons |= XboxOG::GP::Buttons::START;
if (gp_buttons & Gamepad::Button::L3) in_report_.buttons |= XboxOG::GP::Buttons::L3;
if (gp_buttons & Gamepad::Button::R3) in_report_.buttons |= XboxOG::GP::Buttons::R3;
if (gamepad.analog_enabled())
{
in_report_.a = gamepad.get_analog_a();
in_report_.b = gamepad.get_analog_b();
in_report_.x = gamepad.get_analog_x();
in_report_.y = gamepad.get_analog_y();
in_report_.white = gamepad.get_analog_lb();
in_report_.black = gamepad.get_analog_rb();
}
else
{
if (gp_buttons & Gamepad::Button::X) in_report_.x = 0xFF;
if (gp_buttons & Gamepad::Button::A) in_report_.a = 0xFF;
if (gp_buttons & Gamepad::Button::Y) in_report_.y = 0xFF;
if (gp_buttons & Gamepad::Button::B) in_report_.b = 0xFF;
if (gp_buttons & Gamepad::Button::LB) in_report_.white = 0xFF;
if (gp_buttons & Gamepad::Button::RB) in_report_.black = 0xFF;
}
in_report_.trigger_l = gamepad.get_trigger_l().uint8();
in_report_.trigger_r = gamepad.get_trigger_r().uint8();
in_report_.joystick_lx = gamepad.get_joystick_lx().int16();
in_report_.joystick_ly = gamepad.get_joystick_ly().int16(true);
in_report_.joystick_rx = gamepad.get_joystick_rx().int16();
in_report_.joystick_ry = gamepad.get_joystick_ry().int16(true);
if (std::memcmp(&prev_in_report_, &in_report_, sizeof(in_report_)) != 0 &&
tud_xid::send_report(0, reinterpret_cast<uint8_t*>(&in_report_), sizeof(XboxOG::GP::InReport)))
{
std::memcpy(&prev_in_report_, &in_report_, sizeof(XboxOG::GP::InReport));
}
if (tud_xid::receive_report(0, reinterpret_cast<uint8_t*>(&out_report_), sizeof(XboxOG::GP::OutReport)))
{
gamepad.set_rumble_l(out_report_.rumble_l);
gamepad.set_rumble_r(out_report_.rumble_r);
}
}
uint16_t XboxOGDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
{
std::memcpy(buffer, &in_report_, sizeof(in_report_));
return sizeof(XboxOG::GP::InReport);
}
void XboxOGDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
bool XboxOGDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
return tud_xid::class_driver()->control_xfer_cb(rhport, stage, request);
}
const uint16_t* XboxOGDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
{
const char *value = reinterpret_cast<const char*>(XboxOG::GP::STRING_DESCRIPTORS[index]);
return get_string_descriptor(value, index);
}
const uint8_t* XboxOGDevice::get_descriptor_device_cb()
{
return reinterpret_cast<const uint8_t*>(&XboxOG::GP::DEVICE_DESCRIPTORS);
}
const uint8_t* XboxOGDevice::get_hid_descriptor_report_cb(uint8_t itf)
{
return nullptr;
}
const uint8_t* XboxOGDevice::get_descriptor_configuration_cb(uint8_t index)
{
return XboxOG::GP::CONFIGURATION_DESCRIPTORS;
}
const uint8_t* XboxOGDevice::get_descriptor_device_qualifier_cb()
{
return nullptr;
}

View File

@@ -0,0 +1,29 @@
#ifndef _XBOXGOG_DEVICE_H_
#define _XBOXGOG_DEVICE_H_
#include <cstdint>
#include "USBDevice/DeviceDriver/DeviceDriver.h"
#include "Descriptors/XboxOG.h"
class XboxOGDevice : public DeviceDriver
{
public:
void initialize() override;
void process(const uint8_t idx, Gamepad& gamepad) override;
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
const uint8_t* get_descriptor_device_cb() override;
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
const uint8_t* get_descriptor_device_qualifier_cb() override;
private:
XboxOG::GP::InReport in_report_;
XboxOG::GP::InReport prev_in_report_;
XboxOG::GP::OutReport out_report_;
};
#endif // _XBOXGOG_DEVICE_H_

View File

@@ -0,0 +1,368 @@
#include <cstring>
#include <vector>
#include "pico/time.h"
#include "Descriptors/XInput.h"
#include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.h"
#include "USBDevice/DeviceDriver/XboxOG/XboxOG_SB.h"
static constexpr std::array<XboxOGSBDevice::ButtonMap, 9> GP_MAP =
{{
{Gamepad::Button::START, XboxOG::SB::Buttons0::START, 0},
{Gamepad::Button::LB, XboxOG::SB::Buttons0::RIGHTJOYFIRE, 0},
{Gamepad::Button::R3, XboxOG::SB::Buttons0::RIGHTJOYLOCKON, 0},
{Gamepad::Button::B, XboxOG::SB::Buttons0::RIGHTJOYLOCKON, 0},
{Gamepad::Button::RB, XboxOG::SB::Buttons0::RIGHTJOYMAINWEAPON, 0},
{Gamepad::Button::A, XboxOG::SB::Buttons0::RIGHTJOYMAINWEAPON, 0},
{Gamepad::Button::SYS, XboxOG::SB::Buttons0::EJECT, 0},
{Gamepad::Button::L3, XboxOG::SB::Buttons2::LEFTJOYSIGHTCHANGE, 2},
{Gamepad::Button::Y, XboxOG::SB::Buttons1::CHAFF, 1}
}};
static constexpr std::array<XboxOGSBDevice::ButtonMap, 19> CHATPAD_MAP =
{{
{XInput::Chatpad::CODE_0, XboxOG::SB::Buttons0::EJECT, 0},
{XInput::Chatpad::CODE_D, XboxOG::SB::Buttons1::WASHING, 1},
{XInput::Chatpad::CODE_F, XboxOG::SB::Buttons1::EXTINGUISHER, 1},
{XInput::Chatpad::CODE_G, XboxOG::SB::Buttons1::CHAFF, 1},
{XInput::Chatpad::CODE_X, XboxOG::SB::Buttons1::WEAPONCONMAIN, 1},
{XInput::Chatpad::CODE_RIGHT, XboxOG::SB::Buttons1::WEAPONCONMAIN, 1},
{XInput::Chatpad::CODE_C, XboxOG::SB::Buttons1::WEAPONCONSUB, 1},
{XInput::Chatpad::CODE_LEFT, XboxOG::SB::Buttons1::WEAPONCONSUB, 1},
{XInput::Chatpad::CODE_V, XboxOG::SB::Buttons1::WEAPONCONMAGAZINE, 1},
{XInput::Chatpad::CODE_SPACE, XboxOG::SB::Buttons1::WEAPONCONMAGAZINE, 1},
{XInput::Chatpad::CODE_U, XboxOG::SB::Buttons0::MULTIMONOPENCLOSE, 0},
{XInput::Chatpad::CODE_J, XboxOG::SB::Buttons0::MULTIMONMODESELECT, 0},
{XInput::Chatpad::CODE_N, XboxOG::SB::Buttons0::MAINMONZOOMIN, 0},
{XInput::Chatpad::CODE_I, XboxOG::SB::Buttons0::MULTIMONMAPZOOMINOUT, 0},
{XInput::Chatpad::CODE_K, XboxOG::SB::Buttons0::MULTIMONSUBMONITOR, 0},
{XInput::Chatpad::CODE_M, XboxOG::SB::Buttons0::MAINMONZOOMOUT, 0},
{XInput::Chatpad::CODE_ENTER, XboxOG::SB::Buttons0::START, 0},
{XInput::Chatpad::CODE_P, XboxOG::SB::Buttons0::COCKPITHATCH, 0},
{XInput::Chatpad::CODE_COMMA, XboxOG::SB::Buttons0::IGNITION, 0}
}};
static constexpr std::array<XboxOGSBDevice::ButtonMap, 5> CHATPAD_MAP_ALT1 =
{{
{XInput::Chatpad::CODE_1, XboxOG::SB::Buttons1::COMM1, 1},
{XInput::Chatpad::CODE_2, XboxOG::SB::Buttons1::COMM2, 1},
{XInput::Chatpad::CODE_3, XboxOG::SB::Buttons1::COMM3, 1},
{XInput::Chatpad::CODE_4, XboxOG::SB::Buttons1::COMM4, 1},
{XInput::Chatpad::CODE_5, XboxOG::SB::Buttons2::COMM5, 2}
}};
static constexpr std::array<XboxOGSBDevice::ButtonMap, 9> CHATPAD_MAP_ALT2=
{{
{XInput::Chatpad::CODE_1, XboxOG::SB::Buttons1::FUNCTIONF1, 1},
{XInput::Chatpad::CODE_2, XboxOG::SB::Buttons1::FUNCTIONTANKDETACH, 1},
{XInput::Chatpad::CODE_3, XboxOG::SB::Buttons0::FUNCTIONFSS, 0},
{XInput::Chatpad::CODE_4, XboxOG::SB::Buttons1::FUNCTIONF2, 1},
{XInput::Chatpad::CODE_5, XboxOG::SB::Buttons1::FUNCTIONOVERRIDE, 1},
{XInput::Chatpad::CODE_6, XboxOG::SB::Buttons0::FUNCTIONMANIPULATOR, 0},
{XInput::Chatpad::CODE_7, XboxOG::SB::Buttons1::FUNCTIONF3, 1},
{XInput::Chatpad::CODE_8, XboxOG::SB::Buttons1::FUNCTIONNIGHTSCOPE, 1},
{XInput::Chatpad::CODE_9, XboxOG::SB::Buttons0::FUNCTIONLINECOLORCHANGE, 0}
}};
static constexpr std::array<XboxOGSBDevice::ButtonMap, 5> CHATPAD_TOGGLE_MAP =
{{
{XInput::Chatpad::CODE_Q, XboxOG::SB::Buttons2::TOGGLEOXYGENSUPPLY, 2},
{XInput::Chatpad::CODE_A, XboxOG::SB::Buttons2::TOGGLEFILTERCONTROL, 2},
{XInput::Chatpad::CODE_W, XboxOG::SB::Buttons2::TOGGLEVTLOCATION, 2},
{XInput::Chatpad::CODE_S, XboxOG::SB::Buttons2::TOGGLEBUFFREMATERIAL, 2},
{XInput::Chatpad::CODE_Z, XboxOG::SB::Buttons2::TOGGLEFUELFLOWRATE, 2}
}};
void XboxOGSBDevice::initialize()
{
tud_xid::initialize(tud_xid::Type::STEELBATTALION);
class_driver_ = *tud_xid::class_driver();
std::memset(&in_report_, 0, sizeof(XboxOG::SB::InReport));
in_report_.bLength = sizeof(XboxOG::SB::InReport);
in_report_.gearLever = XboxOG::SB::Gear::N;
std::memcpy(&prev_in_report_, &in_report_, sizeof(XboxOG::SB::InReport));
}
void XboxOGSBDevice::process(const uint8_t idx, Gamepad& gamepad)
{
// // uint8_t xid_index = tud_xid::get_index_by_type(0, tud_xid::Type::STEELBATTALION);
// // if (tud_xid::send_report_ready(xid_index))
// // {
in_report_.dButtons[0] = 0;
in_report_.dButtons[1] = 0;
in_report_.dButtons[2] &= XboxOG::SB::BUTTONS2_TOGGLE_MID;
uint16_t gp_buttons = gamepad.get_buttons();
for (const auto& map : GP_MAP)
{
if (gp_buttons & map.gp_mask)
{
in_report_.dButtons[map.button_offset] |= map.sb_mask;
}
}
Gamepad::Chatpad gp_chatpad = gamepad.get_chatpad();
for (const auto& map : CHATPAD_MAP)
{
if (chatpad_pressed(gp_chatpad, map.gp_mask))
{
in_report_.dButtons[map.button_offset] |= map.sb_mask;
}
}
for (const auto& map : CHATPAD_TOGGLE_MAP)
{
if (chatpad_pressed(gp_chatpad, map.gp_mask))
{
in_report_.dButtons[map.button_offset] |= map.sb_mask;
}
}
if (gp_buttons & Gamepad::Button::X)
{
if (out_report_.Chaff_Extinguisher & 0x0F)
{
in_report_.dButtons[1] |= XboxOG::SB::Buttons1::EXTINGUISHER;
}
if (out_report_.Comm1_MagazineChange & 0x0F)
{
in_report_.dButtons[1] |= XboxOG::SB::Buttons1::WEAPONCONMAGAZINE;
}
if (out_report_.Washing_LineColorChange & 0xF0)
{
in_report_.dButtons[1] |= XboxOG::SB::Buttons1::WASHING;
}
}
uint8_t gp_dpad = gamepad.get_dpad_buttons();
if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_MESSENGER) || gp_buttons & Gamepad::Button::BACK)
{
for (const auto& map : CHATPAD_MAP_ALT1)
{
if (chatpad_pressed(gp_chatpad, map.gp_mask))
{
in_report_.dButtons[map.button_offset] |= map.sb_mask;
}
}
// for (uint8_t i = 0; i < sizeof(CHATPAD_MAP_ALT1) / sizeof(CHATPAD_MAP_ALT1[0]); i++)
// {
// if (chatpad_pressed(gp_chatpad, CHATPAD_MAP_ALT1[i].gp_mask))
// {
// in_report_.dButtons[CHATPAD_MAP_ALT1[i].button_offset] |= CHATPAD_MAP_ALT1[i].sb_mask;
// }
// }
if (gp_dpad & Gamepad::DPad::UP || gp_dpad & Gamepad::DPad::RIGHT)
{
in_report_.tunerDial += (prev_in_report_.tunerDial < 15) ? 1 : -15;
}
if (gp_dpad & Gamepad::DPad::DOWN || gp_dpad & Gamepad::DPad::LEFT)
{
in_report_.tunerDial -= (prev_in_report_.tunerDial > 0) ? 1 : -15;
}
}
else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_ORANGE))
{
for (const auto& map : CHATPAD_MAP_ALT2)
{
if (chatpad_pressed(gp_chatpad, map.gp_mask))
{
in_report_.dButtons[map.button_offset] |= map.sb_mask;
}
}
// for (uint8_t i = 0; i < sizeof(CHATPAD_MAP_ALT2) / sizeof(CHATPAD_MAP_ALT2[0]); i++)
// {
// if (chatpad_pressed(gp_chatpad, CHATPAD_MAP_ALT2[i].gp_mask))
// {
// in_report_.dButtons[CHATPAD_MAP_ALT2[i].button_offset] |= CHATPAD_MAP_ALT2[i].sb_mask;
// }
// }
if (!(gp_dpad & Gamepad::DPad::LEFT) && !(gp_dpad & Gamepad::DPad::RIGHT))
{
if (gp_dpad & Gamepad::DPad::UP)
{
in_report_.gearLever += (prev_in_report_.gearLever < XboxOG::SB::Gear::G5) ? 1 : 0;
}
if (gp_dpad & Gamepad::DPad::DOWN)
{
in_report_.gearLever -= (prev_in_report_.gearLever > XboxOG::SB::Gear::R) ? 1 : 0;
}
}
}
if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_SHIFT))
{
if (in_report_.dButtons[2] & XboxOG::SB::BUTTONS2_TOGGLE_MID)
{
in_report_.dButtons[2] &= ~XboxOG::SB::BUTTONS2_TOGGLE_MID;
}
else
{
in_report_.dButtons[2] |= XboxOG::SB::BUTTONS2_TOGGLE_MID;
}
}
in_report_.leftPedal = gamepad.get_trigger_l().uint16();
in_report_.rightPedal = gamepad.get_trigger_r().uint16();
in_report_.middlePedal = chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_BACK) ? 0xFF00 : 0x0000;
in_report_.rotationLever = chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_MESSENGER) ? 0 :
(gp_buttons & Gamepad::Button::BACK) ? 0 :
(gp_dpad & Gamepad::DPad::LEFT) ? INT16_MIN :
(gp_dpad & Gamepad::DPad::RIGHT) ? INT16_MAX : 0;
in_report_.sightChangeX = gamepad.get_joystick_lx().int16(true);
in_report_.sightChangeY = gamepad.get_joystick_ly().int16();
int32_t axis_value = static_cast<int32_t>(gamepad.get_joystick_rx().int16(true));
if (axis_value > XboxOG::SB::DEFAULT_DEADZONE)
{
vmouse_x_ += axis_value / sensitivity_;
}
axis_value = static_cast<int32_t>(gamepad.get_joystick_ry().int16(true));
if (axis_value > XboxOG::SB::DEFAULT_DEADZONE)
{
vmouse_y_ -= axis_value / sensitivity_;
}
if (vmouse_x_ < 0) vmouse_x_ = 0;
if (vmouse_x_ > UINT16_MAX) vmouse_x_ = UINT16_MAX;
if (vmouse_y_ > UINT16_MAX) vmouse_y_ = UINT16_MAX;
if (vmouse_y_ < 0) vmouse_y_ = 0;
if (gp_buttons & Gamepad::Button::L3)
{
if ((time_us_32() / 1000) - aim_reset_timer_ > 500)
{
vmouse_x_ = XboxOG::SB::AIMING_MID;
vmouse_y_ = XboxOG::SB::AIMING_MID;
}
}
else
{
aim_reset_timer_ = time_us_32() / 1000;
}
in_report_.aimingX = static_cast<uint16_t>(vmouse_x_);
in_report_.aimingY = static_cast<uint16_t>(vmouse_y_);
if (tud_suspended())
{
tud_remote_wakeup();
}
if (std::memcmp(&prev_in_report_, &in_report_, sizeof(XboxOG::SB::InReport)) != 0 &&
tud_xid::send_report(0, reinterpret_cast<uint8_t*>(&in_report_), sizeof(XboxOG::SB::InReport)))
{
std::memcpy(&prev_in_report_, &in_report_, sizeof(XboxOG::SB::InReport));
}
// }
// Gamepad::Chatpad gp_chatpad = gamepad.chatpad();
// gp_chatpad = gamepad.get_chatpad();
// if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_ORANGE))
// {
// uint16_t new_sense = 0;
// if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_9))
// {
// new_sense = 200;
// }
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_8))
// {
// new_sense = 250;
// }
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_7))
// {
// new_sense = 300;
// }
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_6))
// {
// new_sense = 350;
// }
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_5))
// {
// new_sense = 400;
// }
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_4))
// {
// new_sense = 650;
// }
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_3))
// {
// new_sense = 800;
// }
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_2))
// {
// new_sense = 1000;
// }
// else if (chatpad_pressed(gp_chatpad, XInput::Chatpad::CODE_1))
// {
// new_sense = 1200;
// }
// if (new_sense != 0 && new_sense != sensitivity_)
// {
// sensitivity_ = new_sense;
// }
// }
// if (tud_xid::receive_report(0, reinterpret_cast<uint8_t*>(&out_report_), sizeof(XboxOG::SB::OutReport)) &&
// out_report_.bLength == sizeof(XboxOG::SB::OutReport))
// {
// uint8_t rumble = out_report_.Chaff_Extinguisher;
// rumble |= out_report_.Chaff_Extinguisher << 4;
// rumble |= out_report_.Comm1_MagazineChange << 4;
// rumble |= out_report_.CockpitHatch_EmergencyEject << 4;
// gamepad.set_rumble_l(rumble);
// gamepad.set_rumble_r(rumble);
// }
}
uint16_t XboxOGSBDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
{
std::memcpy(buffer, &in_report_, sizeof(in_report_));
return sizeof(XboxOG::SB::InReport);
}
void XboxOGSBDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
bool XboxOGSBDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
return tud_xid::class_driver()->control_xfer_cb(rhport, stage, request);
}
const uint16_t* XboxOGSBDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
{
const char *value = reinterpret_cast<const char*>(XboxOG::SB::STRING_DESCRIPTORS[index]);
return get_string_descriptor(value, index);
}
const uint8_t* XboxOGSBDevice::get_descriptor_device_cb()
{
return reinterpret_cast<const uint8_t*>(&XboxOG::GP::DEVICE_DESCRIPTORS);
}
const uint8_t* XboxOGSBDevice::get_hid_descriptor_report_cb(uint8_t itf)
{
return nullptr;
}
const uint8_t* XboxOGSBDevice::get_descriptor_configuration_cb(uint8_t index)
{
return XboxOG::SB::CONFIGURATION_DESCRIPTORS;
}
const uint8_t* XboxOGSBDevice::get_descriptor_device_qualifier_cb()
{
return nullptr;
}

View File

@@ -0,0 +1,68 @@
#ifndef _XBOXGOG_SB_DEVICE_H_
#define _XBOXGOG_SB_DEVICE_H_
#include <cstdint>
#include <numeric>
#include "Gamepad.h"
#include "USBDevice/DeviceDriver/DeviceDriver.h"
#include "Descriptors/XboxOG.h"
class XboxOGSBDevice : public DeviceDriver
{
public:
struct ButtonMap
{
uint16_t gp_mask;
uint16_t sb_mask;
uint8_t button_offset;
};
void initialize() override;
void process(const uint8_t idx, Gamepad& gamepad) override;
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
const uint8_t* get_descriptor_device_cb() override;
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
const uint8_t* get_descriptor_device_qualifier_cb() override;
private:
int32_t vmouse_x_ = XboxOG::SB::AIMING_MID;
int32_t vmouse_y_ = XboxOG::SB::AIMING_MID;
uint16_t sensitivity_ = XboxOG::SB::DEFAULT_SENSE;
uint32_t aim_reset_timer_ = 0;
XboxOG::SB::InReport in_report_;
XboxOG::SB::InReport prev_in_report_;
XboxOG::SB::OutReport out_report_;
static inline bool chatpad_pressed(const Gamepad::Chatpad& chatpad_array, const uint16_t keycode)
{
if (std::accumulate(chatpad_array.data(), chatpad_array.data() + sizeof(Gamepad::Chatpad), 0) == 0)
{
return false;
}
else if (keycode < 17 && (chatpad_array[0] & keycode))
{
return true;
}
else if (keycode < 17)
{
return false;
}
else if (chatpad_array[1] == keycode)
{
return true;
}
else if (chatpad_array[2] == keycode)
{
return true;
}
return false;
}
};
#endif // _XBOXGOG_SB_DEVICE_H_

View File

@@ -0,0 +1,117 @@
#include <cstring>
#include <vector>
#include "pico/stdlib.h"
#include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.h"
#include "USBDevice/DeviceDriver/XboxOG/XboxOG_XR.h"
void XboxOGXRDevice::initialize()
{
tud_xid::initialize(tud_xid::Type::XREMOTE);
class_driver_ = *tud_xid::class_driver();
std::memset(&in_report_, 0, sizeof(XboxOG::XR::InReport));
in_report_.bLength = sizeof(XboxOG::XR::InReport);
}
void XboxOGXRDevice::process(const uint8_t idx, Gamepad& gamepad)
{
if (!tud_xid::xremote_rom_available())
{
return;
}
uint8_t index = tud_xid::get_index_by_type(0, tud_xid::Type::XREMOTE);
uint32_t time_elapsed = to_ms_since_boot(get_absolute_time()) - ms_timer_;
if (tud_suspended())
{
tud_remote_wakeup();
}
if (!tud_xid::send_report_ready(index) || time_elapsed < 64)
{
return;
}
in_report_.buttonCode = 0;
uint16_t gp_buttons = gamepad.get_buttons();
uint8_t gp_dpad = gamepad.get_dpad_buttons();
if (gp_dpad & Gamepad::DPad::UP) in_report_.buttonCode |= XboxOG::XR::ButtonCode::UP ;
if (gp_dpad & Gamepad::DPad::DOWN) in_report_.buttonCode |= XboxOG::XR::ButtonCode::DOWN ;
if (gp_dpad & Gamepad::DPad::LEFT) in_report_.buttonCode |= XboxOG::XR::ButtonCode::LEFT ;
if (gp_dpad & Gamepad::DPad::RIGHT) in_report_.buttonCode |= XboxOG::XR::ButtonCode::RIGHT;
if (gp_buttons & Gamepad::Button::SYS) in_report_.buttonCode |= XboxOG::XR::ButtonCode::DISPLAY;
if (gp_buttons & Gamepad::Button::START) in_report_.buttonCode |= XboxOG::XR::ButtonCode::PLAY ;
if (gp_buttons & Gamepad::Button::BACK) in_report_.buttonCode |= XboxOG::XR::ButtonCode::STOP ;
if (gp_buttons & Gamepad::Button::L3 && !(gp_buttons & Gamepad::Button::R3)) in_report_.buttonCode |= XboxOG::XR::ButtonCode::TITLE;
if (gp_buttons & Gamepad::Button::R3 && !(gp_buttons & Gamepad::Button::L3)) in_report_.buttonCode |= XboxOG::XR::ButtonCode::MENU;
if (gp_buttons & (Gamepad::Button::L3 | Gamepad::Button::R3)) in_report_.buttonCode |= XboxOG::XR::ButtonCode::INFO;
if (gp_buttons & Gamepad::Button::A) in_report_.buttonCode |= XboxOG::XR::ButtonCode::SELECT ;
if (gp_buttons & Gamepad::Button::Y) in_report_.buttonCode |= XboxOG::XR::ButtonCode::PAUSE ;
if (gp_buttons & Gamepad::Button::X) in_report_.buttonCode |= XboxOG::XR::ButtonCode::DISPLAY;
if (gp_buttons & Gamepad::Button::B) in_report_.buttonCode |= XboxOG::XR::ButtonCode::BACK ;
if (gp_buttons & Gamepad::Button::LB && !(gp_buttons & Gamepad::Button::RB)) in_report_.buttonCode |= XboxOG::XR::ButtonCode::SKIP_MINUS;
if (gp_buttons & Gamepad::Button::RB && !(gp_buttons & Gamepad::Button::LB)) in_report_.buttonCode |= XboxOG::XR::ButtonCode::SKIP_PLUS ;
if (gp_buttons & (Gamepad::Button::LB | Gamepad::Button::RB)) in_report_.buttonCode |= XboxOG::XR::ButtonCode::DISPLAY;
if (gamepad.get_trigger_l().uint8() >= 100) in_report_.buttonCode |= XboxOG::XR::ButtonCode::REVERSE;
if (gamepad.get_trigger_r().uint8() >= 100) in_report_.buttonCode |= XboxOG::XR::ButtonCode::FORWARD;
if (in_report_.buttonCode == 0x0000)
{
return;
}
in_report_.timeElapsed = static_cast<uint16_t>(time_elapsed);
if (tud_xid::send_report(index, reinterpret_cast<uint8_t*>(&in_report_), sizeof(XboxOG::XR::InReport)))
{
ms_timer_ = to_ms_since_boot(get_absolute_time());
}
}
uint16_t XboxOGXRDevice::get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
{
std::memcpy(buffer, &in_report_, sizeof(in_report_));
return sizeof(XboxOG::XR::InReport);
}
void XboxOGXRDevice::set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
bool XboxOGXRDevice::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
return tud_xid::class_driver()->control_xfer_cb(rhport, stage, request);
}
const uint16_t* XboxOGXRDevice::get_descriptor_string_cb(uint8_t index, uint16_t langid)
{
const char *value = reinterpret_cast<const char*>(XboxOG::GP::STRING_DESCRIPTORS[index]);
return get_string_descriptor(value, index);
}
const uint8_t* XboxOGXRDevice::get_descriptor_device_cb()
{
return reinterpret_cast<const uint8_t*>(&XboxOG::XR::DEVICE_DESCRIPTORS);
}
const uint8_t* XboxOGXRDevice::get_hid_descriptor_report_cb(uint8_t itf)
{
return nullptr;
}
const uint8_t* XboxOGXRDevice::get_descriptor_configuration_cb(uint8_t index)
{
return XboxOG::XR::CONFIGURATION_DESCRIPTORS;
}
const uint8_t* XboxOGXRDevice::get_descriptor_device_qualifier_cb()
{
return nullptr;
}

View File

@@ -0,0 +1,28 @@
#ifndef _XBOXGOG_XREMOTE_DEVICE_H_
#define _XBOXGOG_XREMOTE_DEVICE_H_
#include <cstdint>
#include "USBDevice/DeviceDriver/DeviceDriver.h"
#include "Descriptors/XboxOG.h"
class XboxOGXRDevice : public DeviceDriver
{
public:
void initialize() override;
void process(const uint8_t idx, Gamepad& gamepad) override;
uint16_t get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) override;
void set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) override;
bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) override;
const uint16_t* get_descriptor_string_cb(uint8_t index, uint16_t langid) override;
const uint8_t* get_descriptor_device_cb() override;
const uint8_t* get_hid_descriptor_report_cb(uint8_t itf) override;
const uint8_t* get_descriptor_configuration_cb(uint8_t index) override;
const uint8_t* get_descriptor_device_qualifier_cb() override;
private:
XboxOG::XR::InReport in_report_;
uint32_t ms_timer_;
};
#endif // _XBOXGOG_XREMOTE_DEVICE_H_

View File

@@ -0,0 +1,970 @@
// #include "tusb_option.h"
// #if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XID)
// #include <cstring>
// #include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.h"
// #include "Descriptors/XboxOG.h"
// #ifndef CFG_TUD_XID_XREMOTE_ENABLE
// #define CFG_TUD_XID_XREMOTE_ENABLE 0
// #endif
// #ifdef XREMOTE_ROM_AVAILABLE
// #define XREMOTE_ENABLED (CFG_TUD_XID_XREMOTE_ENABLE ? 1 : 0)
// #include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid_xremote_rom.h"
// #else
// #define XREMOTE_ENABLED 0
// #endif
// namespace tud_xid {
// static constexpr uint8_t ENDPOINT_SIZE = 32;
// static constexpr uint8_t INTERFACE_MULT = XREMOTE_ENABLED + 1;
// static constexpr uint8_t INTERFACE_CLASS = 0x58;
// static constexpr uint8_t INTERFACE_SUBCLASS = 0x42;
// enum class RequestType { SET_REPORT, GET_REPORT, GET_DESC, GET_CAPABILITIES_IN, GET_CAPABILITIES_OUT, UNKNOWN };
// struct Interface
// {
// Type type{Type::GAMEPAD};
// uint8_t itf_num{0xFF};
// uint8_t ep_in{0xFF};
// uint8_t ep_out{0xFF};
// uint8_t ep_in_size{0xFF};
// uint8_t ep_out_size{0xFF};
// std::array<uint8_t, ENDPOINT_SIZE> ep_out_buffer;
// std::array<uint8_t, ENDPOINT_SIZE> gp_in_buffer;
// std::array<uint8_t, ENDPOINT_SIZE> gp_out_buffer;
// Interface()
// {
// ep_out_buffer.fill(0);
// gp_in_buffer.fill(0);
// gp_out_buffer.fill(0);
// }
// };
// static std::array<Interface, CFG_TUD_XID * INTERFACE_MULT> interfaces_;
// static Type xid_type_{Type::GAMEPAD};
// static inline RequestType get_request_type(const tusb_control_request_t *request)
// {
// if (request->bmRequestType == XboxOG::GET_REPORT_REQ_TYPE && request->bRequest == XboxOG::GET_REPORT_REQ && request->wValue == XboxOG::GET_REPORT_VALUE)
// {
// return RequestType::GET_REPORT;
// }
// if (request->bmRequestType == XboxOG::SET_REPORT_REQ_TYPE && request->bRequest == XboxOG::SET_REPORT_REQ && request->wValue == XboxOG::SET_REPORT_VALUE && request->wLength == static_cast<uint16_t>(0x06))
// {
// return RequestType::SET_REPORT;
// }
// if (request->bmRequestType == XboxOG::GET_DESC_REQ_TYPE && request->bRequest == XboxOG::GET_DESC_REQ && request->wValue == XboxOG::GET_DESC_VALUE)
// {
// return RequestType::GET_DESC;
// }
// if (request->bmRequestType == XboxOG::GET_CAP_REQ_TYPE && request->bRequest == XboxOG::GET_CAP_REQ)
// {
// if (request->wValue == XboxOG::GET_CAP_VALUE_IN)
// {
// return RequestType::GET_CAPABILITIES_IN;
// }
// else if (request->wValue == XboxOG::GET_CAP_VALUE_OUT)
// {
// return RequestType::GET_CAPABILITIES_OUT;
// }
// }
// return RequestType::UNKNOWN;
// }
// static inline uint8_t get_idx_by_itf(uint8_t itf)
// {
// for (uint8_t i = 0; i < interfaces_.size(); i++)
// {
// if (interfaces_[i].itf_num == itf)
// {
// return i;
// }
// if (interfaces_[i].type == Type::XREMOTE)
// {
// if (itf == interfaces_[i].itf_num + 1)
// {
// return i;
// }
// }
// }
// return 0xFF;
// }
// static inline uint8_t get_idx_by_edpt(uint8_t edpt)
// {
// for (uint8_t i = 0; i < interfaces_.size(); i++)
// {
// if (interfaces_[i].ep_in == edpt || interfaces_[i].ep_out == edpt)
// {
// return i;
// }
// }
// return 0xFF;
// }
// uint8_t get_index_by_type(uint8_t type_idx, Type type)
// {
// uint8_t type_idx_ = 0;
// for (uint8_t i = 0; i < interfaces_.size(); i++)
// {
// if (interfaces_[i].type == type)
// {
// if (type_idx_ == type_idx)
// {
// return i;
// }
// type_idx_++;
// }
// }
// return 0xFF;
// }
// static inline Interface* find_available_interface()
// {
// for (Interface &itf : interfaces_)
// {
// if (itf.itf_num == 0xFF)
// {
// return &itf;
// }
// }
// return nullptr;
// }
// const uint8_t *xremote_get_rom()
// {
// #if XREMOTE_ENABLED
// return XRemote::ROM;
// #else
// return nullptr;
// #endif
// }
// //Class driver
// static void xid_init()
// {
// for (Interface &itf : interfaces_)
// {
// itf = Interface();
// itf.type = xid_type_;
// }
// }
// static void xid_reset(uint8_t rhport)
// {
// xid_init();
// }
// static uint16_t xid_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
// {
// TU_VERIFY(itf_desc->bInterfaceClass == INTERFACE_CLASS, 0);
// TU_VERIFY(itf_desc->bInterfaceSubClass == INTERFACE_SUBCLASS, 0);
// Interface *interface = find_available_interface();
// TU_ASSERT(interface != nullptr, 0);
// uint16_t driver_len = 0;
// switch (interface->type)
// {
// case Type::GAMEPAD:
// driver_len = XboxOG::GP::DRIVER_LEN;
// break;
// case Type::STEELBATTALION:
// driver_len = XboxOG::SB::DRIVER_LEN;
// break;
// case Type::XREMOTE:
// driver_len = XboxOG::XR::DRIVER_LEN;
// break;
// }
// TU_ASSERT(max_len >= driver_len, 0);
// interface->itf_num = itf_desc->bInterfaceNumber;
// tusb_desc_endpoint_t *ep_desc;
// ep_desc = (tusb_desc_endpoint_t *)tu_desc_next(itf_desc);
// if (tu_desc_type(ep_desc) == TUSB_DESC_ENDPOINT)
// {
// usbd_edpt_open(rhport, ep_desc);
// (ep_desc->bEndpointAddress & 0x80) ? (interface->ep_in = ep_desc->bEndpointAddress) :
// (interface->ep_out = ep_desc->bEndpointAddress);
// }
// TU_VERIFY(itf_desc->bNumEndpoints >= 2, driver_len);
// ep_desc = (tusb_desc_endpoint_t *)tu_desc_next(ep_desc);
// if (tu_desc_type(ep_desc) == TUSB_DESC_ENDPOINT)
// {
// usbd_edpt_open(rhport, ep_desc);
// (ep_desc->bEndpointAddress & 0x80) ? (interface->ep_in = ep_desc->bEndpointAddress) :
// (interface->ep_out = ep_desc->bEndpointAddress);
// }
// return driver_len;
// }
// static bool xid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
// {
// uint8_t index = get_idx_by_edpt(ep_addr);
// TU_VERIFY(result == XFER_RESULT_SUCCESS, true);
// TU_VERIFY(index != 0xFF, true);
// TU_VERIFY(xferred_bytes < ENDPOINT_SIZE, true);
// if (ep_addr == interfaces_[index].ep_out)
// {
// std::memcpy(interfaces_[index].gp_out_buffer.data(), interfaces_[index].ep_out_buffer.data(), std::min(static_cast<uint8_t>(xferred_bytes), ENDPOINT_SIZE));
// }
// return true;
// }
// bool xremote_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, Interface *interface)
// {
// if (request->bmRequestType == 0xC1 && request->bRequest == 0x01 && request->wIndex == 1 && request->wValue == 0x0000)
// {
// if (stage == CONTROL_STAGE_SETUP)
// {
// TU_LOG1("Sending XREMOTE INFO\r\n");
// const uint8_t *rom = xremote_get_rom();
// if (rom == nullptr)
// {
// return false; //STALL
// }
// tud_control_xfer(rhport, request, const_cast<uint8_t*>(&rom[0]), request->wLength);
// }
// return true;
// }
// //ROM DATA (Interface 1)
// else if (request->bmRequestType == 0xC1 && request->bRequest == 0x02 && request->wIndex == 1)
// {
// if (stage == CONTROL_STAGE_SETUP)
// {
// const uint8_t *rom = xremote_get_rom();
// if (rom == nullptr)
// {
// return false; //STALL
// }
// tud_control_xfer(rhport, request, const_cast<uint8_t*>(&rom[request->wValue * 1024]), request->wLength);
// }
// return true;
// }
// return false;
// }
// bool xid_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
// {
// TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
// uint8_t index = get_idx_by_itf(static_cast<uint8_t>(request->wIndex));
// TU_VERIFY(index != 0xFF, false);
// void* desc_buffer;
// uint16_t desc_buffer_len;
// switch (get_request_type(request))
// {
// case RequestType::GET_REPORT:
// if (stage == CONTROL_STAGE_SETUP)
// {
// tud_control_xfer(rhport, request, interfaces_[index].gp_in_buffer.data(), std::min(static_cast<uint8_t>(request->wLength), ENDPOINT_SIZE));
// }
// return true;
// case RequestType::SET_REPORT:
// if (stage == CONTROL_STAGE_SETUP) //Getting rumble report
// {
// tud_control_xfer(rhport, request, interfaces_[index].ep_out_buffer.data(), std::min(static_cast<uint8_t>(request->wLength), ENDPOINT_SIZE));
// }
// else if (stage == CONTROL_STAGE_ACK) //Got rumble report
// {
// std::memcpy(interfaces_[index].gp_out_buffer.data(), interfaces_[index].ep_out_buffer.data(), std::min(static_cast<uint8_t>(request->wLength), ENDPOINT_SIZE));
// }
// return true;
// case RequestType::GET_DESC:
// if (stage == CONTROL_STAGE_SETUP)
// {
// switch (interfaces_[index].type)
// {
// case Type::GAMEPAD:
// desc_buffer = (void*)XboxOG::GP::XID_DEVICE_DESCRIPTORS;
// desc_buffer_len = sizeof(XboxOG::GP::XID_DEVICE_DESCRIPTORS);
// break;
// case Type::STEELBATTALION:
// desc_buffer = (void*)XboxOG::SB::XID_DEVICE_DESCRIPTORS;
// desc_buffer_len = sizeof(XboxOG::SB::XID_DEVICE_DESCRIPTORS);
// break;
// case Type::XREMOTE:
// desc_buffer = (void*)XboxOG::XR::XID_DEVICE_DESCRIPTORS;
// desc_buffer_len = sizeof(XboxOG::XR::XID_DEVICE_DESCRIPTORS);
// break;
// default:
// return false;
// }
// tud_control_xfer(rhport, request, desc_buffer, desc_buffer_len);
// }
// return true;
// case RequestType::GET_CAPABILITIES_IN:
// if (stage == CONTROL_STAGE_SETUP)
// {
// switch (interfaces_[index].type)
// {
// case Type::GAMEPAD:
// desc_buffer = (void*)XboxOG::GP::XID_CAPABILITIES_IN;
// desc_buffer_len = sizeof(XboxOG::GP::XID_CAPABILITIES_IN);
// break;
// case Type::STEELBATTALION:
// desc_buffer = (void*)XboxOG::SB::XID_CAPABILITIES_IN;
// desc_buffer_len = sizeof(XboxOG::SB::XID_CAPABILITIES_IN);
// break;
// default:
// return false;
// }
// tud_control_xfer(rhport, request, desc_buffer, desc_buffer_len);
// }
// return true;
// case RequestType::GET_CAPABILITIES_OUT:
// if (stage == CONTROL_STAGE_SETUP)
// {
// switch (interfaces_[index].type)
// {
// case Type::GAMEPAD:
// desc_buffer = (void*)XboxOG::GP::XID_CAPABILITIES_OUT;
// desc_buffer_len = sizeof(XboxOG::GP::XID_CAPABILITIES_OUT);
// break;
// case Type::STEELBATTALION:
// desc_buffer = (void*)XboxOG::SB::XID_CAPABILITIES_OUT;
// desc_buffer_len = sizeof(XboxOG::SB::XID_CAPABILITIES_OUT);
// break;
// default:
// return false;
// }
// tud_control_xfer(rhport, request, desc_buffer, desc_buffer_len);
// }
// return true;
// default:
// if (interfaces_[index].type != Type::XREMOTE)
// {
// return false;
// }
// return xremote_control_xfer(rhport, stage, request, &interfaces_[index]);
// }
// return false;
// }
// static const usbd_class_driver_t tud_xid_class_driver_ =
// {
// #if CFG_TUSB_DEBUG >= 2
// .name = "XID DRIVER (DUKE,SB OR XREMOTE)",
// #endif
// .init = xid_init,
// .reset = xid_reset,
// .open = xid_open,
// .control_xfer_cb = xid_control_xfer_cb,
// .xfer_cb = xid_xfer_cb,
// .sof = NULL
// };
// //Public API
// //Call first before device stack is initialized with tud_init(), will default to Duke/Gamepad otherwise
// void initialize(Type xid_type)
// {
// xid_type_ = xid_type;
// }
// const usbd_class_driver_t* class_driver()
// {
// return &tud_xid_class_driver_;
// }
// bool send_report_ready(uint8_t index)
// {
// TU_VERIFY(index < interfaces_.size(), false);
// TU_VERIFY(interfaces_[index].ep_in != 0xFF, false);
// return (tud_ready() && !usbd_edpt_busy(BOARD_TUD_RHPORT, interfaces_[index].ep_in));
// }
// bool send_report(uint8_t index, const uint8_t* report, uint16_t len)
// {
// TU_VERIFY(len < ENDPOINT_SIZE, false);
// TU_VERIFY(send_report_ready(index), false);
// if (tud_suspended())
// {
// tud_remote_wakeup();
// }
// std::memcpy(interfaces_[index].gp_in_buffer.data(), report, len);
// return usbd_edpt_xfer(BOARD_TUD_RHPORT, interfaces_[index].ep_in, interfaces_[index].gp_in_buffer.data(), len);
// }
// bool receive_report(uint8_t index, uint8_t *report, uint16_t len)
// {
// TU_VERIFY(index < interfaces_.size(), false);
// TU_VERIFY(interfaces_[index].ep_out != 0xFF, false);
// TU_VERIFY(len < ENDPOINT_SIZE, false);
// std::memcpy(report, interfaces_[index].gp_out_buffer.data(), len);
// if (tud_ready() && !usbd_edpt_busy(BOARD_TUD_RHPORT, interfaces_[index].ep_out))
// {
// usbd_edpt_xfer(BOARD_TUD_RHPORT, interfaces_[index].ep_out, interfaces_[index].ep_out_buffer.data(), len);
// }
// return true;
// }
// bool xremote_rom_available()
// {
// return (xremote_get_rom() != nullptr);
// }
// } // namespace TUDXID
// #endif // TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XID
#include "tusb_option.h"
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XID)
#include <cstring>
#include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid.h"
#include "Descriptors/XboxOG.h"
#ifndef CFG_TUD_XID_XREMOTE_ENABLE
#define CFG_TUD_XID_XREMOTE_ENABLE 0
#endif
#ifdef XREMOTE_ROM_AVAILABLE
#define XREMOTE_ENABLED (CFG_TUD_XID_XREMOTE_ENABLE ? 1 : 0)
#include "USBDevice/DeviceDriver/XboxOG/tud_xid/tud_xid_xremote_rom.h"
#else
#define XREMOTE_ENABLED 0
#endif
namespace tud_xid {
static constexpr uint8_t ENDPOINT_SIZE = 32;
static constexpr uint8_t INTERFACE_MULT = XREMOTE_ENABLED + 1;
static constexpr uint8_t INTERFACE_CLASS = 0x58;
static constexpr uint8_t INTERFACE_SUBCLASS = 0x42;
enum class RequestType { SET_REPORT, GET_REPORT, GET_DESC, GET_CAPABILITIES_IN, GET_CAPABILITIES_OUT, UNKNOWN };
struct Interface
{
Type type{Type::GAMEPAD};
uint8_t itf_num{0xFF};
uint8_t ep_in{0xFF};
uint8_t ep_out{0xFF};
uint16_t ep_in_size{ENDPOINT_SIZE};
uint16_t ep_out_size{ENDPOINT_SIZE};
std::array<uint8_t, ENDPOINT_SIZE> ep_in_buffer;
std::array<uint8_t, ENDPOINT_SIZE> ep_out_buffer;
Interface()
{
ep_out_buffer.fill(0);
ep_in_buffer.fill(0);
}
};
static std::array<Interface, CFG_TUD_XID * INTERFACE_MULT> interfaces_;
static Type xid_type_{Type::GAMEPAD};
static inline RequestType get_request_type(const tusb_control_request_t *request)
{
if (request->bmRequestType == XboxOG::GET_REPORT_REQ_TYPE && request->bRequest == XboxOG::GET_REPORT_REQ && request->wValue == XboxOG::GET_REPORT_VALUE)
{
return RequestType::GET_REPORT;
}
if (request->bmRequestType == XboxOG::SET_REPORT_REQ_TYPE && request->bRequest == XboxOG::SET_REPORT_REQ && request->wValue == XboxOG::SET_REPORT_VALUE && request->wLength == static_cast<uint16_t>(0x06))
{
return RequestType::SET_REPORT;
}
if (request->bmRequestType == XboxOG::GET_DESC_REQ_TYPE && request->bRequest == XboxOG::GET_DESC_REQ && request->wValue == XboxOG::GET_DESC_VALUE)
{
return RequestType::GET_DESC;
}
if (request->bmRequestType == XboxOG::GET_CAP_REQ_TYPE && request->bRequest == XboxOG::GET_CAP_REQ)
{
if (request->wValue == XboxOG::GET_CAP_VALUE_IN)
{
return RequestType::GET_CAPABILITIES_IN;
}
else if (request->wValue == XboxOG::GET_CAP_VALUE_OUT)
{
return RequestType::GET_CAPABILITIES_OUT;
}
}
return RequestType::UNKNOWN;
}
static inline uint8_t get_idx_by_itf(uint8_t itf)
{
for (uint8_t i = 0; i < interfaces_.size(); i++)
{
if (interfaces_[i].itf_num == itf)
{
return i;
}
if (interfaces_[i].type == Type::XREMOTE)
{
if (itf == interfaces_[i].itf_num + 1)
{
return i;
}
}
}
return 0xFF;
}
static inline uint8_t get_idx_by_edpt(uint8_t edpt)
{
for (uint8_t i = 0; i < interfaces_.size(); i++)
{
if (interfaces_[i].ep_in == edpt || interfaces_[i].ep_out == edpt)
{
return i;
}
}
return 0xFF;
}
uint8_t get_index_by_type(uint8_t type_idx, Type type)
{
uint8_t type_idx_ = 0;
for (uint8_t i = 0; i < interfaces_.size(); i++)
{
if (interfaces_[i].type == type)
{
if (type_idx_ == type_idx)
{
return i;
}
type_idx_++;
}
}
return 0xFF;
}
static inline Interface* find_available_interface()
{
for (Interface &itf : interfaces_)
{
if (itf.itf_num == 0xFF)
{
return &itf;
}
}
return nullptr;
}
const uint8_t *xremote_get_rom()
{
#if XREMOTE_ENABLED
return XRemote::ROM;
#else
return nullptr;
#endif
}
//Class driver
static void xid_init()
{
for (Interface &itf : interfaces_)
{
itf = Interface();
itf.type = xid_type_;
}
}
static void xid_reset(uint8_t rhport)
{
xid_init();
}
static uint16_t xid_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
{
TU_VERIFY(itf_desc->bInterfaceClass == INTERFACE_CLASS, 0);
TU_VERIFY(itf_desc->bInterfaceSubClass == INTERFACE_SUBCLASS, 0);
Interface *interface = find_available_interface();
TU_ASSERT(interface != nullptr, 0);
uint16_t driver_len = 0;
switch (interface->type)
{
case Type::GAMEPAD:
driver_len = XboxOG::GP::DRIVER_LEN;
break;
case Type::STEELBATTALION:
driver_len = XboxOG::SB::DRIVER_LEN;
break;
case Type::XREMOTE:
driver_len = XboxOG::XR::DRIVER_LEN;
break;
}
TU_ASSERT(max_len >= driver_len, 0);
const uint8_t *desc_ep = reinterpret_cast<const uint8_t*>(itf_desc);
int endpoint = 0;
int pos = 0;
while (endpoint < itf_desc->bNumEndpoints && pos < max_len)
{
if (tu_desc_type(desc_ep) != TUSB_DESC_ENDPOINT)
{
pos += tu_desc_len(desc_ep);
desc_ep = tu_desc_next(desc_ep);
continue;
}
const tusb_desc_endpoint_t *ep_desc = reinterpret_cast<const tusb_desc_endpoint_t*>(desc_ep);
TU_VERIFY(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType, 0);
TU_VERIFY(usbd_edpt_open(rhport, ep_desc), 0);
if (tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN)
{
interface->ep_in = ep_desc->bEndpointAddress;
interface->ep_in_size = ep_desc->wMaxPacketSize;
}
else
{
interface->ep_out = ep_desc->bEndpointAddress;
interface->ep_out_size = ep_desc->wMaxPacketSize;
}
interface->itf_num = itf_desc->bInterfaceNumber;
pos += tu_desc_len(desc_ep);
desc_ep = tu_desc_next(desc_ep);
endpoint++;
}
// interface->itf_num = itf_desc->bInterfaceNumber;
// tusb_desc_endpoint_t *ep_desc;
// ep_desc = (tusb_desc_endpoint_t *)tu_desc_next(itf_desc);
// if (tu_desc_type(ep_desc) == TUSB_DESC_ENDPOINT)
// {
// TU_VERIFY(usbd_edpt_open(rhport, ep_desc), 0);
// if (tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN)
// {
// interface->ep_in = ep_desc->bEndpointAddress;
// interface->ep_in_size = ep_desc->wMaxPacketSize;
// }
// else
// {
// interface->ep_out = ep_desc->bEndpointAddress;
// interface->ep_out_size = ep_desc->wMaxPacketSize;
// }
// }
// TU_VERIFY(itf_desc->bNumEndpoints >= 2, driver_len);
// ep_desc = (tusb_desc_endpoint_t *)tu_desc_next(ep_desc);
// if (tu_desc_type(ep_desc) == TUSB_DESC_ENDPOINT)
// {
// TU_VERIFY(usbd_edpt_open(rhport, ep_desc), driver_len);
// if (tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN)
// {
// interface->ep_in = ep_desc->bEndpointAddress;
// interface->ep_in_size = ep_desc->wMaxPacketSize;
// }
// else
// {
// interface->ep_out = ep_desc->bEndpointAddress;
// interface->ep_out_size = ep_desc->wMaxPacketSize;
// }
// }
return driver_len;
}
static bool xid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
// uint8_t index = get_idx_by_edpt(ep_addr);
// TU_VERIFY(result == XFER_RESULT_SUCCESS, true);
// TU_VERIFY(index != 0xFF, true);
// TU_VERIFY(xferred_bytes < ENDPOINT_SIZE, true);
return true;
}
bool xremote_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, Interface *interface)
{
if (request->bmRequestType == 0xC1 && request->bRequest == 0x01 && request->wIndex == 1 && request->wValue == 0x0000)
{
if (stage == CONTROL_STAGE_SETUP)
{
TU_LOG1("Sending XREMOTE INFO\r\n");
const uint8_t *rom = xremote_get_rom();
if (rom == nullptr)
{
return false; //STALL
}
tud_control_xfer(rhport, request, const_cast<uint8_t*>(&rom[0]), request->wLength);
}
return true;
}
//ROM DATA (Interface 1)
else if (request->bmRequestType == 0xC1 && request->bRequest == 0x02 && request->wIndex == 1)
{
if (stage == CONTROL_STAGE_SETUP)
{
const uint8_t *rom = xremote_get_rom();
if (rom == nullptr)
{
return false; //STALL
}
tud_control_xfer(rhport, request, const_cast<uint8_t*>(&rom[request->wValue * 1024]), request->wLength);
}
return true;
}
return false;
}
bool steelbattalion_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, Interface *p_xid);
bool duke_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, Interface *p_xid);
bool xid_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
uint8_t index = get_idx_by_itf(static_cast<uint8_t>(request->wIndex));
TU_VERIFY(index != 0xFF, false);
Interface& interface = interfaces_[index];
bool ret = false;
//Get HID Report
if (request->bmRequestType == 0xA1 && request->bRequest == 0x01 && request->wValue == 0x0100)
{
if (stage == CONTROL_STAGE_SETUP)
{
TU_LOG1("Sending HID report on control pipe for index %02x\n", request->wIndex);
tud_control_xfer(rhport, request, interface.ep_in_buffer.data(), std::min(request->wLength, interface.ep_in_size));
}
return true;
}
//Set HID Report
if (request->bmRequestType == 0x21 && request->bRequest == 0x09 && request->wValue == 0x0200 && request->wLength == 0x06)
{
if (stage == CONTROL_STAGE_SETUP)
{
//Host is sending a rumble command to control pipe. Queue receipt.
tud_control_xfer(rhport, request, interface.ep_out_buffer.data(), std::min(request->wLength, interface.ep_out_size));
}
else if (stage == CONTROL_STAGE_ACK)
{
//Receipt complete. Copy data to rumble struct
TU_LOG1("Got HID report from control pipe for index %02x\n", request->wIndex);
// memcpy(interface.out, interface.ep_out_buff, min(request->wLength, sizeof(interface.out)));
}
return true;
}
switch (interface.type)
{
case Type::GAMEPAD:
ret = duke_control_xfer(rhport, stage, request, &interface);
break;
case Type::STEELBATTALION:
ret = steelbattalion_control_xfer(rhport, stage, request, &interface);
break;
case Type::XREMOTE:
ret = xremote_control_xfer(rhport, stage, request, &interface);
break;
default:
break;
}
if (ret == false)
{
TU_LOG1("STALL: wIndex: %02x bmRequestType: %02x, bRequest: %02x, wValue: %04x\n",
request->wIndex,
request->bmRequestType,
request->bRequest,
request->wValue);
return false;
}
return true;
}
static const usbd_class_driver_t tud_xid_class_driver_ =
{
#if CFG_TUSB_DEBUG >= 2
.name = "XID DRIVER (DUKE,SB OR XREMOTE)",
#endif
.init = xid_init,
.reset = xid_reset,
.open = xid_open,
.control_xfer_cb = xid_control_xfer_cb,
.xfer_cb = xid_xfer_cb,
.sof = NULL
};
//Public API
//Call first before device stack is initialized with tud_init(), will default to Duke/Gamepad otherwise
void initialize(Type xid_type)
{
xid_type_ = xid_type;
}
const usbd_class_driver_t* class_driver()
{
return &tud_xid_class_driver_;
}
bool send_report_ready(uint8_t index)
{
TU_VERIFY(index < interfaces_.size(), false);
TU_VERIFY(interfaces_[index].ep_in != 0xFF, false);
return (tud_ready() && !usbd_edpt_busy(BOARD_TUD_RHPORT, interfaces_[index].ep_in));
}
bool send_report(uint8_t index, const uint8_t* report, uint16_t len)
{
TU_VERIFY(len < ENDPOINT_SIZE, false);
TU_VERIFY(send_report_ready(index), false);
if (tud_suspended())
{
tud_remote_wakeup();
}
uint16_t size = std::min(len, static_cast<uint16_t>(interfaces_[index].ep_in_size));
std::memcpy(interfaces_[index].ep_in_buffer.data(), report, size);
return usbd_edpt_xfer(BOARD_TUD_RHPORT, interfaces_[index].ep_in, interfaces_[index].ep_in_buffer.data(), size);
}
bool receive_report(uint8_t index, uint8_t *report, uint16_t len)
{
TU_VERIFY(index < interfaces_.size());
TU_VERIFY(interfaces_[index].ep_out != 0xFF);
TU_VERIFY(len < ENDPOINT_SIZE);
uint16_t size = std::min(len, static_cast<uint16_t>(interfaces_[index].ep_out_size));
if (tud_ready() && !usbd_edpt_busy(BOARD_TUD_RHPORT, interfaces_[index].ep_out))
{
usbd_edpt_xfer(BOARD_TUD_RHPORT, interfaces_[index].ep_out, interfaces_[index].ep_out_buffer.data(), size);
}
std::memcpy(report, interfaces_[index].ep_out_buffer.data(), size);
return true;
}
bool xremote_rom_available()
{
return (xremote_get_rom() != nullptr);
}
bool steelbattalion_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, Interface *p_xid)
{
if (request->bmRequestType == 0xC1 && request->bRequest == 0x06 && request->wValue == 0x4200)
{
if (stage == CONTROL_STAGE_SETUP)
{
TU_LOG1("Sending STEELBATTALION_DESC_XID\n");
tud_control_xfer(rhport, request, (void *)XboxOG::SB::XID_DEVICE_DESCRIPTORS, sizeof(XboxOG::SB::XID_DEVICE_DESCRIPTORS));
}
return true;
}
else if (request->bmRequestType == 0xC1 && request->bRequest == 0x01 && request->wValue == 0x0100)
{
if (stage == CONTROL_STAGE_SETUP)
{
TU_LOG1("Sending STEELBATTALION_CAPABILITIES_IN\n");
tud_control_xfer(rhport, request, (void *)XboxOG::SB::XID_CAPABILITIES_IN, sizeof(XboxOG::SB::XID_CAPABILITIES_IN));
}
return true;
}
else if (request->bmRequestType == 0xC1 && request->bRequest == 0x01 && request->wValue == 0x0200)
{
if (stage == CONTROL_STAGE_SETUP)
{
TU_LOG1("Sending STEELBATTALION_CAPABILITIES_OUT\n");
tud_control_xfer(rhport, request, (void *)XboxOG::SB::XID_CAPABILITIES_OUT, sizeof(XboxOG::SB::XID_CAPABILITIES_OUT));
}
return true;
}
return false;
}
bool duke_control_xfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, Interface *p_xid)
{
if (request->bmRequestType == 0xC1 && request->bRequest == 0x06 && request->wValue == 0x4200)
{
if (stage == CONTROL_STAGE_SETUP)
{
TU_LOG1("Sending DUKE_DESC_XID\n");
tud_control_xfer(rhport, request, (void *)XboxOG::GP::XID_DEVICE_DESCRIPTORS, sizeof(XboxOG::GP::XID_DEVICE_DESCRIPTORS));
}
return true;
}
else if (request->bmRequestType == 0xC1 && request->bRequest == 0x01 && request->wValue == 0x0100)
{
if (stage == CONTROL_STAGE_SETUP)
{
TU_LOG1("Sending DUKE_CAPABILITIES_IN\n");
tud_control_xfer(rhport, request, (void *)XboxOG::GP::XID_CAPABILITIES_IN, sizeof(XboxOG::GP::XID_CAPABILITIES_IN));
}
return true;
}
else if (request->bmRequestType == 0xC1 && request->bRequest == 0x01 && request->wValue == 0x0200)
{
if (stage == CONTROL_STAGE_SETUP)
{
TU_LOG1("Sending DUKE_CAPABILITIES_OUT\n");
tud_control_xfer(rhport, request, (void *)XboxOG::SB::XID_CAPABILITIES_OUT, sizeof(XboxOG::SB::XID_CAPABILITIES_OUT));
}
return true;
}
return false;
}
} // namespace TUDXID
#endif // TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XID

View File

@@ -0,0 +1,55 @@
/*
TinyUSB XID Device driver based on https://github.com/Ryzee119/ogx360_t4
MIT License
Copyright (c) 2020 Ryan Wendland
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef _TUD_XID_H_
#define _TUD_XID_H_
#include <cstdint>
#include <array>
#include "tusb.h"
#include "device/usbd.h"
#include "device/usbd_pvt.h"
namespace tud_xid
{
enum class Type { GAMEPAD, STEELBATTALION, XREMOTE };
void initialize(tud_xid::Type xid_type);
const usbd_class_driver_t* class_driver();
uint8_t get_index_by_type(uint8_t type_index, tud_xid::Type xid_type);
bool receive_report(uint8_t idx, uint8_t* buffer, uint16_t len);
bool send_report(uint8_t idx, const uint8_t* buffer, uint16_t len);
bool send_report_ready(uint8_t idx);
bool xremote_rom_available();
} // namespace TUDXID
#endif // _TUD_XID_H_

View File

@@ -0,0 +1,60 @@
#include "tusb.h"
#include "board_config.h"
#include "USBDevice/DeviceDriver/PSClassic/PSClassic.h"
#include "USBDevice/DeviceDriver/XInput/XInput.h"
#include "USBDevice/DeviceDriver/Switch/Switch.h"
#include "USBDevice/DeviceDriver/DInput/DInput.h"
#include "USBDevice/DeviceDriver/PS3/PS3.h"
#include "USBDevice/DeviceDriver/XboxOG/XboxOG_GP.h"
#include "USBDevice/DeviceDriver/XboxOG/XboxOG_SB.h"
#include "USBDevice/DeviceDriver/XboxOG/XboxOG_XR.h"
#include "USBDevice/DeviceDriver/WebApp/WebApp.h"
#include "USBDevice/DeviceManager.h"
#if defined(CONFIG_EN_UART_BRIDGE)
#include "USBDevice/DeviceDriver/UARTBridge/UARTBridge.h"
#endif // defined(CONFIG_EN_UART_BRIDGE)
void DeviceManager::initialize_driver(DeviceDriver::Type driver_type)
{
switch (driver_type)
{
case DeviceDriver::Type::DINPUT:
device_driver_ = new DInputDevice();
break;
case DeviceDriver::Type::PS3:
device_driver_ = new PS3Device();
break;
case DeviceDriver::Type::PSCLASSIC:
device_driver_ = new PSClassicDevice();
break;
case DeviceDriver::Type::SWITCH:
device_driver_ = new SwitchDevice();
break;
case DeviceDriver::Type::XINPUT:
device_driver_ = new XInputDevice();
break;
case DeviceDriver::Type::XBOXOG:
device_driver_ = new XboxOGDevice();
break;
case DeviceDriver::Type::XBOXOG_SB:
device_driver_ = new XboxOGSBDevice();
break;
case DeviceDriver::Type::XBOXOG_XR:
device_driver_ = new XboxOGXRDevice();
break;
case DeviceDriver::Type::WEBAPP:
device_driver_ = new WebAppDevice();
break;
#if defined(CONFIG_EN_UART_BRIDGE)
case DeviceDriver::Type::UART_BRIDGE:
device_driver_ = new UARTBridgeDevice();
break;
#endif //defined(CONFIG_EN_UART_BRIDGE)
default:
return;
}
device_driver_->initialize();
}

View File

@@ -0,0 +1,31 @@
#ifndef _DEVICE_MANAGER_H_
#define _DEVICE_MANAGER_H_
#include <memory>
#include "USBDevice/DeviceDriver/DeviceDriver.h"
class DeviceManager
{
public:
DeviceManager(DeviceManager const&) = delete;
void operator=(DeviceManager const&) = delete;
static DeviceManager& get_instance()
{
static DeviceManager instance;
return instance;
}
//Must be called before any other method
void initialize_driver(DeviceDriver::Type driver_type);
DeviceDriver* get_driver() { return device_driver_; }
private:
DeviceManager() {}
~DeviceManager() { delete device_driver_; }
DeviceDriver* device_driver_{nullptr};
};
#endif // _DEVICE_MANAGER_H_

View File

@@ -0,0 +1,54 @@
#include <cstdint>
#include "tusb.h"
#include "class/hid/hid_device.h"
#include "device/usbd_pvt.h"
#include "USBDevice/DeviceManager.h"
const usbd_class_driver_t *usbd_app_driver_get_cb(uint8_t *driver_count)
{
*driver_count = 1;
return DeviceManager::get_instance().get_driver()->get_class_driver();
}
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
{
return DeviceManager::get_instance().get_driver()->get_report_cb(itf, report_id, report_type, buffer, reqlen);
}
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
{
DeviceManager::get_instance().get_driver()->set_report_cb(itf, report_id, report_type, buffer, bufsize);
tud_hid_report(report_id, buffer, bufsize);
}
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
return DeviceManager::get_instance().get_driver()->vendor_control_xfer_cb(rhport, stage, request);
}
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
return DeviceManager::get_instance().get_driver()->get_descriptor_string_cb(index, langid);
}
uint8_t const *tud_descriptor_device_cb()
{
return DeviceManager::get_instance().get_driver()->get_descriptor_device_cb();
}
uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf)
{
return DeviceManager::get_instance().get_driver()->get_hid_descriptor_report_cb(itf);
}
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
{
return DeviceManager::get_instance().get_driver()->get_descriptor_configuration_cb(index);
}
uint8_t const* tud_descriptor_device_qualifier_cb()
{
return DeviceManager::get_instance().get_driver()->get_descriptor_device_qualifier_cb();
}

View File

@@ -0,0 +1,170 @@
#include "USBHost/HIDParser/HIDJoystick.h"
#include "USBHost/HIDParser/HIDUtils.h"
#include <cstring>
/* ----------------------------------------------- */
static int32_t mapValue(int32_t value, int32_t in_min, int32_t in_max, int32_t out_min, int32_t out_max)
{
return (value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
/* ----------------------------------------------- */
HIDJoystickData::HIDJoystickData() : index(0xFF),
support(0),
X(0),
Y(0),
Z(0),
Rx(0),
Ry(0),
Rz(0),
Slider(0),
Dial(0),
hat_switch(HIDJoystickHatSwitch::NEUTRAL),
button_count(0)
{
memset(buttons, 0, sizeof(buttons));
}
/* ----------------------------------------------- */
HIDJoystickData::~HIDJoystickData()
{
}
/* ----------------------------------------------- */
HIDJoystick::HIDJoystick(const std::shared_ptr<HIDReportDescriptor> &descriptor)
{
this->m_reports = descriptor->GetReports();
}
/* ----------------------------------------------- */
HIDJoystick::~HIDJoystick()
{
}
/* ----------------------------------------------- */
bool HIDJoystick::isValid()
{
return getCount() > 0;
}
/* ----------------------------------------------- */
uint8_t HIDJoystick::getCount()
{
uint8_t count = 0;
for (auto report : this->m_reports)
{
if (report.report_type == HIDIOReportType::Joystick || report.report_type == HIDIOReportType::GamePad)
count++;
}
return count;
}
/* ----------------------------------------------- */
bool HIDJoystick::parseData(uint8_t *data, uint16_t datalen, HIDJoystickData *joystick_data)
{
bool found = false;
uint8_t joystick_count = 0;
for (uint32_t i = 0; i < this->m_reports.size(); i++)
{
auto report = this->m_reports[i];
if (report.report_type != HIDIOReportType::Joystick && report.report_type != HIDIOReportType::GamePad)
continue;
joystick_count += 1;
for (auto ioblock : report.inputs)
{
uint32_t bitOffset = 0;
for (auto input : ioblock.data)
{
uint32_t value = HIDUtils::readBitsLE(data, bitOffset, input.size);
bitOffset += input.size;
if (bitOffset > (datalen * (uint32_t)8))
return false; // Out of range
if (input.type == HIDIOType::ReportId)
{
if (value != input.id)
break; // Not the correct report id
}
found = true;
joystick_data->index = joystick_count - 1;
if (input.type == HIDIOType::Button)
{
if (input.id >= MAX_BUTTONS)
return false;
joystick_data->buttons[input.id] = value;
if (joystick_data->button_count < input.id)
joystick_data->button_count = input.id;
}
else if (input.type == HIDIOType::X)
{
joystick_data->support |= JOYSTICK_SUPPORT_X;
joystick_data->X = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
}
else if (input.type == HIDIOType::Y)
{
joystick_data->support |= JOYSTICK_SUPPORT_Y;
joystick_data->Y = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
}
else if (input.type == HIDIOType::Z)
{
joystick_data->support |= JOYSTICK_SUPPORT_Z;
joystick_data->Z = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
}
else if (input.type == HIDIOType::Rx)
{
joystick_data->support |= JOYSTICK_SUPPORT_Rx;
joystick_data->Rx = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
}
else if (input.type == HIDIOType::Ry)
{
joystick_data->support |= JOYSTICK_SUPPORT_Ry;
joystick_data->Ry = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
}
else if (input.type == HIDIOType::Rz)
{
joystick_data->support |= JOYSTICK_SUPPORT_Rz;
joystick_data->Rz = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
}
else if (input.type == HIDIOType::Slider)
{
joystick_data->support |= JOYSTICK_SUPPORT_Slider;
joystick_data->Slider = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
}
else if (input.type == HIDIOType::Dial)
{
joystick_data->support |= JOYSTICK_SUPPORT_Dial;
joystick_data->Dial = mapValue(value, input.logical_min, input.logical_max, -32768, 32767);
}
else if (input.type == HIDIOType::HatSwitch)
{
joystick_data->support |= JOYSTICK_SUPPORT_HatSwitch;
joystick_data->hat_switch = (HIDJoystickHatSwitch)value;
}
}
if (found)
return true;
}
}
return false;
}

View File

@@ -0,0 +1,68 @@
#include "USBHost/HIDParser/HIDReportDescriptor.h"
#include <memory>
#include <vector>
#define MAX_BUTTONS 32
enum class HIDJoystickHatSwitch
{
UP = 0,
UP_RIGHT = 1,
RIGHT = 2,
DOWN_RIGHT = 3,
DOWN = 4,
DOWN_LEFT = 5,
LEFT = 6,
UP_LEFT = 7,
NEUTRAL = 8
};
#define JOYSTICK_SUPPORT_X 0x0001
#define JOYSTICK_SUPPORT_Y 0x0002
#define JOYSTICK_SUPPORT_Z 0x0004
#define JOYSTICK_SUPPORT_Rx 0x0008
#define JOYSTICK_SUPPORT_Ry 0x0010
#define JOYSTICK_SUPPORT_Rz 0x0020
#define JOYSTICK_SUPPORT_Slider 0x0040
#define JOYSTICK_SUPPORT_Dial 0x0080
#define JOYSTICK_SUPPORT_HatSwitch 0x0100
class HIDJoystickData
{
public:
HIDJoystickData();
~HIDJoystickData();
uint8_t index;
uint16_t support;
int16_t X; //-32768 to 32767
int16_t Y; //-32768 to 32767
int16_t Z; //-32768 to 32767
int16_t Rx; //-32768 to 32767
int16_t Ry; //-32768 to 32767
int16_t Rz; //-32768 to 32767
int16_t Slider; //-32768 to 32767
int16_t Dial; //-32768 to 32767
HIDJoystickHatSwitch hat_switch;
uint8_t button_count;
uint8_t buttons[MAX_BUTTONS];
};
class HIDJoystick
{
public:
HIDJoystick(const std::shared_ptr<HIDReportDescriptor> &descriptor);
~HIDJoystick();
bool isValid();
uint8_t getCount();
bool parseData(uint8_t *data, uint16_t datalen, HIDJoystickData *joystick_data);
private:
std::vector<HIDIOReport> m_reports;
};

View File

@@ -0,0 +1,141 @@
#include "USBHost/HIDParser/HIDReportDescriptor.h"
#include "USBHost/HIDParser/HIDReportDescriptorElements.h"
#include "USBHost/HIDParser/HIDReportDescriptorUsages.h"
#include <iostream>
#include <vector>
#include <memory>
#include <stack>
#include <cassert>
#include <algorithm>
// https://github.com/pasztorpisti/hid-report-parser/blob/master/src/hid_report_parser.cpp
// https://docs.kernel.org/hid/hidintro.html
HIDInputOutput::HIDInputOutput(HIDIOType type, uint32_t size, uint32_t id) : type(type),
sub_type(0),
size(size),
id(id),
logical_min(0),
logical_max(0),
physical_min(0),
physical_max(0),
unit(0),
unit_exponent(0)
{
}
HIDInputOutput::~HIDInputOutput()
{
}
HIDInputOutput::HIDInputOutput(const HIDUsage &usage, uint32_t idx) : type(HIDIOType::Unknown),
sub_type(0),
size(usage.property.size),
id(0),
logical_min(usage.property.logical_min),
logical_max(usage.property.logical_max),
physical_min(usage.property.physical_min),
physical_max(usage.property.physical_max),
unit(usage.property.unit),
unit_exponent(usage.property.unit_exponent)
{
if (usage.type == HIDUsageType::GenericDesktop)
{
if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::X)
this->type = HIDIOType::X;
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Y)
this->type = HIDIOType::Y;
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Z)
this->type = HIDIOType::Z;
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Rx)
this->type = HIDIOType::Rx;
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Ry)
this->type = HIDIOType::Ry;
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Rz)
this->type = HIDIOType::Rz;
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Slider)
this->type = HIDIOType::Slider;
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Dial)
this->type = HIDIOType::Dial;
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::HatSwitch)
this->type = HIDIOType::HatSwitch;
else if (usage.sub_type == (uint32_t)HIDUsageGenericDesktopSubType::Wheel)
this->type = HIDIOType::Wheel;
}
else if (usage.type == HIDUsageType::Button)
this->type = HIDIOType::Button;
else if (usage.type == HIDUsageType::ReportId)
this->type = HIDIOType::ReportId;
else if (usage.type == HIDUsageType::Padding)
this->type = HIDIOType::Padding;
else if (usage.type == HIDUsageType::VendorDefined)
{
this->type = HIDIOType::VendorDefined;
this->sub_type = usage.sub_type;
}
else
{
this->type = HIDIOType::Unknown;
this->sub_type = usage.sub_type;
}
this->id = usage.usage_min + idx;
}
/* -------------------------------------------------------------------- */
HIDReportDescriptor::HIDReportDescriptor()
{
}
/* -------------------------------------------------------------------- */
HIDReportDescriptor::HIDReportDescriptor(const uint8_t *hid_report_data, uint16_t hid_report_data_len)
{
parse(hid_report_data, hid_report_data_len);
}
/* -------------------------------------------------------------------- */
HIDReportDescriptor::~HIDReportDescriptor()
{
}
/* -------------------------------------------------------------------- */
void HIDReportDescriptor::parse(const uint8_t *hid_report_data, uint16_t hid_report_data_len)
{
HIDReportDescriptorElements hid_report_elements = HIDReportDescriptorElements(hid_report_data, hid_report_data_len);
std::vector<HIDReport> hid_report_usage = HIDReportDescriptorUsages::parse(hid_report_elements);
for (auto report : hid_report_usage)
{
m_reports.push_back(HIDIOReport((HIDIOReportType)report.usages[0].sub_type));
for (size_t k = 1; k < report.usages.size(); k++)
{
std::vector<HIDIOBlock> *ioblocks = NULL;
HIDUsage &usage = report.usages[k];
if (usage.io_type == HIDUsageIOType::Input)
ioblocks = &m_reports.back().inputs;
else if (usage.io_type == HIDUsageIOType::Output)
ioblocks = &m_reports.back().outputs;
else if (usage.io_type == HIDUsageIOType::Feature)
ioblocks = &m_reports.back().features;
else
continue;
for (uint32_t i = 0; i < usage.property.count; i++)
{
HIDInputOutput io(usage, i);
//We need to create a new block everytime we meet a ReportId and if there is no block
if (io.type == HIDIOType::ReportId || ioblocks->size() == 0)
ioblocks->push_back(HIDIOBlock());
ioblocks->back().data.push_back(io);
}
}
}
assert(m_reports.size() > 0);
}

View File

@@ -0,0 +1,101 @@
#pragma once
#include <stdint.h>
#include <vector>
enum class HIDIOType
{
Unknown = 0x00,
ReportId,
VendorDefined,
Padding,
Button,
X,
Y,
Z,
Rx,
Ry,
Rz,
Slider,
Dial,
HatSwitch,
Wheel
};
class HIDUsage;
class HIDInputOutput
{
public:
HIDInputOutput(HIDIOType type = HIDIOType::Unknown, uint32_t size=0, uint32_t id=0);
~HIDInputOutput();
HIDInputOutput(const HIDUsage &usage, uint32_t idx);
HIDIOType type; //Type (Button, X, Y, Hat switch, Padding, etc.)
uint32_t sub_type; //Sub type (Usefull for vendor defined and non handled types)
uint32_t size; //Size of the data in bits
uint32_t id; //Index of the input in the report
int32_t logical_min;
int32_t logical_max;
int32_t physical_min;
int32_t physical_max;
uint32_t unit;
uint32_t unit_exponent;
};
/* -------------------------------------------------------------------------- */
//https://usb.org/sites/default/files/hut1_2.pdf p31
typedef enum class HIDIOReportType
{
Unknown = 0x00,
Pointer = 0x01,
Mouse = 0x02,
Joystick = 0x04,
GamePad = 0x05,
Keyboard = 0x06,
Keypad = 0x07,
MultiAxis = 0x08,
Tablet = 0x09,
MAX = 0x2F
} HIDIOReportType;
class HIDIOBlock
{
public:
HIDIOBlock() {}
~HIDIOBlock() {}
std::vector<HIDInputOutput> data;
};
class HIDIOReport
{
public:
HIDIOReport(HIDIOReportType report_type = HIDIOReportType::Unknown) :
report_type(report_type)
{}
HIDIOReportType report_type;
std::vector<HIDIOBlock> inputs;
std::vector<HIDIOBlock> outputs;
std::vector<HIDIOBlock> features;
};
/* -------------------------------------------------------------------------- */
class HIDReportDescriptor
{
public:
HIDReportDescriptor();
HIDReportDescriptor(const uint8_t *hid_report_data, uint16_t hid_report_data_size);
~HIDReportDescriptor();
std::vector<HIDIOReport> GetReports() const { return m_reports; }
private:
void parse(const uint8_t *hid_report_data, uint16_t hid_report_data_len);
std::vector<HIDIOReport> m_reports;
};

View File

@@ -0,0 +1,153 @@
#include "USBHost/HIDParser/HIDReportDescriptorElements.h"
#include <cstring>
// https://docs.kernel.org/hid/hidreport-parsing.html
#define HID_FUNC_TYPE_MASK 0xFC
#define HID_TYPE_MASK 0x0C
#define HID_LENGTH_MASK 0x03
/* -------------------------------------------------------------------------- */
HIDElement::HIDElement() : type(HIDElementType::HID_UNKNOWN),
data_size(0)
{
memset(data, 0x00, sizeof(data));
}
/* -------------------------------------------------------------------------- */
HIDElement::~HIDElement()
{
}
/* -------------------------------------------------------------------------- */
HIDElement::HIDElement(HIDElementType type, const uint8_t *data, uint8_t data_size)
{
this->type = type;
memset(this->data, 0x00, sizeof(this->data));
memcpy(this->data, data, data_size);
this->data_size = data_size;
}
/* -------------------------------------------------------------------------- */
uint32_t HIDElement::GetSize() const
{
return data_size;
}
/* -------------------------------------------------------------------------- */
HIDElementType HIDElement::GetType() const
{
return type;
}
/* -------------------------------------------------------------------------- */
uint32_t HIDElement::GetValueUint32() const
{
return data[0] | ((uint32_t)data[1] << 8) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24);
}
/* -------------------------------------------------------------------------- */
int32_t HIDElement::GetValueInt32() const
{
if (this->data_size == 1)
return (int8_t)data[0];
else if (this->data_size == 2)
return (int16_t)(data[0] | ((uint16_t)data[1] << 8));
else if (this->data_size == 4)
return (int32_t)(data[0] | ((uint32_t)data[1] << 8) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24));
return 0;
}
/* -------------------------------------------------------------------------- */
HIDReportDescriptorElements::HIDReportDescriptorElements(const uint8_t *hid_report_data, uint16_t hid_report_data_len):
hid_report_data(hid_report_data),
hid_report_data_len(hid_report_data_len)
{
}
/* -------------------------------------------------------------------------- */
HIDReportDescriptorElements::~HIDReportDescriptorElements()
{
}
/* -------------------------------------------------------------------------- */
HIDReportDescriptorElements::Iterator HIDReportDescriptorElements::begin() const
{
return HIDReportDescriptorElements::Iterator(hid_report_data, hid_report_data_len);
}
/* -------------------------------------------------------------------------- */
HIDReportDescriptorElements::Iterator HIDReportDescriptorElements::end() const
{
return HIDReportDescriptorElements::Iterator(hid_report_data, hid_report_data_len, hid_report_data_len);
}
/* -------------------------------------------------------------------------- */
HIDReportDescriptorElements::Iterator::Iterator(const uint8_t* hid_report_data, uint16_t hid_report_data_len, uint16_t offset) :
hid_report_data(hid_report_data),
hid_report_data_len(hid_report_data_len),
offset(offset)
{
if (offset < hid_report_data_len)
parse_current_element();
}
/* -------------------------------------------------------------------------- */
HIDElement& HIDReportDescriptorElements::Iterator::operator*()
{
return current_element;
}
/* -------------------------------------------------------------------------- */
HIDElement* HIDReportDescriptorElements::Iterator::operator->()
{
return &current_element;
}
/* -------------------------------------------------------------------------- */
HIDReportDescriptorElements::Iterator& HIDReportDescriptorElements::Iterator::operator++()
{
offset += 1 + current_element_length;
if (offset < hid_report_data_len)
parse_current_element();
else
offset = hid_report_data_len; // End condition
return *this;
}
/* -------------------------------------------------------------------------- */
bool HIDReportDescriptorElements::Iterator::operator!=(const Iterator& other) const
{
return offset != other.offset;
}
/* -------------------------------------------------------------------------- */
void HIDReportDescriptorElements::Iterator::parse_current_element()
{
uint8_t type = hid_report_data[offset];
uint8_t datalen = type & HID_LENGTH_MASK;
if (datalen == 3)
datalen = 4;
current_element = HIDElement((HIDElementType)(type & HID_FUNC_TYPE_MASK), &hid_report_data[offset + 1], datalen);
current_element_length = datalen;
}

View File

@@ -0,0 +1,93 @@
#pragma once
#include <memory>
#include <vector>
#include <stdint.h>
/* -------------------------------------------------------------------------- */
typedef enum class HIDElementType
{
HID_UNKNOWN = 0x00,
HID_INPUT = 0x80,
HID_OUTPUT = 0x90,
HID_FEATURE = 0xb0,
HID_COLLECTION = 0xa0,
HID_END_COLLECTION = 0xc0,
HID_USAGE_PAGE = 0x04,
HID_LOGICAL_MINIMUM = 0x14,
HID_LOGICAL_MAXIMUM = 0x24,
HID_PHYSICAL_MINIMUM = 0x34,
HID_PHYSICAL_MAXIMUM = 0x44,
HID_UNIT_EXPONENT = 0x54,
HID_UNIT = 0x64,
HID_REPORT_SIZE = 0x74,
HID_REPORT_ID = 0x84,
HID_REPORT_COUNT = 0x94,
HID_PUSH = 0xa4,
HID_POP = 0xb4,
HID_USAGE = 0x08,
HID_USAGE_MINIMUM = 0x18,
HID_USAGE_MAXIMUM = 0x28,
HID_DESIGNATOR_INDEX = 0x38,
HID_DESIGNATOR_MINIMUM = 0x48,
HID_DESIGNATOR_MAXIMUM = 0x58,
HID_STRING_INDEX = 0x78,
HID_STRING_MINIMUM = 0x88,
HID_STRING_MAXIMUM = 0x98,
HID_DELIMITER = 0xa8
} HIDElementType;
/* -------------------------------------------------------------------------- */
class HIDElement
{
public:
HIDElement();
HIDElement(HIDElementType type, const uint8_t *data, uint8_t data_size);
~HIDElement();
uint32_t GetSize() const;
HIDElementType GetType() const;
uint32_t GetValueUint32() const;
int32_t GetValueInt32() const;
private:
HIDElementType type;
uint8_t data[4];
uint8_t data_size;
};
/* -------------------------------------------------------------------------- */
class HIDReportDescriptorElements
{
public:
HIDReportDescriptorElements(const uint8_t *hid_report_data, uint16_t hid_report_data_len);
~HIDReportDescriptorElements();
class Iterator {
public:
Iterator(const uint8_t* hid_report_data, uint16_t hid_report_data_len, uint16_t offset = 0);
HIDElement& operator*();
HIDElement* operator->();
Iterator& operator++();
bool operator!=(const Iterator& other) const;
private:
void parse_current_element() ;
const uint8_t* hid_report_data;
uint16_t hid_report_data_len;
uint16_t offset;
HIDElement current_element;
uint8_t current_element_length;
};
Iterator begin() const;
Iterator end() const;
private:
const uint8_t *hid_report_data;
uint16_t hid_report_data_len;
};

View File

@@ -0,0 +1,269 @@
#include "USBHost/HIDParser/HIDReportDescriptorUsages.h"
#include <iostream>
#include <vector>
#include <memory>
#include <stack>
#include <cassert>
#include <algorithm>
//---------------USAGE_PAGE-----------------
#define USAGE_PAGE_GenericDesktop 0x01
#define USAGE_PAGE_Simulation 0x02
#define USAGE_PAGE_VR 0x03
#define USAGE_PAGE_Sport 0x04
#define USAGE_PAGE_Game 0x05
#define USAGE_PAGE_GenericDevice 0x06
#define USAGE_PAGE_Keyboard 0x07
#define USAGE_PAGE_LEDs 0x08
#define USAGE_PAGE_Button 0x09
#define USAGE_PAGE_Ordinal 0x0A
#define USAGE_PAGE_Telephony 0x0B
#define USAGE_PAGE_Consumer 0x0C
#define USAGE_PAGE_VendorDefined 0xFF00
//---------------USAGE-----------------
#define USAGE_X 0x30
#define USAGE_Y 0x31
#define USAGE_Z 0x32
#define USAGE_Rx 0x33
#define USAGE_Ry 0x34
#define USAGE_Rz 0x35
#define USAGE_Slider 0x36
#define USAGE_Dial 0x37
#define USAGE_Wheel 0x38
#define USAGE_Hat_switch 0x39
//---------------INPUT-----------------
#define INPUT_Const 0x01
#define INPUT_Var 0x02
#define INPUT_Rel 0x04
#define INPUT_Wrap 0x08
#define INPUT_NLin 0x10
#define INPUT_NPrf 0x20
#define INPUT_Null 0x40
#define INPUT_Vol 0x80
//---------------COLLECTION-----------------
#define HID_COLLECTION_PHYSICAL 0x00
#define HID_COLLECTION_APPLICATION 0x01
#define HID_COLLECTION_LOGICAL 0x02
#define HID_COLLECTION_REPORT 0x03
#define HID_COLLECTION_NAMED_ARRAY 0x04
#define HID_COLLECTION_USAGE_SWITCH 0x05
#define HID_COLLECTION_USAGE_MODIFIER 0x06
/* -------------------------------------------------------------------------- */
HIDUsage::HIDUsage(HIDUsageType type, uint32_t sub_type, HIDUsageIOType io_type, HIDProperty property) : type(type),
sub_type(sub_type),
usage_min(0),
usage_max(0),
io_type(io_type),
property(property)
{
if (sub_type != 0)
{
usage_min = sub_type;
usage_max = sub_type;
}
}
/* -------------------------------------------------------------------------- */
HIDUsage::~HIDUsage()
{
}
/* -------------------------------------------------------------------------- */
HIDProperty::HIDProperty(uint32_t size, uint32_t count) : logical_min(0),
logical_max(0),
physical_min(0),
physical_max(0),
unit(0),
unit_exponent(0),
size(size),
count(count)
{
}
/* -------------------------------------------------------------------------- */
HIDProperty::~HIDProperty()
{
}
/* -------------------------------------------------------------------------- */
bool HIDProperty::is_valid()
{
return size != 0 && count != 0;
}
/* -------------------------------------------------------------------------- */
HIDUsageType convert_usage_page(uint32_t usage_page)
{
switch (usage_page)
{
case USAGE_PAGE_Button:
return HIDUsageType::Button;
case USAGE_PAGE_GenericDesktop:
return HIDUsageType::GenericDesktop;
case USAGE_PAGE_VendorDefined:
return HIDUsageType::VendorDefined;
default:
return HIDUsageType::Unknown;
}
}
/* -------------------------------------------------------------------------- */
std::vector<HIDReport> HIDReportDescriptorUsages::parse(const HIDReportDescriptorElements &elements)
{
HIDProperty current_property;
std::vector<HIDUsage> current_usages;
HIDUsageType current_usage_page_type = HIDUsageType::Unknown;
std::vector<HIDReport> report;
uint8_t current_report_id = 0;
for (const HIDElement &element : elements)
{
switch (element.GetType())
{
case HIDElementType::HID_USAGE_PAGE:
current_usage_page_type = convert_usage_page(element.GetValueUint32());
break;
case HIDElementType::HID_USAGE:
current_usages.push_back(HIDUsage(current_usage_page_type, element.GetValueUint32()));
break;
case HIDElementType::HID_USAGE_MAXIMUM:
case HIDElementType::HID_USAGE_MINIMUM:
{
if (current_usages.size() == 0)
current_usages.push_back(HIDUsage(current_usage_page_type));
for (HIDUsage &usage : current_usages)
{
if (element.GetType() == HIDElementType::HID_USAGE_MINIMUM)
usage.usage_min = element.GetValueUint32();
else if (element.GetType() == HIDElementType::HID_USAGE_MAXIMUM)
usage.usage_max = element.GetValueUint32();
}
break;
}
case HIDElementType::HID_REPORT_ID:
current_report_id = element.GetValueUint32();
break;
case HIDElementType::HID_LOGICAL_MINIMUM:
current_property.logical_min = element.GetValueInt32();
current_property.logical_min_unsigned = element.GetValueUint32();
break;
case HIDElementType::HID_LOGICAL_MAXIMUM:
current_property.logical_max = element.GetValueInt32();
current_property.logical_max_unsigned = element.GetValueUint32();
break;
case HIDElementType::HID_PHYSICAL_MINIMUM:
current_property.physical_min = element.GetValueInt32();
current_property.physical_min_unsigned = element.GetValueUint32();
break;
case HIDElementType::HID_PHYSICAL_MAXIMUM:
current_property.physical_max = element.GetValueInt32();
current_property.physical_max_unsigned = element.GetValueUint32();
break;
case HIDElementType::HID_UNIT_EXPONENT:
current_property.unit_exponent = element.GetValueUint32();
break;
case HIDElementType::HID_UNIT:
current_property.unit = element.GetValueUint32();
break;
case HIDElementType::HID_REPORT_SIZE:
current_property.size = element.GetValueUint32();
break;
case HIDElementType::HID_REPORT_COUNT:
current_property.count = element.GetValueUint32();
break;
case HIDElementType::HID_INPUT:
case HIDElementType::HID_OUTPUT:
case HIDElementType::HID_FEATURE:
{
if (current_usages.size() == 0)
current_usages.push_back(HIDUsage(HIDUsageType::Padding));
HIDUsageIOType io_type = HIDUsageIOType::None;
if (element.GetType() == HIDElementType::HID_INPUT)
io_type = HIDUsageIOType::Input;
else if (element.GetType() == HIDElementType::HID_OUTPUT)
io_type = HIDUsageIOType::Output;
else if (element.GetType() == HIDElementType::HID_FEATURE)
io_type = HIDUsageIOType::Feature;
for (HIDUsage &usage : current_usages)
{
//Fix bug on few controllers, that provide incorrect "Unsigned" values
if (current_property.logical_max < current_property.logical_min)
current_property.logical_max = (int32_t)current_property.logical_max_unsigned;
if (current_property.physical_max < current_property.physical_min)
current_property.physical_max = (int32_t)current_property.physical_max_unsigned;
usage.io_type = io_type;
usage.property = current_property;
usage.property.count = (current_property.count / (uint32_t)current_usages.size());
}
if (current_report_id != 0)
{
current_usages.insert(current_usages.begin(), HIDUsage(HIDUsageType::ReportId, current_report_id, io_type, HIDProperty(8, 1)));
current_report_id = 0;
}
report.back().usages.insert(report.back().usages.end(), current_usages.begin(), current_usages.end());
current_usages.clear();
break;
}
// For now collections are ignored
case HIDElementType::HID_COLLECTION:
{
if (element.GetValueUint32() == HID_COLLECTION_APPLICATION)
report.push_back(HIDReport());
report.back().usages.insert(report.back().usages.end(), current_usages.begin(), current_usages.end());
current_usages.clear();
break;
}
case HIDElementType::HID_END_COLLECTION:
{
break;
}
case HIDElementType::HID_UNKNOWN:
case HIDElementType::HID_PUSH:
case HIDElementType::HID_POP:
case HIDElementType::HID_DELIMITER:
case HIDElementType::HID_DESIGNATOR_INDEX:
case HIDElementType::HID_DESIGNATOR_MINIMUM:
case HIDElementType::HID_DESIGNATOR_MAXIMUM:
case HIDElementType::HID_STRING_INDEX:
case HIDElementType::HID_STRING_MINIMUM:
case HIDElementType::HID_STRING_MAXIMUM:
break;
}
}
return report;
}

View File

@@ -0,0 +1,101 @@
#pragma once
#include "USBHost/HIDParser/HIDReportDescriptorElements.h"
#include <vector>
enum class HIDUsageIOType
{
None = 0x00,
Input,
Output,
Feature
};
enum class HIDUsageType
{
Unknown = 0x00,
ReportId,
Padding,
Button,
GenericDesktop,
VendorDefined
};
enum class HIDUsageGenericDesktopSubType
{
Pointer = 0x01,
Mouse = 0x02,
Joystick = 0x04,
GamePad = 0x05,
Keyboard = 0x06,
Keypad = 0x07,
MultiAxisController = 0x08,
ReportTypeEnd = 0x1F,
X = 0x30,
Y = 0x31,
Z = 0x32,
Rx = 0x33,
Ry = 0x34,
Rz = 0x35,
Slider = 0x36,
Dial = 0x37,
Wheel = 0x38,
HatSwitch = 0x39,
};
class HIDProperty
{
public:
HIDProperty(uint32_t size=0, uint32_t count=0);
virtual ~HIDProperty();
virtual bool is_valid();
int32_t logical_min;
uint32_t logical_min_unsigned;
int32_t logical_max;
uint32_t logical_max_unsigned;
int32_t physical_min;
uint32_t physical_min_unsigned;
int32_t physical_max;
uint32_t physical_max_unsigned;
uint32_t unit;
uint32_t unit_exponent;
uint32_t size; //Size of the data in bits
uint32_t count; //Number of data items
};
class HIDUsage
{
public:
/// @brief
/// @param type
/// @param sub_type will depend on the type, for example, if type is GenericDesktop, sub_type will be HIDUsageGenericDesktopSubType etc.
/// @param property
HIDUsage(HIDUsageType type, uint32_t sub_type = 0, HIDUsageIOType io_type = HIDUsageIOType::None, HIDProperty property = HIDProperty());
~HIDUsage();
HIDUsageType type; //Input type (Button, X, Y, Hat switch, Padding, etc.)
uint32_t sub_type; //Sub type (Button number, etc.)
uint32_t usage_min;
uint32_t usage_max;
HIDUsageIOType io_type;
HIDProperty property;
};
class HIDReport
{
public:
std::vector<HIDUsage> usages;
};
class HIDReportDescriptorUsages
{
public:
static std::vector<HIDReport> parse(const HIDReportDescriptorElements &elements);
};

View File

@@ -0,0 +1,26 @@
#include "USBHost/HIDParser/HIDUtils.h"
uint32_t HIDUtils::readBitsLE(uint8_t *buffer, uint32_t bitOffset, uint32_t bitLength) {
// Calculate the starting byte index and bit index within that byte
uint32_t byteIndex = bitOffset / 8;
uint32_t bitIndex = bitOffset % 8; // Little endian, LSB is at index 0
uint32_t result = 0;
for (uint32_t i = 0; i < bitLength; ++i) {
// Check if we need to move to the next byte
if (bitIndex > 7) {
++byteIndex;
bitIndex = 0;
}
// Get the bit at the current position and add it to the result
uint8_t bit = (buffer[byteIndex] >> bitIndex) & 0x01;
result |= (bit << i);
// Move to the next bit
++bitIndex;
}
return result;
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include <stdint.h>
class HIDUtils
{
public:
HIDUtils() {}
~HIDUtils() {}
static uint32_t readBitsLE(uint8_t *buffer, uint32_t bitOffset, uint32_t bitLength);
};

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