Initial commit
This commit is contained in:
commit
bb5e51330a
14 changed files with 1764 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
GP2040-CE
|
||||||
|
build
|
||||||
|
!.vscode/*
|
||||||
22
.vscode/c_cpp_properties.json
vendored
Normal file
22
.vscode/c_cpp_properties.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Pico",
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/**",
|
||||||
|
"${userHome}/.pico-sdk/sdk/2.2.0/**"
|
||||||
|
],
|
||||||
|
"forcedInclude": [
|
||||||
|
"${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h",
|
||||||
|
"${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h"
|
||||||
|
],
|
||||||
|
"defines": [],
|
||||||
|
"compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc",
|
||||||
|
"compileCommands": "${workspaceFolder}/build/compile_commands.json",
|
||||||
|
"cStandard": "c17",
|
||||||
|
"cppStandard": "c++14",
|
||||||
|
"intelliSenseMode": "linux-gcc-arm"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
||||||
15
.vscode/cmake-kits.json
vendored
Normal file
15
.vscode/cmake-kits.json
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Pico",
|
||||||
|
"compilers": {
|
||||||
|
"C": "${command:raspberry-pi-pico.getCompilerPath}",
|
||||||
|
"CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}"
|
||||||
|
},
|
||||||
|
"environmentVariables": {
|
||||||
|
"PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}"
|
||||||
|
},
|
||||||
|
"cmakeSettings": {
|
||||||
|
"Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
9
.vscode/extensions.json
vendored
Normal file
9
.vscode/extensions.json
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"marus25.cortex-debug",
|
||||||
|
"ms-vscode.cpptools",
|
||||||
|
"ms-vscode.cpptools-extension-pack",
|
||||||
|
"ms-vscode.vscode-serial-monitor",
|
||||||
|
"raspberry-pi.raspberry-pi-pico"
|
||||||
|
]
|
||||||
|
}
|
||||||
50
.vscode/launch.json
vendored
Normal file
50
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Pico Debug (Cortex-Debug)",
|
||||||
|
"cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
|
||||||
|
"executable": "${command:raspberry-pi-pico.launchTargetPath}",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"servertype": "openocd",
|
||||||
|
"serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
|
||||||
|
"gdbPath": "${command:raspberry-pi-pico.getGDBPath}",
|
||||||
|
"device": "${command:raspberry-pi-pico.getChipUppercase}",
|
||||||
|
"configFiles": [
|
||||||
|
"interface/cmsis-dap.cfg",
|
||||||
|
"target/${command:raspberry-pi-pico.getTarget}.cfg"
|
||||||
|
],
|
||||||
|
"svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd",
|
||||||
|
"runToEntryPoint": "main",
|
||||||
|
// Fix for no_flash binaries, where monitor reset halt doesn't do what is expected
|
||||||
|
// Also works fine for flash binaries
|
||||||
|
"overrideLaunchCommands": [
|
||||||
|
"monitor reset init",
|
||||||
|
"load \"${command:raspberry-pi-pico.launchTargetPath}\""
|
||||||
|
],
|
||||||
|
"openOCDLaunchCommands": [
|
||||||
|
"adapter speed 5000"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pico Debug (Cortex-Debug with external OpenOCD)",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"executable": "${command:raspberry-pi-pico.launchTargetPath}",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"servertype": "external",
|
||||||
|
"gdbTarget": "localhost:3333",
|
||||||
|
"gdbPath": "${command:raspberry-pi-pico.getGDBPath}",
|
||||||
|
"device": "${command:raspberry-pi-pico.getChipUppercase}",
|
||||||
|
"svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd",
|
||||||
|
"runToEntryPoint": "main",
|
||||||
|
// Fix for no_flash binaries, where monitor reset halt doesn't do what is expected
|
||||||
|
// Also works fine for flash binaries
|
||||||
|
"overrideLaunchCommands": [
|
||||||
|
"monitor reset init",
|
||||||
|
"load \"${command:raspberry-pi-pico.launchTargetPath}\""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
40
.vscode/settings.json
vendored
Normal file
40
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"cmake.showSystemKits": false,
|
||||||
|
"cmake.options.statusBarVisibility": "hidden",
|
||||||
|
"cmake.options.advanced": {
|
||||||
|
"build": {
|
||||||
|
"statusBarVisibility": "hidden"
|
||||||
|
},
|
||||||
|
"launch": {
|
||||||
|
"statusBarVisibility": "hidden"
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"statusBarVisibility": "hidden"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cmake.configureOnEdit": true,
|
||||||
|
"cmake.automaticReconfigure": true,
|
||||||
|
"cmake.configureOnOpen": true,
|
||||||
|
"cmake.generator": "Ninja",
|
||||||
|
"cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake",
|
||||||
|
"C_Cpp.debugShortcut": false,
|
||||||
|
"terminal.integrated.env.windows": {
|
||||||
|
"PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0",
|
||||||
|
"PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1",
|
||||||
|
"Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}"
|
||||||
|
},
|
||||||
|
"terminal.integrated.env.osx": {
|
||||||
|
"PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0",
|
||||||
|
"PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1",
|
||||||
|
"PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}"
|
||||||
|
},
|
||||||
|
"terminal.integrated.env.linux": {
|
||||||
|
"PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0",
|
||||||
|
"PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1",
|
||||||
|
"PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}"
|
||||||
|
},
|
||||||
|
"raspberry-pi-pico.cmakeAutoConfigure": false,
|
||||||
|
"raspberry-pi-pico.useCmakeTools": true,
|
||||||
|
"raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake",
|
||||||
|
"raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja"
|
||||||
|
}
|
||||||
102
.vscode/tasks.json
vendored
Normal file
102
.vscode/tasks.json
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Compile Project",
|
||||||
|
"type": "process",
|
||||||
|
"isBuildCommand": true,
|
||||||
|
"command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja",
|
||||||
|
"args": ["-C", "${workspaceFolder}/build"],
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "dedicated"
|
||||||
|
},
|
||||||
|
"problemMatcher": "$gcc",
|
||||||
|
"windows": {
|
||||||
|
"command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Run Project",
|
||||||
|
"type": "process",
|
||||||
|
"command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool",
|
||||||
|
"args": [
|
||||||
|
"load",
|
||||||
|
"${command:raspberry-pi-pico.launchTargetPath}",
|
||||||
|
"-fx"
|
||||||
|
],
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "dedicated"
|
||||||
|
},
|
||||||
|
"problemMatcher": [],
|
||||||
|
"windows": {
|
||||||
|
"command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Flash",
|
||||||
|
"type": "process",
|
||||||
|
"command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
|
||||||
|
"args": [
|
||||||
|
"-s",
|
||||||
|
"${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
|
||||||
|
"-f",
|
||||||
|
"interface/cmsis-dap.cfg",
|
||||||
|
"-f",
|
||||||
|
"target/${command:raspberry-pi-pico.getTarget}.cfg",
|
||||||
|
"-c",
|
||||||
|
"adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit"
|
||||||
|
],
|
||||||
|
"problemMatcher": [],
|
||||||
|
"windows": {
|
||||||
|
"command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Rescue Reset",
|
||||||
|
"type": "process",
|
||||||
|
"command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
|
||||||
|
"args": [
|
||||||
|
"-s",
|
||||||
|
"${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
|
||||||
|
"-f",
|
||||||
|
"interface/cmsis-dap.cfg",
|
||||||
|
"-f",
|
||||||
|
"target/${command:raspberry-pi-pico.getChip}-rescue.cfg",
|
||||||
|
"-c",
|
||||||
|
"adapter speed 5000; reset halt; exit"
|
||||||
|
],
|
||||||
|
"problemMatcher": [],
|
||||||
|
"windows": {
|
||||||
|
"command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "RISC-V Reset (RP2350)",
|
||||||
|
"type": "process",
|
||||||
|
"command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
|
||||||
|
"args": [
|
||||||
|
"-s",
|
||||||
|
"${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
|
||||||
|
"-c",
|
||||||
|
"set USE_CORE { rv0 rv1 cm0 cm1 }",
|
||||||
|
"-f",
|
||||||
|
"interface/cmsis-dap.cfg",
|
||||||
|
"-f",
|
||||||
|
"target/rp2350.cfg",
|
||||||
|
"-c",
|
||||||
|
"adapter speed 5000; init;",
|
||||||
|
"-c",
|
||||||
|
"write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];",
|
||||||
|
"-c",
|
||||||
|
"reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit"
|
||||||
|
],
|
||||||
|
"problemMatcher": [],
|
||||||
|
"windows": {
|
||||||
|
"command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
64
CMakeLists.txt
Normal file
64
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
# Generated Cmake Pico project file
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
# Initialise pico_sdk from installed location
|
||||||
|
# (note this can come from environment, CMake cache etc)
|
||||||
|
|
||||||
|
# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work ==
|
||||||
|
if(WIN32)
|
||||||
|
set(USERHOME $ENV{USERPROFILE})
|
||||||
|
else()
|
||||||
|
set(USERHOME $ENV{HOME})
|
||||||
|
endif()
|
||||||
|
set(sdkVersion 2.2.0)
|
||||||
|
set(toolchainVersion 14_2_Rel1)
|
||||||
|
set(picotoolVersion 2.2.0-a4)
|
||||||
|
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
|
||||||
|
if (EXISTS ${picoVscode})
|
||||||
|
include(${picoVscode})
|
||||||
|
endif()
|
||||||
|
# ====================================================================================
|
||||||
|
set(PICO_BOARD pico CACHE STRING "Board type")
|
||||||
|
|
||||||
|
# Pull in Raspberry Pi Pico SDK (must be before project)
|
||||||
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
project(switch-pico C CXX ASM)
|
||||||
|
|
||||||
|
# Initialise the Raspberry Pi Pico SDK
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
# Add executable. Default name is the project name, version 0.1
|
||||||
|
|
||||||
|
add_executable(switch-pico
|
||||||
|
switch-pico.cpp
|
||||||
|
switch_pro_driver.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
pico_set_program_name(switch-pico "switch-pico")
|
||||||
|
pico_set_program_version(switch-pico "0.1")
|
||||||
|
|
||||||
|
# Modify the below lines to enable/disable output over UART/USB
|
||||||
|
pico_enable_stdio_uart(switch-pico 0)
|
||||||
|
pico_enable_stdio_usb(switch-pico 0)
|
||||||
|
|
||||||
|
# Add the standard library to the build
|
||||||
|
target_link_libraries(switch-pico
|
||||||
|
pico_stdlib
|
||||||
|
tinyusb_device
|
||||||
|
tinyusb_board
|
||||||
|
hardware_uart
|
||||||
|
pico_rand
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add the standard include files to the build
|
||||||
|
target_include_directories(switch-pico PRIVATE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
pico_add_extra_outputs(switch-pico)
|
||||||
121
pico_sdk_import.cmake
Normal file
121
pico_sdk_import.cmake
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||||
|
|
||||||
|
# This can be dropped into an external project to help locate this SDK
|
||||||
|
# It should be include()ed prior to project()
|
||||||
|
|
||||||
|
# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
|
||||||
|
# following conditions are met:
|
||||||
|
#
|
||||||
|
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
|
||||||
|
# disclaimer.
|
||||||
|
#
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
|
||||||
|
# disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||||
|
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||||
|
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_TAG "master")
|
||||||
|
message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK")
|
||||||
|
|
||||||
|
if (NOT PICO_SDK_PATH)
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT)
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||||
|
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
|
endif ()
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_sdk
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (NOT pico_sdk)
|
||||||
|
message("Downloading Raspberry Pi Pico SDK")
|
||||||
|
# GIT_SUBMODULES_RECURSE was added in 3.17
|
||||||
|
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
|
||||||
|
FetchContent_Populate(
|
||||||
|
pico_sdk
|
||||||
|
QUIET
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
|
||||||
|
GIT_SUBMODULES_RECURSE FALSE
|
||||||
|
|
||||||
|
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src
|
||||||
|
BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build
|
||||||
|
SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild
|
||||||
|
)
|
||||||
|
else ()
|
||||||
|
FetchContent_Populate(
|
||||||
|
pico_sdk
|
||||||
|
QUIET
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
|
||||||
|
|
||||||
|
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src
|
||||||
|
BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build
|
||||||
|
SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||||
|
endif ()
|
||||||
|
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||||
|
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||||
|
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||||
|
|
||||||
|
include(${PICO_SDK_INIT_CMAKE_FILE})
|
||||||
76
switch-pico.cpp
Normal file
76
switch-pico.cpp
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "bsp/board.h"
|
||||||
|
#include "hardware/uart.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "tusb.h"
|
||||||
|
#include "switch_pro_driver.h"
|
||||||
|
|
||||||
|
// UART1 is reserved for external input frames from the host PC.
|
||||||
|
#define UART_ID uart1
|
||||||
|
#define BAUD_RATE 115200
|
||||||
|
#define UART_TX_PIN 4
|
||||||
|
#define UART_RX_PIN 5
|
||||||
|
|
||||||
|
static void init_uart_input() {
|
||||||
|
uart_init(UART_ID, BAUD_RATE);
|
||||||
|
gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
|
||||||
|
gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);
|
||||||
|
uart_set_format(UART_ID, 8, 1, UART_PARITY_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SwitchInputState neutral_input() {
|
||||||
|
SwitchInputState state{};
|
||||||
|
state.lx = SWITCH_PRO_JOYSTICK_MID;
|
||||||
|
state.ly = SWITCH_PRO_JOYSTICK_MID;
|
||||||
|
state.rx = SWITCH_PRO_JOYSTICK_MID;
|
||||||
|
state.ry = SWITCH_PRO_JOYSTICK_MID;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume UART bytes and forward complete frames to the Switch Pro driver.
|
||||||
|
static void poll_uart_frames() {
|
||||||
|
static uint8_t buffer[8];
|
||||||
|
static uint8_t index = 0;
|
||||||
|
static absolute_time_t last_byte_time = {0};
|
||||||
|
static bool has_last_byte = false;
|
||||||
|
|
||||||
|
while (uart_is_readable(UART_ID)) {
|
||||||
|
uint8_t byte = uart_getc(UART_ID);
|
||||||
|
|
||||||
|
uint64_t now = to_ms_since_boot(get_absolute_time());
|
||||||
|
if (has_last_byte && (now - to_ms_since_boot(last_byte_time)) > 20) {
|
||||||
|
index = 0; // stale data, restart frame
|
||||||
|
}
|
||||||
|
last_byte_time = get_absolute_time();
|
||||||
|
has_last_byte = true;
|
||||||
|
|
||||||
|
if (index == 0) {
|
||||||
|
if (byte != 0xAA) {
|
||||||
|
continue; // wait for start-of-frame marker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[index++] = byte;
|
||||||
|
if (index >= sizeof(buffer)) {
|
||||||
|
switch_pro_apply_uart_packet(buffer, sizeof(buffer));
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
board_init();
|
||||||
|
stdio_init_all();
|
||||||
|
|
||||||
|
init_uart_input();
|
||||||
|
|
||||||
|
tusb_init();
|
||||||
|
switch_pro_init();
|
||||||
|
switch_pro_set_input(neutral_input());
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
tud_task(); // USB device tasks
|
||||||
|
poll_uart_frames(); // Pull controller state from UART1
|
||||||
|
switch_pro_task(); // Push state to the Switch host
|
||||||
|
}
|
||||||
|
}
|
||||||
511
switch_pro_descriptors.h
Normal file
511
switch_pro_descriptors.h
Normal file
|
|
@ -0,0 +1,511 @@
|
||||||
|
/*
|
||||||
|
* Switch Pro controller descriptor and data definitions.
|
||||||
|
* Copied from the GP2040-CE project to mirror the behaviour of its
|
||||||
|
* SwitchProDriver without pulling in the rest of the codebase.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define SWITCH_PRO_ENDPOINT_SIZE 64
|
||||||
|
|
||||||
|
// HAT report (4 bits)
|
||||||
|
#define SWITCH_PRO_HAT_UP 0x00
|
||||||
|
#define SWITCH_PRO_HAT_UPRIGHT 0x01
|
||||||
|
#define SWITCH_PRO_HAT_RIGHT 0x02
|
||||||
|
#define SWITCH_PRO_HAT_DOWNRIGHT 0x03
|
||||||
|
#define SWITCH_PRO_HAT_DOWN 0x04
|
||||||
|
#define SWITCH_PRO_HAT_DOWNLEFT 0x05
|
||||||
|
#define SWITCH_PRO_HAT_LEFT 0x06
|
||||||
|
#define SWITCH_PRO_HAT_UPLEFT 0x07
|
||||||
|
#define SWITCH_PRO_HAT_NOTHING 0x08
|
||||||
|
|
||||||
|
#define SWITCH_PRO_MASK_ZR (1U << 7)
|
||||||
|
#define SWITCH_PRO_MASK_R (1U << 6)
|
||||||
|
#define SWITCH_PRO_MASK_A (1U << 3)
|
||||||
|
#define SWITCH_PRO_MASK_B (1U << 2)
|
||||||
|
#define SWITCH_PRO_MASK_X (1U << 1)
|
||||||
|
#define SWITCH_PRO_MASK_Y 1U
|
||||||
|
|
||||||
|
#define SWITCH_PRO_MASK_CAPTURE (1U << 5)
|
||||||
|
#define SWITCH_PRO_MASK_HOME (1U << 4)
|
||||||
|
#define SWITCH_PRO_MASK_L3 (1U << 3)
|
||||||
|
#define SWITCH_PRO_MASK_R3 (1U << 2)
|
||||||
|
#define SWITCH_PRO_MASK_PLUS (1U << 1)
|
||||||
|
#define SWITCH_PRO_MASK_MINUS 1U
|
||||||
|
|
||||||
|
#define SWITCH_PRO_MASK_ZL (1U << 7)
|
||||||
|
#define SWITCH_PRO_MASK_L (1U << 6)
|
||||||
|
|
||||||
|
#define SWITCH_PRO_JOYSTICK_MIN 0x0000
|
||||||
|
#define SWITCH_PRO_JOYSTICK_MID 0x7FFF
|
||||||
|
#define SWITCH_PRO_JOYSTICK_MAX 0xFFFF
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
REPORT_OUTPUT_00 = 0x00,
|
||||||
|
REPORT_FEATURE = 0x01,
|
||||||
|
REPORT_OUTPUT_10 = 0x10,
|
||||||
|
REPORT_OUTPUT_21 = 0x21,
|
||||||
|
REPORT_OUTPUT_30 = 0x30,
|
||||||
|
REPORT_CONFIGURATION = 0x80,
|
||||||
|
REPORT_USB_INPUT_81 = 0x81,
|
||||||
|
} SwitchReportID;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
IDENTIFY = 0x01,
|
||||||
|
HANDSHAKE,
|
||||||
|
BAUD_RATE,
|
||||||
|
DISABLE_USB_TIMEOUT,
|
||||||
|
ENABLE_USB_TIMEOUT
|
||||||
|
} SwitchOutputSubtypes;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GET_CONTROLLER_STATE = 0x00,
|
||||||
|
BLUETOOTH_PAIR_REQUEST = 0x01,
|
||||||
|
REQUEST_DEVICE_INFO = 0x02,
|
||||||
|
SET_MODE = 0x03,
|
||||||
|
TRIGGER_BUTTONS = 0x04,
|
||||||
|
SET_SHIPMENT = 0x08,
|
||||||
|
SPI_READ = 0x10,
|
||||||
|
SET_NFC_IR_CONFIG = 0x21,
|
||||||
|
SET_NFC_IR_STATE = 0x22,
|
||||||
|
SET_PLAYER_LIGHTS = 0x30,
|
||||||
|
GET_PLAYER_LIGHTS = 0x31,
|
||||||
|
COMMAND_UNKNOWN_33 = 0x33,
|
||||||
|
SET_HOME_LIGHT = 0x38,
|
||||||
|
TOGGLE_IMU = 0x40,
|
||||||
|
IMU_SENSITIVITY = 0x41,
|
||||||
|
READ_IMU = 0x43,
|
||||||
|
ENABLE_VIBRATION = 0x48,
|
||||||
|
GET_VOLTAGE = 0x50,
|
||||||
|
} SwitchCommands;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t data[3];
|
||||||
|
|
||||||
|
void setX(uint16_t x) {
|
||||||
|
data[0] = x & 0xFF;
|
||||||
|
data[1] = (data[1] & 0xF0) | ((x >> 8) & 0x0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setY(uint16_t y) {
|
||||||
|
data[1] = (data[1] & 0x0F) | ((y & 0x0F) << 4);
|
||||||
|
data[2] = (y >> 4) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t getX() {
|
||||||
|
return static_cast<uint16_t>(data[0]) | ((data[1] & 0x0F) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t getY() {
|
||||||
|
return static_cast<uint16_t>((data[1] >> 4)) | (data[2] << 4);
|
||||||
|
}
|
||||||
|
} SwitchAnalog;
|
||||||
|
|
||||||
|
// left and right calibration are stored differently for some reason, so two structs
|
||||||
|
typedef struct {
|
||||||
|
uint8_t data[9];
|
||||||
|
|
||||||
|
void getMin(uint16_t& x, uint16_t& y) const { packCalib(6, x, y); }
|
||||||
|
void getCenter(uint16_t& x, uint16_t& y) const { packCalib(3, x, y); }
|
||||||
|
void getMax(uint16_t& x, uint16_t& y) const { packCalib(0, x, y); }
|
||||||
|
|
||||||
|
void getRealMin(uint16_t& x, uint16_t& y) const {
|
||||||
|
uint16_t minX, minY;
|
||||||
|
uint16_t cenX, cenY;
|
||||||
|
|
||||||
|
getMin(minX, minY);
|
||||||
|
getCenter(cenX, cenY);
|
||||||
|
|
||||||
|
x = cenX - minX;
|
||||||
|
y = cenY - minY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getRealMax(uint16_t& x, uint16_t& y) const {
|
||||||
|
uint16_t maxX, maxY;
|
||||||
|
uint16_t cenX, cenY;
|
||||||
|
|
||||||
|
getMax(maxX, maxY);
|
||||||
|
getCenter(cenX, cenY);
|
||||||
|
|
||||||
|
x = cenX + maxX;
|
||||||
|
y = cenY + maxY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void packCalib(uint8_t offset, uint16_t& x, uint16_t& y) const {
|
||||||
|
x = static_cast<uint16_t>(data[offset]) | ((data[offset + 1] & 0x0F) << 8);
|
||||||
|
y = static_cast<uint16_t>(data[offset + 2] << 4) | (data[offset + 1] >> 4);
|
||||||
|
}
|
||||||
|
} SwitchLeftCalibration;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t data[9];
|
||||||
|
|
||||||
|
void getMin(uint16_t& x, uint16_t& y) const { packCalib(3, x, y); }
|
||||||
|
void getCenter(uint16_t& x, uint16_t& y) const { packCalib(0, x, y); }
|
||||||
|
void getMax(uint16_t& x, uint16_t& y) const { packCalib(6, x, y); }
|
||||||
|
|
||||||
|
void getRealMin(uint16_t& x, uint16_t& y) const {
|
||||||
|
uint16_t minX, minY;
|
||||||
|
uint16_t cenX, cenY;
|
||||||
|
|
||||||
|
getMin(minX, minY);
|
||||||
|
getCenter(cenX, cenY);
|
||||||
|
|
||||||
|
x = cenX - minX;
|
||||||
|
y = cenY - minY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getRealMax(uint16_t& x, uint16_t& y) const {
|
||||||
|
uint16_t maxX, maxY;
|
||||||
|
uint16_t cenX, cenY;
|
||||||
|
|
||||||
|
getMax(maxX, maxY);
|
||||||
|
getCenter(cenX, cenY);
|
||||||
|
|
||||||
|
x = cenX + maxX;
|
||||||
|
y = cenY + maxY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void packCalib(uint8_t offset, uint16_t& x, uint16_t& y) const {
|
||||||
|
x = static_cast<uint16_t>(data[offset]) | ((data[offset + 1] & 0x0F) << 8);
|
||||||
|
y = static_cast<uint16_t>(data[offset + 2] << 4) | (data[offset + 1] >> 4);
|
||||||
|
}
|
||||||
|
} SwitchRightCalibration;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t red;
|
||||||
|
uint8_t green;
|
||||||
|
uint8_t blue;
|
||||||
|
} SwitchColorDefinition;
|
||||||
|
|
||||||
|
typedef struct __attribute((packed, aligned(1)))
|
||||||
|
{
|
||||||
|
uint8_t serialNumber[16];
|
||||||
|
|
||||||
|
uint8_t unknown00[2];
|
||||||
|
|
||||||
|
uint8_t deviceType;
|
||||||
|
|
||||||
|
uint8_t unknown01; // usually 0xA0
|
||||||
|
|
||||||
|
uint8_t unknown02[7];
|
||||||
|
|
||||||
|
uint8_t colorInfo; // 0 = default colors
|
||||||
|
|
||||||
|
uint8_t unknown03[4];
|
||||||
|
|
||||||
|
uint8_t motionCalibration[24];
|
||||||
|
|
||||||
|
uint8_t unknown04[5];
|
||||||
|
|
||||||
|
SwitchLeftCalibration leftStickCalibration;
|
||||||
|
|
||||||
|
SwitchRightCalibration rightStickCalibration;
|
||||||
|
|
||||||
|
uint8_t unknown08;
|
||||||
|
|
||||||
|
SwitchColorDefinition bodyColor;
|
||||||
|
|
||||||
|
SwitchColorDefinition buttonColor;
|
||||||
|
|
||||||
|
SwitchColorDefinition leftGripColor;
|
||||||
|
|
||||||
|
SwitchColorDefinition rightGripColor;
|
||||||
|
|
||||||
|
uint8_t unknown06[37];
|
||||||
|
|
||||||
|
uint8_t motionHorizontalOffsets[6];
|
||||||
|
|
||||||
|
uint8_t stickParams1[17];
|
||||||
|
|
||||||
|
uint8_t stickParams2[17];
|
||||||
|
|
||||||
|
uint8_t unknown07[0xE57];
|
||||||
|
} SwitchFactoryConfig;
|
||||||
|
|
||||||
|
typedef struct __attribute((packed, aligned(1)))
|
||||||
|
{
|
||||||
|
uint8_t unknown00[16];
|
||||||
|
|
||||||
|
uint8_t leftCalibrationMagic[2];
|
||||||
|
SwitchLeftCalibration leftCalibration;
|
||||||
|
|
||||||
|
uint8_t rightCalibrationMagic[2];
|
||||||
|
SwitchRightCalibration rightCalibration;
|
||||||
|
|
||||||
|
uint8_t motionCalibrationMagic[2];
|
||||||
|
uint8_t motionCalibration[24];
|
||||||
|
} SwitchUserCalibration;
|
||||||
|
|
||||||
|
typedef struct __attribute((packed, aligned(1)))
|
||||||
|
{
|
||||||
|
uint8_t connectionInfo : 4;
|
||||||
|
uint8_t batteryLevel : 4;
|
||||||
|
|
||||||
|
// byte 00
|
||||||
|
uint8_t buttonY : 1;
|
||||||
|
uint8_t buttonX : 1;
|
||||||
|
uint8_t buttonB : 1;
|
||||||
|
uint8_t buttonA : 1;
|
||||||
|
uint8_t buttonRightSR : 1;
|
||||||
|
uint8_t buttonRightSL : 1;
|
||||||
|
uint8_t buttonR : 1;
|
||||||
|
uint8_t buttonZR : 1;
|
||||||
|
|
||||||
|
// byte 01
|
||||||
|
uint8_t buttonMinus : 1;
|
||||||
|
uint8_t buttonPlus : 1;
|
||||||
|
uint8_t buttonThumbR : 1;
|
||||||
|
uint8_t buttonThumbL : 1;
|
||||||
|
uint8_t buttonHome : 1;
|
||||||
|
uint8_t buttonCapture : 1;
|
||||||
|
uint8_t dummy : 1;
|
||||||
|
uint8_t chargingGrip : 1;
|
||||||
|
|
||||||
|
// byte 02
|
||||||
|
uint8_t dpadDown : 1;
|
||||||
|
uint8_t dpadUp : 1;
|
||||||
|
uint8_t dpadRight : 1;
|
||||||
|
uint8_t dpadLeft : 1;
|
||||||
|
uint8_t buttonLeftSL : 1;
|
||||||
|
uint8_t buttonLeftSR : 1;
|
||||||
|
uint8_t buttonL : 1;
|
||||||
|
uint8_t buttonZL : 1;
|
||||||
|
|
||||||
|
SwitchAnalog leftStick;
|
||||||
|
SwitchAnalog rightStick;
|
||||||
|
} SwitchInputReport;
|
||||||
|
|
||||||
|
typedef struct __attribute((packed, aligned(1)))
|
||||||
|
{
|
||||||
|
uint8_t reportID;
|
||||||
|
uint8_t timestamp;
|
||||||
|
SwitchInputReport inputs;
|
||||||
|
uint8_t rumbleReport;
|
||||||
|
uint8_t imuData[36];
|
||||||
|
uint8_t padding[15];
|
||||||
|
} SwitchProReport;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SWITCH_TYPE_LEFT_JOYCON = 0x01,
|
||||||
|
SWITCH_TYPE_RIGHT_JOYCON,
|
||||||
|
SWITCH_TYPE_PRO_CONTROLLER,
|
||||||
|
SWITCH_TYPE_FAMICOM_LEFT_JOYCON = 0x07,
|
||||||
|
SWITCH_TYPE_FAMICOM_RIGHT_JOYCON = 0x08,
|
||||||
|
SWITCH_TYPE_NES_LEFT_JOYCON = 0x09,
|
||||||
|
SWITCH_TYPE_NES_RIGHT_JOYCON = 0x0A,
|
||||||
|
SWITCH_TYPE_SNES = 0x0B,
|
||||||
|
SWITCH_TYPE_N64 = 0x0C,
|
||||||
|
} SwitchControllerType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t majorVersion;
|
||||||
|
uint8_t minorVersion;
|
||||||
|
uint8_t controllerType;
|
||||||
|
uint8_t unknown00;
|
||||||
|
uint8_t macAddress[6];
|
||||||
|
uint8_t unknown01;
|
||||||
|
uint8_t storedColors;
|
||||||
|
} SwitchDeviceInfo;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t buttons;
|
||||||
|
uint8_t hat;
|
||||||
|
uint8_t lx;
|
||||||
|
uint8_t ly;
|
||||||
|
uint8_t rx;
|
||||||
|
uint8_t ry;
|
||||||
|
} SwitchProOutReport;
|
||||||
|
|
||||||
|
static const uint8_t switch_pro_string_language[] = { 0x09, 0x04 };
|
||||||
|
static const uint8_t switch_pro_string_manufacturer[] = "Open Stick Community";
|
||||||
|
static const uint8_t switch_pro_string_product[] = "GP2040-CE (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_manufacturer,
|
||||||
|
switch_pro_string_product,
|
||||||
|
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
|
||||||
|
0x10, 0x02, // bcdDevice 4.10
|
||||||
|
0x01, // iManufacturer (String Index)
|
||||||
|
0x02, // iProduct (String Index)
|
||||||
|
0x03, // iSerialNumber (String Index)
|
||||||
|
0x01, // bNumConfigurations 1
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t switch_pro_hid_descriptor[] =
|
||||||
|
{
|
||||||
|
0x09, // bLength
|
||||||
|
0x21, // bDescriptorType (HID)
|
||||||
|
0x11, 0x01, // bcdHID 1.11
|
||||||
|
0x00, // bCountryCode
|
||||||
|
0x01, // bNumDescriptors
|
||||||
|
0x22, // bDescriptorType[0] (HID)
|
||||||
|
0xCB, 0x00, // wDescriptorLength[0] 86
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
0x05, // 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
|
||||||
|
};
|
||||||
663
switch_pro_driver.cpp
Normal file
663
switch_pro_driver.cpp
Normal file
|
|
@ -0,0 +1,663 @@
|
||||||
|
#include "switch_pro_driver.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include <map>
|
||||||
|
#include "pico/rand.h"
|
||||||
|
#include "pico/time.h"
|
||||||
|
#include "tusb.h"
|
||||||
|
|
||||||
|
// force a report to be sent every X ms
|
||||||
|
#define SWITCH_PRO_KEEPALIVE_TIMER 5
|
||||||
|
|
||||||
|
static SwitchInputState g_input_state{
|
||||||
|
false, false, false, false,
|
||||||
|
false, false, false, false, false, false, false, false,
|
||||||
|
false, false, false, false, false, false,
|
||||||
|
SWITCH_PRO_JOYSTICK_MID, SWITCH_PRO_JOYSTICK_MID,
|
||||||
|
SWITCH_PRO_JOYSTICK_MID, SWITCH_PRO_JOYSTICK_MID};
|
||||||
|
|
||||||
|
static uint8_t report_buffer[SWITCH_PRO_ENDPOINT_SIZE] = {};
|
||||||
|
static uint8_t last_report[SWITCH_PRO_ENDPOINT_SIZE] = {};
|
||||||
|
static SwitchProReport switch_report{};
|
||||||
|
static uint8_t last_report_counter = 0;
|
||||||
|
static uint32_t last_report_timer = 0;
|
||||||
|
static bool is_ready = false;
|
||||||
|
static bool is_initialized = false;
|
||||||
|
static bool is_report_queued = false;
|
||||||
|
static bool report_sent = false;
|
||||||
|
static uint8_t queued_report_id = 0;
|
||||||
|
static uint8_t handshake_counter = 0;
|
||||||
|
|
||||||
|
static SwitchDeviceInfo device_info{};
|
||||||
|
static uint8_t player_id = 0;
|
||||||
|
static uint8_t input_mode = 0x30;
|
||||||
|
static bool is_imu_enabled = false;
|
||||||
|
static bool is_vibration_enabled = false;
|
||||||
|
|
||||||
|
static uint16_t leftMinX, leftMinY;
|
||||||
|
static uint16_t leftCenX, leftCenY;
|
||||||
|
static uint16_t leftMaxX, leftMaxY;
|
||||||
|
static uint16_t rightMinX, rightMinY;
|
||||||
|
static uint16_t rightCenX, rightCenY;
|
||||||
|
static uint16_t rightMaxX, rightMaxY;
|
||||||
|
|
||||||
|
static const uint8_t factory_config_data[0xEFF] = {
|
||||||
|
// serial number
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
|
||||||
|
0xFF, 0xFF,
|
||||||
|
|
||||||
|
// device type
|
||||||
|
SWITCH_TYPE_PRO_CONTROLLER,
|
||||||
|
|
||||||
|
// unknown
|
||||||
|
0xA0,
|
||||||
|
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
|
||||||
|
// color options
|
||||||
|
0x02,
|
||||||
|
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
|
||||||
|
// config & calibration 1
|
||||||
|
0xE3, 0xFF, 0x39, 0xFF, 0xED, 0x01, 0x00, 0x40,
|
||||||
|
0x00, 0x40, 0x00, 0x40, 0x09, 0x00, 0xEA, 0xFF,
|
||||||
|
0xA1, 0xFF, 0x3B, 0x34, 0x3B, 0x34, 0x3B, 0x34,
|
||||||
|
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
|
||||||
|
// config & calibration 2
|
||||||
|
// left stick
|
||||||
|
0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, 0xa4, 0x46,
|
||||||
|
0x6a,
|
||||||
|
|
||||||
|
// right stick
|
||||||
|
0x00, 0x08, 0x80, 0xa4, 0x46, 0x6a, 0xa4, 0x46,
|
||||||
|
0x6a,
|
||||||
|
|
||||||
|
0xFF,
|
||||||
|
|
||||||
|
// body color
|
||||||
|
0x1B, 0x1B, 0x1D,
|
||||||
|
|
||||||
|
// button color
|
||||||
|
0xFF, 0xFF, 0xFF,
|
||||||
|
|
||||||
|
// left grip color
|
||||||
|
0xEC, 0x00, 0x8C,
|
||||||
|
|
||||||
|
// right grip color
|
||||||
|
0xEC, 0x00, 0x8C,
|
||||||
|
|
||||||
|
0x01,
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
0x50, 0xFD, 0x00, 0x00, 0xC6, 0x0F,
|
||||||
|
0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14,
|
||||||
|
0x54, 0x41, 0x15, 0x54, 0xC7, 0x79, 0x9C, 0x33,
|
||||||
|
0x36, 0x63,
|
||||||
|
|
||||||
|
0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14,
|
||||||
|
0x54, 0x41, 0x15, 0x54,
|
||||||
|
|
||||||
|
0xC7,
|
||||||
|
|
||||||
|
0x79,
|
||||||
|
|
||||||
|
0x9C,
|
||||||
|
|
||||||
|
0x33,
|
||||||
|
|
||||||
|
0x36,
|
||||||
|
|
||||||
|
0x63, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t user_calibration_data[0x3F] = {
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
|
||||||
|
// Left Stick
|
||||||
|
0xB2, 0xA1, 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80,
|
||||||
|
0xa4, 0x46, 0x6a,
|
||||||
|
|
||||||
|
// Right Stick
|
||||||
|
0xB2, 0xA1, 0x00, 0x08, 0x80, 0xa4, 0x46, 0x6a,
|
||||||
|
0xa4, 0x46, 0x6a,
|
||||||
|
|
||||||
|
// Motion
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||||
|
};
|
||||||
|
|
||||||
|
static const SwitchFactoryConfig* factory_config = reinterpret_cast<const SwitchFactoryConfig*>(factory_config_data);
|
||||||
|
static const SwitchUserCalibration* user_calibration [[maybe_unused]] = reinterpret_cast<const SwitchUserCalibration*>(user_calibration_data);
|
||||||
|
|
||||||
|
static std::map<uint32_t, const uint8_t*> spi_flash_data = {
|
||||||
|
{0x6000, factory_config_data},
|
||||||
|
{0x8000, user_calibration_data}
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline uint16_t scale16To12(uint16_t pos) { return pos >> 4; }
|
||||||
|
|
||||||
|
static SwitchInputState make_neutral_state() {
|
||||||
|
SwitchInputState s{};
|
||||||
|
s.lx = SWITCH_PRO_JOYSTICK_MID;
|
||||||
|
s.ly = SWITCH_PRO_JOYSTICK_MID;
|
||||||
|
s.rx = SWITCH_PRO_JOYSTICK_MID;
|
||||||
|
s.ry = SWITCH_PRO_JOYSTICK_MID;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_identify() {
|
||||||
|
memset(report_buffer, 0x00, sizeof(report_buffer));
|
||||||
|
report_buffer[0] = REPORT_USB_INPUT_81;
|
||||||
|
report_buffer[1] = IDENTIFY;
|
||||||
|
report_buffer[2] = 0x00;
|
||||||
|
report_buffer[3] = device_info.controllerType;
|
||||||
|
for (uint8_t i = 0; i < 6; i++) {
|
||||||
|
report_buffer[4 + i] = device_info.macAddress[5 - i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool send_report(uint8_t reportID, const void* reportData, uint16_t reportLength) {
|
||||||
|
bool result = tud_hid_report(reportID, reportData, reportLength);
|
||||||
|
if (last_report_counter < 255) {
|
||||||
|
last_report_counter++;
|
||||||
|
} else {
|
||||||
|
last_report_counter = 0;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_spi_flash(uint8_t* dest, uint32_t address, uint8_t size) {
|
||||||
|
uint32_t addressBank = address & 0xFFFFFF00;
|
||||||
|
uint32_t addressOffset = address & 0x000000FF;
|
||||||
|
auto it = spi_flash_data.find(addressBank);
|
||||||
|
|
||||||
|
if (it != spi_flash_data.end()) {
|
||||||
|
const uint8_t* data = it->second;
|
||||||
|
memcpy(dest, data + addressOffset, size);
|
||||||
|
} else {
|
||||||
|
memset(dest, 0xFF, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSubID, const uint8_t *reportData, uint16_t reportLength) {
|
||||||
|
bool canSend = false;
|
||||||
|
|
||||||
|
switch (switchReportSubID) {
|
||||||
|
case IDENTIFY:
|
||||||
|
send_identify();
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case HANDSHAKE:
|
||||||
|
report_buffer[0] = REPORT_USB_INPUT_81;
|
||||||
|
report_buffer[1] = HANDSHAKE;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case BAUD_RATE:
|
||||||
|
report_buffer[0] = REPORT_USB_INPUT_81;
|
||||||
|
report_buffer[1] = BAUD_RATE;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case DISABLE_USB_TIMEOUT:
|
||||||
|
report_buffer[0] = REPORT_OUTPUT_30;
|
||||||
|
report_buffer[1] = switchReportSubID;
|
||||||
|
//if (handshakeCounter < 4) {
|
||||||
|
// handshakeCounter++;
|
||||||
|
//} else {
|
||||||
|
is_ready = true;
|
||||||
|
//}
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case ENABLE_USB_TIMEOUT:
|
||||||
|
report_buffer[0] = REPORT_OUTPUT_30;
|
||||||
|
report_buffer[1] = switchReportSubID;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
report_buffer[0] = REPORT_OUTPUT_30;
|
||||||
|
report_buffer[1] = switchReportSubID;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canSend) is_report_queued = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSubID, const uint8_t *reportData, uint16_t reportLength) {
|
||||||
|
uint8_t commandID = reportData[10];
|
||||||
|
uint32_t spiReadAddress = 0;
|
||||||
|
uint8_t spiReadSize = 0;
|
||||||
|
bool canSend = false;
|
||||||
|
|
||||||
|
report_buffer[0] = REPORT_OUTPUT_21;
|
||||||
|
report_buffer[1] = last_report_counter;
|
||||||
|
memcpy(report_buffer + 2, &switch_report.inputs, sizeof(SwitchInputReport));
|
||||||
|
|
||||||
|
switch (commandID) {
|
||||||
|
case GET_CONTROLLER_STATE:
|
||||||
|
report_buffer[13] = 0x80;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
report_buffer[15] = 0x03;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case BLUETOOTH_PAIR_REQUEST:
|
||||||
|
report_buffer[13] = 0x81;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
report_buffer[15] = 0x03;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case REQUEST_DEVICE_INFO:
|
||||||
|
report_buffer[13] = 0x82;
|
||||||
|
report_buffer[14] = 0x02;
|
||||||
|
memcpy(&report_buffer[15], &device_info, sizeof(device_info));
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case SET_MODE:
|
||||||
|
input_mode = reportData[11];
|
||||||
|
report_buffer[13] = 0x80;
|
||||||
|
report_buffer[14] = 0x03;
|
||||||
|
report_buffer[15] = input_mode;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case TRIGGER_BUTTONS:
|
||||||
|
report_buffer[13] = 0x83;
|
||||||
|
report_buffer[14] = 0x04;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case SET_SHIPMENT:
|
||||||
|
report_buffer[13] = 0x80;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case SPI_READ:
|
||||||
|
spiReadAddress = (reportData[14] << 24) | (reportData[13] << 16) | (reportData[12] << 8) | (reportData[11]);
|
||||||
|
spiReadSize = reportData[15];
|
||||||
|
report_buffer[13] = 0x90;
|
||||||
|
report_buffer[14] = reportData[10];
|
||||||
|
report_buffer[15] = reportData[11];
|
||||||
|
report_buffer[16] = reportData[12];
|
||||||
|
report_buffer[17] = reportData[13];
|
||||||
|
report_buffer[18] = reportData[14];
|
||||||
|
report_buffer[19] = reportData[15];
|
||||||
|
read_spi_flash(&report_buffer[20], spiReadAddress, spiReadSize);
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case SET_NFC_IR_CONFIG:
|
||||||
|
report_buffer[13] = 0x80;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case SET_NFC_IR_STATE:
|
||||||
|
report_buffer[13] = 0x80;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case SET_PLAYER_LIGHTS:
|
||||||
|
player_id = reportData[11];
|
||||||
|
report_buffer[13] = 0x80;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case GET_PLAYER_LIGHTS:
|
||||||
|
player_id = reportData[11];
|
||||||
|
report_buffer[13] = 0xB0;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
report_buffer[15] = player_id;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case COMMAND_UNKNOWN_33:
|
||||||
|
report_buffer[13] = 0x80;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
report_buffer[15] = 0x03;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case SET_HOME_LIGHT:
|
||||||
|
report_buffer[13] = 0x80;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
report_buffer[15] = 0x00;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case TOGGLE_IMU:
|
||||||
|
is_imu_enabled = reportData[11];
|
||||||
|
report_buffer[13] = 0x80;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
report_buffer[15] = 0x00;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case IMU_SENSITIVITY:
|
||||||
|
report_buffer[13] = 0x80;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case ENABLE_VIBRATION:
|
||||||
|
is_vibration_enabled = reportData[11];
|
||||||
|
report_buffer[13] = 0x80;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
report_buffer[15] = 0x00;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case READ_IMU:
|
||||||
|
report_buffer[13] = 0xC0;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
report_buffer[15] = reportData[11];
|
||||||
|
report_buffer[16] = reportData[12];
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
case GET_VOLTAGE:
|
||||||
|
report_buffer[13] = 0xD0;
|
||||||
|
report_buffer[14] = 0x50;
|
||||||
|
report_buffer[15] = 0x83;
|
||||||
|
report_buffer[16] = 0x06;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
report_buffer[13] = 0x80;
|
||||||
|
report_buffer[14] = commandID;
|
||||||
|
report_buffer[15] = 0x03;
|
||||||
|
canSend = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canSend) is_report_queued = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_switch_report_from_state() {
|
||||||
|
switch_report.inputs.dpadUp = g_input_state.dpad_up;
|
||||||
|
switch_report.inputs.dpadDown = g_input_state.dpad_down;
|
||||||
|
switch_report.inputs.dpadLeft = g_input_state.dpad_left;
|
||||||
|
switch_report.inputs.dpadRight = g_input_state.dpad_right;
|
||||||
|
|
||||||
|
switch_report.inputs.chargingGrip = 1;
|
||||||
|
|
||||||
|
switch_report.inputs.buttonY = g_input_state.button_y;
|
||||||
|
switch_report.inputs.buttonX = g_input_state.button_x;
|
||||||
|
switch_report.inputs.buttonB = g_input_state.button_b;
|
||||||
|
switch_report.inputs.buttonA = g_input_state.button_a;
|
||||||
|
switch_report.inputs.buttonRightSR = 0;
|
||||||
|
switch_report.inputs.buttonRightSL = 0;
|
||||||
|
switch_report.inputs.buttonR = g_input_state.button_r;
|
||||||
|
switch_report.inputs.buttonZR = g_input_state.button_zr;
|
||||||
|
switch_report.inputs.buttonMinus = g_input_state.button_minus;
|
||||||
|
switch_report.inputs.buttonPlus = g_input_state.button_plus;
|
||||||
|
switch_report.inputs.buttonThumbR = g_input_state.button_r3;
|
||||||
|
switch_report.inputs.buttonThumbL = g_input_state.button_l3;
|
||||||
|
switch_report.inputs.buttonHome = g_input_state.button_home;
|
||||||
|
switch_report.inputs.buttonCapture = g_input_state.button_capture;
|
||||||
|
switch_report.inputs.buttonLeftSR = 0;
|
||||||
|
switch_report.inputs.buttonLeftSL = 0;
|
||||||
|
switch_report.inputs.buttonL = g_input_state.button_l;
|
||||||
|
switch_report.inputs.buttonZL = g_input_state.button_zl;
|
||||||
|
|
||||||
|
uint16_t scaleLeftStickX = scale16To12(g_input_state.lx);
|
||||||
|
uint16_t scaleLeftStickY = scale16To12(g_input_state.ly);
|
||||||
|
uint16_t scaleRightStickX = scale16To12(g_input_state.rx);
|
||||||
|
uint16_t scaleRightStickY = scale16To12(g_input_state.ry);
|
||||||
|
|
||||||
|
switch_report.inputs.leftStick.setX(std::min(std::max(scaleLeftStickX,leftMinX), leftMaxX));
|
||||||
|
switch_report.inputs.leftStick.setY(-std::min(std::max(scaleLeftStickY,leftMinY), leftMaxY));
|
||||||
|
switch_report.inputs.rightStick.setX(std::min(std::max(scaleRightStickX,rightMinX), rightMaxX));
|
||||||
|
switch_report.inputs.rightStick.setY(-std::min(std::max(scaleRightStickY,rightMinY), rightMaxY));
|
||||||
|
|
||||||
|
switch_report.rumbleReport = 0x09;
|
||||||
|
}
|
||||||
|
|
||||||
|
void switch_pro_init() {
|
||||||
|
player_id = 0;
|
||||||
|
last_report_counter = 0;
|
||||||
|
handshake_counter = 0;
|
||||||
|
is_ready = false;
|
||||||
|
is_initialized = false;
|
||||||
|
is_report_queued = false;
|
||||||
|
report_sent = false;
|
||||||
|
|
||||||
|
device_info = {
|
||||||
|
.majorVersion = 0x04,
|
||||||
|
.minorVersion = 0x91,
|
||||||
|
.controllerType = SWITCH_TYPE_PRO_CONTROLLER,
|
||||||
|
.unknown00 = 0x02,
|
||||||
|
.macAddress = {0x7c, 0xbb, 0x8a, static_cast<uint8_t>(get_rand_32() % 0xff), static_cast<uint8_t>(get_rand_32() % 0xff), static_cast<uint8_t>(get_rand_32() % 0xff)},
|
||||||
|
.unknown01 = 0x01,
|
||||||
|
.storedColors = 0x02,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch_report = {
|
||||||
|
.reportID = 0x30,
|
||||||
|
.timestamp = 0,
|
||||||
|
|
||||||
|
.inputs {
|
||||||
|
.connectionInfo = 0,
|
||||||
|
.batteryLevel = 0x08,
|
||||||
|
|
||||||
|
.buttonY = 0,
|
||||||
|
.buttonX = 0,
|
||||||
|
.buttonB = 0,
|
||||||
|
.buttonA = 0,
|
||||||
|
.buttonRightSR = 0,
|
||||||
|
.buttonRightSL = 0,
|
||||||
|
.buttonR = 0,
|
||||||
|
.buttonZR = 0,
|
||||||
|
|
||||||
|
.buttonMinus = 0,
|
||||||
|
.buttonPlus = 0,
|
||||||
|
.buttonThumbR = 0,
|
||||||
|
.buttonThumbL = 0,
|
||||||
|
.buttonHome = 0,
|
||||||
|
.buttonCapture = 0,
|
||||||
|
.dummy = 0,
|
||||||
|
.chargingGrip = 0,
|
||||||
|
|
||||||
|
.dpadDown = 0,
|
||||||
|
.dpadUp = 0,
|
||||||
|
.dpadRight = 0,
|
||||||
|
.dpadLeft = 0,
|
||||||
|
.buttonLeftSL = 0,
|
||||||
|
.buttonLeftSR = 0,
|
||||||
|
.buttonL = 0,
|
||||||
|
.buttonZL = 0,
|
||||||
|
.leftStick = {0xFF, 0xF7, 0x7F},
|
||||||
|
.rightStick = {0xFF, 0xF7, 0x7F},
|
||||||
|
},
|
||||||
|
.rumbleReport = 0,
|
||||||
|
.imuData = {0x00},
|
||||||
|
.padding = {0x00}
|
||||||
|
};
|
||||||
|
|
||||||
|
last_report_timer = to_ms_since_boot(get_absolute_time());
|
||||||
|
|
||||||
|
factory_config->leftStickCalibration.getRealMin(leftMinX, leftMinY);
|
||||||
|
factory_config->leftStickCalibration.getCenter(leftCenX, leftCenY);
|
||||||
|
factory_config->leftStickCalibration.getRealMax(leftMaxX, leftMaxY);
|
||||||
|
factory_config->rightStickCalibration.getRealMin(rightMinX, rightMinY);
|
||||||
|
factory_config->rightStickCalibration.getCenter(rightCenX, rightCenY);
|
||||||
|
factory_config->rightStickCalibration.getRealMax(rightMaxX, rightMaxY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void switch_pro_set_input(const SwitchInputState& state) {
|
||||||
|
g_input_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void switch_pro_task() {
|
||||||
|
uint32_t now = to_ms_since_boot(get_absolute_time());
|
||||||
|
report_sent = false;
|
||||||
|
|
||||||
|
update_switch_report_from_state();
|
||||||
|
|
||||||
|
if (tud_suspended()) {
|
||||||
|
tud_remote_wakeup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_report_queued) {
|
||||||
|
if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) {
|
||||||
|
if (tud_hid_ready() && send_report(queued_report_id, report_buffer, 64) == true ) {
|
||||||
|
}
|
||||||
|
is_report_queued = false;
|
||||||
|
last_report_timer = now;
|
||||||
|
}
|
||||||
|
report_sent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_ready && !report_sent) {
|
||||||
|
if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) {
|
||||||
|
switch_report.timestamp = last_report_counter;
|
||||||
|
void * inputReport = &switch_report;
|
||||||
|
uint16_t report_size = sizeof(switch_report);
|
||||||
|
if (memcmp(last_report, inputReport, report_size) != 0) {
|
||||||
|
if (tud_hid_ready() && send_report(0, inputReport, report_size) == true ) {
|
||||||
|
memcpy(last_report, inputReport, report_size);
|
||||||
|
report_sent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_report_timer = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!is_initialized) {
|
||||||
|
send_identify();
|
||||||
|
if (tud_hid_ready() && tud_hid_report(0, report_buffer, 64) == true) {
|
||||||
|
is_initialized = true;
|
||||||
|
report_sent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_report_timer = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length) {
|
||||||
|
// Packet format: 0xAA, buttons(2 LE), hat, lx, ly, rx, ry
|
||||||
|
if (length < 8 || packet[0] != 0xAA) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchProOutReport out{};
|
||||||
|
out.buttons = static_cast<uint16_t>(packet[1]) | (static_cast<uint16_t>(packet[2]) << 8);
|
||||||
|
out.hat = packet[3];
|
||||||
|
out.lx = packet[4];
|
||||||
|
out.ly = packet[5];
|
||||||
|
out.rx = packet[6];
|
||||||
|
out.ry = packet[7];
|
||||||
|
|
||||||
|
auto expand_axis = [](uint8_t v) -> uint16_t {
|
||||||
|
return static_cast<uint16_t>(v) << 8 | v;
|
||||||
|
};
|
||||||
|
|
||||||
|
SwitchInputState state = make_neutral_state();
|
||||||
|
|
||||||
|
switch (out.hat) {
|
||||||
|
case SWITCH_PRO_HAT_UP: state.dpad_up = true; break;
|
||||||
|
case SWITCH_PRO_HAT_UPRIGHT: state.dpad_up = true; state.dpad_right = true; break;
|
||||||
|
case SWITCH_PRO_HAT_RIGHT: state.dpad_right = true; break;
|
||||||
|
case SWITCH_PRO_HAT_DOWNRIGHT: state.dpad_down = true; state.dpad_right = true; break;
|
||||||
|
case SWITCH_PRO_HAT_DOWN: state.dpad_down = true; break;
|
||||||
|
case SWITCH_PRO_HAT_DOWNLEFT: state.dpad_down = true; state.dpad_left = true; break;
|
||||||
|
case SWITCH_PRO_HAT_LEFT: state.dpad_left = true; break;
|
||||||
|
case SWITCH_PRO_HAT_UPLEFT: state.dpad_up = true; state.dpad_left = true; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.button_y = out.buttons & SWITCH_PRO_MASK_Y;
|
||||||
|
state.button_x = out.buttons & SWITCH_PRO_MASK_X;
|
||||||
|
state.button_b = out.buttons & SWITCH_PRO_MASK_B;
|
||||||
|
state.button_a = out.buttons & SWITCH_PRO_MASK_A;
|
||||||
|
state.button_r = out.buttons & SWITCH_PRO_MASK_R;
|
||||||
|
state.button_zr = out.buttons & SWITCH_PRO_MASK_ZR;
|
||||||
|
state.button_plus = out.buttons & SWITCH_PRO_MASK_PLUS;
|
||||||
|
state.button_minus = out.buttons & SWITCH_PRO_MASK_MINUS;
|
||||||
|
state.button_r3 = out.buttons & SWITCH_PRO_MASK_R3;
|
||||||
|
state.button_l3 = out.buttons & SWITCH_PRO_MASK_L3;
|
||||||
|
state.button_home = out.buttons & SWITCH_PRO_MASK_HOME;
|
||||||
|
state.button_capture = out.buttons & SWITCH_PRO_MASK_CAPTURE;
|
||||||
|
state.button_zl = out.buttons & SWITCH_PRO_MASK_ZL;
|
||||||
|
state.button_l = out.buttons & SWITCH_PRO_MASK_L;
|
||||||
|
|
||||||
|
state.lx = expand_axis(out.lx);
|
||||||
|
state.ly = expand_axis(out.ly);
|
||||||
|
state.rx = expand_axis(out.rx);
|
||||||
|
state.ry = expand_axis(out.ry);
|
||||||
|
|
||||||
|
switch_pro_set_input(state);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HID callbacks
|
||||||
|
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
|
||||||
|
(void)instance;
|
||||||
|
(void)report_id;
|
||||||
|
(void)report_type;
|
||||||
|
(void)buffer;
|
||||||
|
(void)reqlen;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {
|
||||||
|
(void)instance;
|
||||||
|
if (report_type != HID_REPORT_TYPE_OUTPUT) return;
|
||||||
|
|
||||||
|
memset(report_buffer, 0x00, bufsize);
|
||||||
|
|
||||||
|
uint8_t switchReportID = buffer[0];
|
||||||
|
uint8_t switchReportSubID = buffer[1];
|
||||||
|
if (switchReportID == REPORT_OUTPUT_00) {
|
||||||
|
} else if (switchReportID == REPORT_FEATURE) {
|
||||||
|
queued_report_id = report_id;
|
||||||
|
handle_feature_report(switchReportID, switchReportSubID, buffer, bufsize);
|
||||||
|
} else if (switchReportID == REPORT_CONFIGURATION) {
|
||||||
|
queued_report_id = report_id;
|
||||||
|
handle_config_report(switchReportID, switchReportSubID, buffer, bufsize);
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) {
|
||||||
|
(void)itf;
|
||||||
|
return switch_pro_report_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t const * tud_descriptor_device_cb(void) {
|
||||||
|
return switch_pro_device_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t const * tud_descriptor_configuration_cb(uint8_t index) {
|
||||||
|
(void)index;
|
||||||
|
return switch_pro_configuration_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t desc_str[32];
|
||||||
|
|
||||||
|
uint16_t const * tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||||
|
(void)langid;
|
||||||
|
|
||||||
|
uint8_t chr_count;
|
||||||
|
|
||||||
|
if ( index == 0 ) {
|
||||||
|
memcpy(&desc_str[1], switch_pro_string_language, 2);
|
||||||
|
chr_count = 1;
|
||||||
|
} else {
|
||||||
|
if ( index >= sizeof(switch_pro_string_descriptors)/sizeof(switch_pro_string_descriptors[0]) ) return nullptr;
|
||||||
|
|
||||||
|
const uint8_t *str = switch_pro_string_descriptors[index];
|
||||||
|
|
||||||
|
chr_count = 0;
|
||||||
|
while ( str[chr_count] ) chr_count++;
|
||||||
|
if ( chr_count > 31 ) chr_count = 31;
|
||||||
|
|
||||||
|
for(uint8_t i=0; i<chr_count; i++) {
|
||||||
|
desc_str[1+i] = str[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
desc_str[0] = (uint16_t) ((0x03 << 8 ) | (2*chr_count + 2));
|
||||||
|
return desc_str;
|
||||||
|
}
|
||||||
50
switch_pro_driver.h
Normal file
50
switch_pro_driver.h
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Minimal Switch Pro controller emulation glue derived from the GP2040-CE
|
||||||
|
* SwitchProDriver. The driver keeps the same descriptors/handshake while
|
||||||
|
* exposing a simple API for feeding inputs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "switch_pro_descriptors.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool dpad_up;
|
||||||
|
bool dpad_down;
|
||||||
|
bool dpad_left;
|
||||||
|
bool dpad_right;
|
||||||
|
|
||||||
|
bool button_a;
|
||||||
|
bool button_b;
|
||||||
|
bool button_x;
|
||||||
|
bool button_y;
|
||||||
|
bool button_l;
|
||||||
|
bool button_r;
|
||||||
|
bool button_zl;
|
||||||
|
bool button_zr;
|
||||||
|
bool button_plus;
|
||||||
|
bool button_minus;
|
||||||
|
bool button_home;
|
||||||
|
bool button_capture;
|
||||||
|
bool button_l3;
|
||||||
|
bool button_r3;
|
||||||
|
|
||||||
|
uint16_t lx; // 0-65535
|
||||||
|
uint16_t ly;
|
||||||
|
uint16_t rx;
|
||||||
|
uint16_t ry;
|
||||||
|
} SwitchInputState;
|
||||||
|
|
||||||
|
// Initialize USB state and calibration before entering the main loop.
|
||||||
|
void switch_pro_init();
|
||||||
|
|
||||||
|
// Update the desired controller state for the next USB report.
|
||||||
|
void switch_pro_set_input(const SwitchInputState& state);
|
||||||
|
|
||||||
|
// Drive the Switch Pro USB state machine; call this frequently in the main loop.
|
||||||
|
void switch_pro_task();
|
||||||
|
|
||||||
|
// Convert a packed UART message into controller state (returns true if parsed).
|
||||||
|
bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length);
|
||||||
38
tusb_config.h
Normal file
38
tusb_config.h
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
// TinyUSB configuration tailored for a single Switch Pro style HID interface.
|
||||||
|
// Data is derived from TinyUSB examples and tuned for a 64-byte HID endpoint.
|
||||||
|
#ifndef _TUSB_CONFIG_H_
|
||||||
|
#define _TUSB_CONFIG_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED)
|
||||||
|
#ifndef CFG_TUSB_OS
|
||||||
|
#define CFG_TUSB_OS OPT_OS_NONE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUSB_MEM_SECTION
|
||||||
|
#define CFG_TUSB_MEM_SECTION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUSB_MEM_ALIGN
|
||||||
|
#define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||||
|
|
||||||
|
// Device class configuration
|
||||||
|
#define CFG_TUD_HID 1
|
||||||
|
#define CFG_TUD_CDC 0
|
||||||
|
#define CFG_TUD_MSC 0
|
||||||
|
#define CFG_TUD_MIDI 0
|
||||||
|
#define CFG_TUD_VENDOR 0
|
||||||
|
|
||||||
|
#define CFG_TUD_HID_EP_BUFSIZE 64
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // _TUSB_CONFIG_H_
|
||||||
Loading…
Add table
Add a link
Reference in a new issue