fix(native-usb): isolate two-pair transport and hand off read status in IRQ

This commit is contained in:
Joey Yakimowich-Payne 2026-09-17 20:24:32 -06:00
commit 9f8678af96
51 changed files with 8159 additions and 815 deletions

View file

@ -6,6 +6,20 @@ set(PICO_MBEDTLS_CONFIG_FILE "${SWITCH2_USB_PROBE_DIR}/mbedtls_config.h")
option(SWITCH2_PROBE_COMPOSITE
"Experiment: independent right and left Joy-Con 2 functions on one USB port" OFF)
option(SWITCH2_PROBE_HUB "Native R/L devices on the built-in SIO USB hub" OFF)
set(SWITCH2_PROBE_PAIR_COUNT "1" CACHE STRING "Native hub pair count: 1 or 2")
set_property(CACHE SWITCH2_PROBE_PAIR_COUNT PROPERTY STRINGS 1 2)
option(SWITCH2_PROBE_NEUTRAL_INPUT "Standalone hub transport experiment with neutral reports and no Bluetooth" OFF)
if(NOT "${SWITCH2_PROBE_PAIR_COUNT}" MATCHES "^[12]$")
message(FATAL_ERROR "SWITCH2_PROBE_PAIR_COUNT must be 1 or 2")
endif()
if(SWITCH2_PROBE_NEUTRAL_INPUT AND (NOT SWITCH2_PROBE_HUB OR SWITCH_PICO_SWITCH2_USB_BRIDGE))
message(FATAL_ERROR "Neutral transport requires the standalone native HUB, without the Bluetooth bridge")
endif()
if(SWITCH2_PROBE_PAIR_COUNT GREATER 1 AND
(NOT SWITCH2_PROBE_HUB OR
(NOT SWITCH2_PROBE_NEUTRAL_INPUT AND NOT SWITCH2_BRIDGE_FULL_INPUT)))
message(FATAL_ERROR "Two pairs require a native HUB with GAMEPAD/DUALSENSE input or explicit neutral transport")
endif()
if(SWITCH2_PROBE_HUB AND SWITCH2_PROBE_COMPOSITE)
message(FATAL_ERROR "Select native hub or composite, not both")
endif()
@ -24,15 +38,20 @@ else()
message(FATAL_ERROR "SWITCH2_PROBE_SIDE must be LEFT or RIGHT")
endif()
if(SWITCH2_PROBE_COMPOSITE OR SWITCH2_PROBE_HUB)
if(NOT SWITCH_PICO_SWITCH2_USB_BRIDGE OR SWITCH2_BRIDGE_WII_INPUT
OR (NOT SWITCH2_BRIDGE_INPUT STREQUAL "JOYCON2" AND NOT SWITCH2_BRIDGE_FULL_INPUT))
message(FATAL_ERROR "Native R/L output requires JOYCON2 input or a full-controller HUB source")
if(NOT SWITCH2_PROBE_NEUTRAL_INPUT AND
(NOT SWITCH_PICO_SWITCH2_USB_BRIDGE OR SWITCH2_BRIDGE_WII_INPUT
OR (NOT SWITCH2_BRIDGE_INPUT STREQUAL "JOYCON2" AND NOT SWITCH2_BRIDGE_FULL_INPUT)))
message(FATAL_ERROR "Native R/L output requires JOYCON2 input, a full-controller HUB source, or standalone neutral transport")
endif()
set(probe_composite 0)
if(SWITCH2_PROBE_COMPOSITE)
set(probe_composite 1)
endif()
set(probe_controller_count 2)
if(SWITCH2_PROBE_HUB)
math(EXPR probe_controller_count "2 * ${SWITCH2_PROBE_PAIR_COUNT}")
else()
set(probe_controller_count 2)
endif()
else()
set(probe_composite 0)
set(probe_controller_count 1)
@ -45,20 +64,35 @@ add_compile_definitions(
SWITCH2_PROBE_JOYCON_LEFT=${probe_joycon_left}
SWITCH2_PROBE_COMPOSITE=${probe_composite}
SWITCH2_PROBE_HUB=$<BOOL:${SWITCH2_PROBE_HUB}>
SWITCH2_PROBE_NEUTRAL_INPUT=$<BOOL:${SWITCH2_PROBE_NEUTRAL_INPUT}>
PROBE_CONTROLLER_COUNT=${probe_controller_count})
set(SWITCH2_BRIDGE_SOURCE_ADDRESS "" CACHE STRING
"Primary physical Bluetooth source address (xx:xx:xx:xx:xx:xx)")
set(SWITCH2_BRIDGE_SECOND_SOURCE_ADDRESS "" CACHE STRING
"Secondary left physical Bluetooth source address (xx:xx:xx:xx:xx:xx)")
"Second physical Joy-Con source, or Pair B full-gamepad source (empty selects auto)")
set(SWITCH2_BRIDGE_SOURCE_AUTO OFF)
set(SWITCH2_BRIDGE_SECOND_SOURCE_AUTO OFF)
if(SWITCH_PICO_SWITCH2_USB_BRIDGE OR SWITCH2_PROBE_COMPOSITE)
set(probe_source_fields SWITCH2_BRIDGE_SOURCE_ADDRESS)
if(SWITCH2_BRIDGE_FULL_INPUT AND SWITCH2_BRIDGE_SOURCE_ADDRESS STREQUAL "")
set(SWITCH2_BRIDGE_SOURCE_AUTO ON)
set(probe_source_fields "")
elseif((SWITCH2_PROBE_COMPOSITE OR SWITCH2_PROBE_HUB) AND NOT SWITCH2_BRIDGE_FULL_INPUT)
list(APPEND probe_source_fields SWITCH2_BRIDGE_SECOND_SOURCE_ADDRESS)
set(probe_source_fields "")
if(SWITCH2_BRIDGE_FULL_INPUT)
if(SWITCH2_BRIDGE_SOURCE_ADDRESS STREQUAL "")
set(SWITCH2_BRIDGE_SOURCE_AUTO ON)
else()
list(APPEND probe_source_fields SWITCH2_BRIDGE_SOURCE_ADDRESS)
endif()
if(probe_controller_count GREATER 2)
if(SWITCH2_BRIDGE_SECOND_SOURCE_ADDRESS STREQUAL "")
set(SWITCH2_BRIDGE_SECOND_SOURCE_AUTO ON)
else()
list(APPEND probe_source_fields SWITCH2_BRIDGE_SECOND_SOURCE_ADDRESS)
endif()
endif()
else()
list(APPEND probe_source_fields SWITCH2_BRIDGE_SOURCE_ADDRESS)
if(SWITCH2_PROBE_COMPOSITE OR SWITCH2_PROBE_HUB)
list(APPEND probe_source_fields SWITCH2_BRIDGE_SECOND_SOURCE_ADDRESS)
endif()
endif()
set(probe_source_addresses "")
foreach(field IN LISTS probe_source_fields)
@ -71,7 +105,7 @@ if(SWITCH_PICO_SWITCH2_USB_BRIDGE OR SWITCH2_PROBE_COMPOSITE)
message(FATAL_ERROR "Provide ${field} as a physical six-byte Bluetooth address")
endif()
if(source_address IN_LIST probe_source_addresses)
message(FATAL_ERROR "Composite physical source addresses must be distinct")
message(FATAL_ERROR "Physical source addresses must be distinct")
endif()
list(APPEND probe_source_addresses "${source_address}")
string(REPLACE ":" ",0x" ${field}_BYTES "${source_address}")
@ -79,6 +113,7 @@ if(SWITCH_PICO_SWITCH2_USB_BRIDGE OR SWITCH2_PROBE_COMPOSITE)
endforeach()
endif()
add_compile_definitions(SWITCH2_BRIDGE_SOURCE_AUTO=$<BOOL:${SWITCH2_BRIDGE_SOURCE_AUTO}>)
add_compile_definitions(SWITCH2_BRIDGE_SECOND_SOURCE_AUTO=$<BOOL:${SWITCH2_BRIDGE_SECOND_SOURCE_AUTO}>)
function(switch2_usb_probe_configure target)
set(probe_sources
@ -101,8 +136,8 @@ function(switch2_usb_probe_configure target)
option(SWITCH2_PROBE_TRACE_NATIVE_INPUT
"Trace one completed native USB input report per second without changing its contents" OFF)
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
if(NOT SWITCH_PICO_SWITCH2_USB_BRIDGE)
message(FATAL_ERROR "Native input tracing requires the Bluetooth USB bridge")
if(NOT SWITCH_PICO_SWITCH2_USB_BRIDGE AND NOT SWITCH2_PROBE_NEUTRAL_INPUT)
message(FATAL_ERROR "Native input tracing requires the Bluetooth bridge or neutral hub experiment")
endif()
target_compile_definitions(${target} PRIVATE SWITCH2_PROBE_TRACE_NATIVE_INPUT=1)
endif()
@ -161,7 +196,7 @@ function(switch2_usb_probe_configure target)
target_compile_definitions(${target} PRIVATE SWITCH2_PROBE_ZERO_NATIVE_IMU_PAYLOAD=1)
endif()
foreach(prefix IN ITEMS SWITCH2_PROBE SWITCH2_PROBE_SECOND)
foreach(prefix IN ITEMS SWITCH2_PROBE SWITCH2_PROBE_SECOND SWITCH2_PROBE_THIRD SWITCH2_PROBE_FOURTH)
set(${prefix}_IDENTITY_FILE "" CACHE FILEPATH "64-byte matching Joy-Con 2 factory-format identity block")
set(${prefix}_VERSION_FILE "" CACHE FILEPATH "Captured 12-byte matching Joy-Con 2 firmware-version reply")
set(${prefix}_CONTROLLER_ADDRESS "" CACHE STRING "Advertised controller address (captured or distinct virtual identity)")
@ -183,18 +218,25 @@ function(switch2_usb_probe_configure target)
target_compile_definitions(${target} PRIVATE SWITCH2_PROBE_USB_INIT=1)
endif()
if(SWITCH2_PROBE_NEUTRAL_INPUT AND NOT SWITCH2_PROBE_USB_INIT)
message(FATAL_ERROR "Neutral transport requires SWITCH2_PROBE_USB_INIT and verified identity/calibration captures")
endif()
set(probe_capture_prefixes SWITCH2_PROBE)
if(SWITCH2_PROBE_COMPOSITE OR SWITCH2_PROBE_HUB)
list(APPEND probe_capture_prefixes SWITCH2_PROBE_SECOND)
endif()
if(probe_controller_count EQUAL 4)
list(APPEND probe_capture_prefixes SWITCH2_PROBE_THIRD SWITCH2_PROBE_FOURTH)
endif()
set(identity_rows "")
set(status_rows "")
set(firmware_rows "")
set(factory_rows "")
set(user_calibration_rows "")
set(controller_addresses "")
set(controller_identities "")
foreach(prefix IN LISTS probe_capture_prefixes)
if(probe_joycon_left OR prefix STREQUAL "SWITCH2_PROBE_SECOND")
if(probe_joycon_left OR prefix STREQUAL "SWITCH2_PROBE_SECOND" OR prefix STREQUAL "SWITCH2_PROBE_FOURTH")
set(probe_model "Joy-Con 2 (L)")
set(probe_vid_pid "7e056720")
set(probe_firmware_type "00")
@ -206,7 +248,7 @@ function(switch2_usb_probe_configure target)
if(SWITCH2_PROBE_COMPOSITE OR SWITCH2_PROBE_HUB)
foreach(field IDENTITY_FILE VERSION_FILE FACTORY_FILE USER_CALIBRATION_FILE CONTROLLER_ADDRESS)
if(NOT ${prefix}_${field})
message(FATAL_ERROR "Composite ${probe_model} requires ${prefix}_${field}")
message(FATAL_ERROR "Native ${probe_model} requires ${prefix}_${field}")
endif()
endforeach()
endif()
@ -217,6 +259,10 @@ function(switch2_usb_probe_configure target)
message(FATAL_ERROR "${prefix}_IDENTITY_FILE must contain exactly 64 bytes")
endif()
string(TOLOWER "${identity_hex}" identity_hex)
if(identity_hex IN_LIST controller_identities)
message(FATAL_ERROR "Native child factory identities must be distinct")
endif()
list(APPEND controller_identities "${identity_hex}")
string(SUBSTRING "${identity_hex}" 36 8 identity_vid_pid)
if(NOT identity_vid_pid STREQUAL probe_vid_pid)
message(FATAL_ERROR "${prefix}_IDENTITY_FILE must match selected model ${probe_model}")
@ -247,7 +293,7 @@ function(switch2_usb_probe_configure target)
message(FATAL_ERROR "Provide ${prefix}_CONTROLLER_ADDRESS as a six-byte advertised Bluetooth address")
endif()
if(address_hex IN_LIST controller_addresses)
message(FATAL_ERROR "Composite advertised controller addresses must be distinct")
message(FATAL_ERROR "Advertised controller addresses must be distinct")
endif()
list(APPEND controller_addresses "${address_hex}")
set(address_reversed "")
@ -354,7 +400,9 @@ function(switch2_usb_probe_configure target)
endif()
pico_enable_stdio_usb(${target} 0)
pico_enable_stdio_uart(${target} 1)
if(SWITCH2_PROBE_HUB)
if(SWITCH2_PROBE_NEUTRAL_INPUT)
pico_set_program_name(${target} "Native Joy-Con 2 neutral transport experiment")
elseif(SWITCH2_PROBE_HUB)
pico_set_program_name(${target} "Native Joy-Con 2 R and L stock USB hub bridge")
elseif(SWITCH2_PROBE_COMPOSITE)
pico_set_program_name(${target} "Switch 2 right and left Joy-Con composite bridge")
@ -367,17 +415,29 @@ function(switch2_usb_probe_configure target)
else()
pico_set_program_name(${target} "Switch 2 USB initialization capture")
endif()
if(SWITCH2_PROBE_HUB AND SWITCH2_BRIDGE_FULL_INPUT)
if(SWITCH2_PROBE_NEUTRAL_INPUT)
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
pico_set_program_version(${target} "0.89-native-digital-stick-trace")
pico_set_program_version(${target} "0.97-neutral-hub-${SWITCH2_PROBE_PAIR_COUNT}pair-trace")
else()
pico_set_program_version(${target} "0.89-native-digital-stick")
pico_set_program_version(${target} "0.97-neutral-hub-${SWITCH2_PROBE_PAIR_COUNT}pair")
endif()
elseif(SWITCH2_PROBE_HUB AND SWITCH2_BRIDGE_FULL_INPUT)
if(probe_controller_count GREATER 2)
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
pico_set_program_version(${target} "0.97-live-two-pair-trace")
else()
pico_set_program_version(${target} "0.97-live-two-pair")
endif()
elseif(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
pico_set_program_version(${target} "0.97-native-digital-stick-trace")
else()
pico_set_program_version(${target} "0.97-native-digital-stick")
endif()
elseif(SWITCH2_PROBE_HUB)
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
pico_set_program_version(${target} "0.89-native-hub-profiles-trace")
pico_set_program_version(${target} "0.97-native-hub-profiles-trace")
else()
pico_set_program_version(${target} "0.89-native-hub-profiles")
pico_set_program_version(${target} "0.97-native-hub-profiles")
endif()
elseif(SWITCH2_PROBE_JOIN_CHORD_GATE)
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)