Queue correlated, volatile wake requests for the BTstack owner and expose read-only completion state. Keep the controller chord, captured identity, pairings, profiles, HD settings and existing wake burst unchanged. Add Python API and wake CLI with bounded waits, explicit failure outcomes and no automatic broadcast retries. Avoid importing SDL just to obtain fixed sensor IDs so USB automation has clean output and no SDL dependency. Windows libusb cannot control hub roots. Expose only INFO/WAKE on each native child's existing vendor Interface 1 with isolated control state; retain hub/HID drivers, descriptors and identities. Add safe sibling-aware Windows discovery and a packaged Windows libusb runtime. Document manual WinUSB binding to Interface 1 only and migrate examples to find_wake_pico. Validate with 713 tests, 10 firmware/probe builds, Windows x64 wheel resolution on Python 3.9/3.11, and a real child-interface wake burst on Linux (2.03 s, no controller, no failures). Device/configuration descriptors, serials and persistent state matched before/after. Physical Windows hardware/driver operation remains unverified on this Linux workstation.
479 lines
27 KiB
CMake
479 lines
27 KiB
CMake
# Shared by the standalone USB probe and the Bluetooth-backed root target.
|
|
# Include before pico_sdk_init() so every mbedTLS source uses the same config.
|
|
set(SWITCH2_USB_PROBE_DIR "${CMAKE_CURRENT_LIST_DIR}")
|
|
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()
|
|
option(SWITCH2_PROBE_JOIN_CHORD_GATE
|
|
"Experiment: pass L/R shoulder presses only while both physical halves hold them" OFF)
|
|
set(SWITCH2_PROBE_SIDE "RIGHT" CACHE STRING "Primary Joy-Con 2 model: LEFT or RIGHT")
|
|
set_property(CACHE SWITCH2_PROBE_SIDE PROPERTY STRINGS LEFT RIGHT)
|
|
if(SWITCH2_PROBE_SIDE STREQUAL "LEFT")
|
|
if(SWITCH2_PROBE_COMPOSITE OR SWITCH2_PROBE_HUB OR SWITCH2_BRIDGE_WII_INPUT)
|
|
message(FATAL_ERROR "SWITCH2_PROBE_SIDE=LEFT cannot be combined with composite or Wii input; select RIGHT")
|
|
endif()
|
|
set(probe_joycon_left 1)
|
|
elseif(SWITCH2_PROBE_SIDE STREQUAL "RIGHT")
|
|
set(probe_joycon_left 0)
|
|
else()
|
|
message(FATAL_ERROR "SWITCH2_PROBE_SIDE must be LEFT or RIGHT")
|
|
endif()
|
|
if(SWITCH2_PROBE_COMPOSITE OR SWITCH2_PROBE_HUB)
|
|
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()
|
|
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)
|
|
endif()
|
|
if(SWITCH2_PROBE_JOIN_CHORD_GATE AND NOT SWITCH2_PROBE_COMPOSITE)
|
|
message(FATAL_ERROR "The L+R shoulder gate requires the composite Joy-Con bridge")
|
|
endif()
|
|
# Capture, the Bluetooth backend, and TinyUSB must agree before their targets exist.
|
|
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
|
|
"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 "")
|
|
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)
|
|
string(TOLOWER "${${field}}" source_address)
|
|
string(LENGTH "${source_address}" source_address_length)
|
|
if(NOT source_address_length EQUAL 17
|
|
OR NOT source_address MATCHES "^([0-9a-f][0-9a-f]:)+[0-9a-f][0-9a-f]$"
|
|
OR source_address STREQUAL "00:00:00:00:00:00"
|
|
OR source_address STREQUAL "ff:ff:ff:ff:ff")
|
|
message(FATAL_ERROR "Provide ${field} as a physical six-byte Bluetooth address")
|
|
endif()
|
|
if(source_address IN_LIST probe_source_addresses)
|
|
message(FATAL_ERROR "Physical source addresses must be distinct")
|
|
endif()
|
|
list(APPEND probe_source_addresses "${source_address}")
|
|
string(REPLACE ":" ",0x" ${field}_BYTES "${source_address}")
|
|
string(PREPEND ${field}_BYTES "0x")
|
|
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
|
|
${SWITCH2_USB_PROBE_DIR}/main.c
|
|
${SWITCH2_USB_PROBE_DIR}/protocol.c
|
|
${SWITCH2_USB_PROBE_DIR}/storage.cpp
|
|
${SWITCH2_USB_PROBE_DIR}/button_test.c)
|
|
target_compile_features(${target} PRIVATE c_std_11 cxx_std_17)
|
|
if(SWITCH2_PROBE_JOIN_CHORD_GATE)
|
|
target_compile_definitions(${target} PRIVATE SWITCH2_PROBE_JOIN_CHORD_GATE=1)
|
|
endif()
|
|
target_include_directories(${target} PRIVATE
|
|
${SWITCH2_USB_PROBE_DIR}
|
|
${SWITCH2_USB_PROBE_DIR}/../../src/firmware
|
|
${CMAKE_CURRENT_BINARY_DIR}
|
|
${PICO_SDK_PATH}/src/rp2_common/pico_btstack/include
|
|
${PICO_SDK_PATH}/lib/btstack/platform/embedded)
|
|
target_compile_definitions(${target} PRIVATE
|
|
CFG_TUSB_CONFIG_FILE="${SWITCH2_USB_PROBE_DIR}/tusb_config.h")
|
|
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 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()
|
|
if(SWITCH2_BRIDGE_WII_INPUT)
|
|
set(SWITCH2_WII_IR_SENSITIVITY 3 CACHE STRING "Standard Wii camera sensitivity preset: 2 or 3")
|
|
set_property(CACHE SWITCH2_WII_IR_SENSITIVITY PROPERTY STRINGS 2 3)
|
|
if(NOT "${SWITCH2_WII_IR_SENSITIVITY}" MATCHES "^[23]$")
|
|
message(FATAL_ERROR "SWITCH2_WII_IR_SENSITIVITY must be standard preset 2 or 3")
|
|
endif()
|
|
target_compile_definitions(bluepad32 PRIVATE
|
|
SWITCH_PICO_WII_IR_SENSITIVITY_LEVEL=${SWITCH2_WII_IR_SENSITIVITY})
|
|
set(SWITCH2_WII_IR_VIEW_WIDTH 660 CACHE STRING "Native Wii IR viewport width in camera pixels")
|
|
set(SWITCH2_WII_IR_VIEW_HEIGHT 370 CACHE STRING "Native Wii IR viewport height in camera pixels")
|
|
set(SWITCH2_WII_IR_OFFSET_X 0 CACHE STRING "Native Wii IR viewport horizontal camera offset")
|
|
set(SWITCH2_WII_IR_OFFSET_Y -115 CACHE STRING "Native Wii IR viewport vertical offset: -115 below, +115 above")
|
|
set(SWITCH2_WII_IR_SPAN_X 1920 CACHE STRING "Native mouse counts across the IR viewport width")
|
|
set(SWITCH2_WII_IR_SPAN_Y 1080 CACHE STRING "Native mouse counts across the IR viewport height")
|
|
foreach(field VIEW_WIDTH VIEW_HEIGHT OFFSET_X OFFSET_Y SPAN_X SPAN_Y)
|
|
if(NOT "${SWITCH2_WII_IR_${field}}" MATCHES "^-?[0-9]+$")
|
|
message(FATAL_ERROR "SWITCH2_WII_IR_${field} must be an integer")
|
|
endif()
|
|
endforeach()
|
|
math(EXPR offset_x_twice "2 * ${SWITCH2_WII_IR_OFFSET_X}")
|
|
math(EXPR offset_y_twice "2 * ${SWITCH2_WII_IR_OFFSET_Y}")
|
|
math(EXPR room_x "1024 - ${SWITCH2_WII_IR_VIEW_WIDTH}")
|
|
math(EXPR room_y "768 - ${SWITCH2_WII_IR_VIEW_HEIGHT}")
|
|
if(SWITCH2_WII_IR_VIEW_WIDTH LESS 1 OR SWITCH2_WII_IR_VIEW_WIDTH GREATER 1024 OR
|
|
SWITCH2_WII_IR_VIEW_HEIGHT LESS 1 OR SWITCH2_WII_IR_VIEW_HEIGHT GREATER 768 OR
|
|
offset_x_twice LESS -${room_x} OR offset_x_twice GREATER ${room_x} OR
|
|
offset_y_twice LESS -${room_y} OR offset_y_twice GREATER ${room_y} OR
|
|
SWITCH2_WII_IR_SPAN_X LESS 1 OR SWITCH2_WII_IR_SPAN_X GREATER 32767 OR
|
|
SWITCH2_WII_IR_SPAN_Y LESS 1 OR SWITCH2_WII_IR_SPAN_Y GREATER 32767)
|
|
message(FATAL_ERROR "Native IR viewport must lie inside the camera; mouse spans must be 1..32767")
|
|
endif()
|
|
target_compile_definitions(${target} PRIVATE
|
|
SWITCH2_WII_IR_SCREEN_CONFIG=${SWITCH2_WII_IR_VIEW_WIDTH},${SWITCH2_WII_IR_VIEW_HEIGHT},${SWITCH2_WII_IR_OFFSET_X},${SWITCH2_WII_IR_OFFSET_Y},${SWITCH2_WII_IR_SPAN_X},${SWITCH2_WII_IR_SPAN_Y})
|
|
endif()
|
|
option(SWITCH2_PROBE_OMIT_NATIVE_IMU
|
|
"Activation experiment: omit only native IMU bytes while retaining advertised features" OFF)
|
|
option(SWITCH2_PROBE_ZERO_NATIVE_IMU_PAYLOAD
|
|
"Activation experiment: zero native IMU payload while preserving its length" OFF)
|
|
if(SWITCH2_PROBE_OMIT_NATIVE_IMU AND SWITCH2_PROBE_ZERO_NATIVE_IMU_PAYLOAD)
|
|
message(FATAL_ERROR "Select only one IMU activation experiment")
|
|
endif()
|
|
if(SWITCH2_PROBE_OMIT_NATIVE_IMU OR SWITCH2_PROBE_ZERO_NATIVE_IMU_PAYLOAD)
|
|
if(NOT SWITCH_PICO_SWITCH2_USB_BRIDGE)
|
|
message(FATAL_ERROR "Native IMU activation experiments require the Bluetooth USB bridge")
|
|
endif()
|
|
if(SWITCH2_BRIDGE_WII_INPUT)
|
|
message(FATAL_ERROR "Disable IMU ablation experiments for the Wii source bridge")
|
|
endif()
|
|
endif()
|
|
if(SWITCH2_PROBE_OMIT_NATIVE_IMU)
|
|
target_compile_definitions(${target} PRIVATE SWITCH2_PROBE_OMIT_NATIVE_IMU=1)
|
|
elseif(SWITCH2_PROBE_ZERO_NATIVE_IMU_PAYLOAD)
|
|
target_compile_definitions(${target} PRIVATE SWITCH2_PROBE_ZERO_NATIVE_IMU_PAYLOAD=1)
|
|
endif()
|
|
|
|
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)")
|
|
set(${prefix}_FACTORY_FILE "" CACHE FILEPATH "8192-byte captured factory region with configured virtual identity")
|
|
set(${prefix}_USER_CALIBRATION_FILE "" CACHE FILEPATH "4096-byte captured user calibration region")
|
|
endforeach()
|
|
option(SWITCH2_PROBE_ACK_SETUP04 "Acknowledge the observed vendor-04 setup transaction" OFF)
|
|
if(SWITCH2_PROBE_ACK_SETUP04)
|
|
if(NOT SWITCH2_PROBE_IDENTITY_FILE OR NOT SWITCH2_PROBE_VERSION_FILE)
|
|
message(FATAL_ERROR "Setup acknowledgement requires both verified controller replies")
|
|
endif()
|
|
target_compile_definitions(${target} PRIVATE SWITCH2_PROBE_ACK_SETUP04=1)
|
|
endif()
|
|
option(SWITCH2_PROBE_USB_INIT "Implement documented USB init and stream neutral diagnostic reports" OFF)
|
|
if(SWITCH2_PROBE_USB_INIT)
|
|
if(NOT SWITCH2_PROBE_ACK_SETUP04)
|
|
message(FATAL_ERROR "USB initialization probe requires the verified setup acknowledgement")
|
|
endif()
|
|
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" OR prefix STREQUAL "SWITCH2_PROBE_FOURTH")
|
|
set(probe_model "Joy-Con 2 (L)")
|
|
set(probe_vid_pid "7e056720")
|
|
set(probe_firmware_type "00")
|
|
else()
|
|
set(probe_model "Joy-Con 2 (R)")
|
|
set(probe_vid_pid "7e056620")
|
|
set(probe_firmware_type "01")
|
|
endif()
|
|
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 "Native ${probe_model} requires ${prefix}_${field}")
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
if(${prefix}_IDENTITY_FILE)
|
|
file(READ "${${prefix}_IDENTITY_FILE}" identity_hex LIMIT 65 HEX)
|
|
string(LENGTH "${identity_hex}" identity_length)
|
|
if(NOT identity_length EQUAL 128)
|
|
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}")
|
|
endif()
|
|
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," identity_bytes "${identity_hex}")
|
|
string(APPEND identity_rows " {${identity_bytes}},\n")
|
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${${prefix}_IDENTITY_FILE}")
|
|
endif()
|
|
if(${prefix}_VERSION_FILE)
|
|
if(NOT ${prefix}_IDENTITY_FILE)
|
|
message(FATAL_ERROR "${prefix}_VERSION_FILE requires the matching identity capture")
|
|
endif()
|
|
file(READ "${${prefix}_VERSION_FILE}" version_hex LIMIT 13 HEX)
|
|
string(LENGTH "${version_hex}" version_length)
|
|
if(NOT version_length EQUAL 24)
|
|
message(FATAL_ERROR "${prefix}_VERSION_FILE must contain exactly 12 bytes")
|
|
endif()
|
|
string(TOLOWER "${version_hex}" version_hex)
|
|
string(SUBSTRING "${version_hex}" 6 2 firmware_type)
|
|
if(NOT firmware_type STREQUAL probe_firmware_type)
|
|
message(FATAL_ERROR "${prefix}_VERSION_FILE must describe selected model ${probe_model}")
|
|
endif()
|
|
string(REPLACE ":" "" address_hex "${${prefix}_CONTROLLER_ADDRESS}")
|
|
string(TOLOWER "${address_hex}" address_hex)
|
|
string(LENGTH "${address_hex}" address_length)
|
|
if(NOT address_length EQUAL 12 OR NOT address_hex MATCHES "^[0-9a-f]+$"
|
|
OR address_hex STREQUAL "000000000000" OR address_hex STREQUAL "ffffffffffff")
|
|
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 "Advertised controller addresses must be distinct")
|
|
endif()
|
|
list(APPEND controller_addresses "${address_hex}")
|
|
set(address_reversed "")
|
|
foreach(byte RANGE 0 5)
|
|
math(EXPR position "10 - 2 * ${byte}")
|
|
string(SUBSTRING "${address_hex}" ${position} 2 octet)
|
|
string(APPEND address_reversed "${octet}")
|
|
endforeach()
|
|
string(SUBSTRING "${version_hex}" 0 6 main_version)
|
|
string(SUBSTRING "${version_hex}" 8 6 bluetooth_version)
|
|
# Layout corroborated against two genuine USB vendor-02 responses and their
|
|
# matching command-10 version and command-15 address responses.
|
|
set(status_hex "${main_version}000000${bluetooth_version}00${address_reversed}")
|
|
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," status_bytes "${status_hex}")
|
|
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," firmware_bytes "${version_hex}")
|
|
string(APPEND status_rows " {${status_bytes}},\n")
|
|
string(APPEND firmware_rows " {${firmware_bytes}},\n")
|
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${${prefix}_VERSION_FILE}")
|
|
endif()
|
|
if(${prefix}_FACTORY_FILE OR ${prefix}_USER_CALIBRATION_FILE)
|
|
if(NOT SWITCH2_PROBE_USB_INIT OR NOT ${prefix}_FACTORY_FILE OR NOT ${prefix}_USER_CALIBRATION_FILE)
|
|
message(FATAL_ERROR "${prefix} memory replies require initialized USB and both calibration captures")
|
|
endif()
|
|
file(READ "${${prefix}_FACTORY_FILE}" factory_hex LIMIT 8193 HEX)
|
|
file(READ "${${prefix}_USER_CALIBRATION_FILE}" user_calibration_hex LIMIT 4097 HEX)
|
|
string(LENGTH "${factory_hex}" factory_length)
|
|
string(LENGTH "${user_calibration_hex}" user_calibration_length)
|
|
if(NOT factory_length EQUAL 16384 OR NOT user_calibration_length EQUAL 8192)
|
|
message(FATAL_ERROR "${prefix} factory/user captures must contain exactly 8192/4096 bytes")
|
|
endif()
|
|
string(TOLOWER "${factory_hex}" factory_hex)
|
|
string(TOLOWER "${user_calibration_hex}" user_calibration_hex)
|
|
string(SUBSTRING "${factory_hex}" 0 128 factory_identity_hex)
|
|
if(NOT factory_identity_hex STREQUAL identity_hex)
|
|
message(FATAL_ERROR "${prefix} factory memory identity must match the vendor-control identity")
|
|
endif()
|
|
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," factory_bytes "${factory_hex}")
|
|
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," user_calibration_bytes "${user_calibration_hex}")
|
|
string(APPEND factory_rows " {${factory_bytes}},\n")
|
|
string(APPEND user_calibration_rows " {${user_calibration_bytes}},\n")
|
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
|
|
"${${prefix}_FACTORY_FILE}" "${${prefix}_USER_CALIBRATION_FILE}")
|
|
endif()
|
|
endforeach()
|
|
set(capture_header "// Generated from private, read-only controller captures; do not commit.\n#include <stdint.h>\n#include \"model.h\"\n")
|
|
if(SWITCH2_PROBE_IDENTITY_FILE)
|
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/probe_identity.h"
|
|
"${capture_header}static const uint8_t probe_identity_replies[PROBE_CONTROLLER_COUNT][64] = {\n${identity_rows}};\n")
|
|
target_compile_definitions(${target} PRIVATE SWITCH2_PROBE_IDENTITY_REPLY=1)
|
|
endif()
|
|
if(SWITCH2_PROBE_VERSION_FILE)
|
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/probe_version.h"
|
|
"${capture_header}static const uint8_t probe_version_replies[PROBE_CONTROLLER_COUNT][16] = {\n${status_rows}};\nstatic const uint8_t probe_firmware_versions[PROBE_CONTROLLER_COUNT][12] = {\n${firmware_rows}};\n")
|
|
target_compile_definitions(${target} PRIVATE SWITCH2_PROBE_VERSION_REPLY=1)
|
|
endif()
|
|
if(SWITCH2_PROBE_FACTORY_FILE)
|
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/probe_memory_data.h"
|
|
"${capture_header}static const uint8_t probe_factory_memories[PROBE_CONTROLLER_COUNT][8192] = {\n${factory_rows}};\nstatic const uint8_t probe_user_calibrations[PROBE_CONTROLLER_COUNT][4096] = {\n${user_calibration_rows}};\n")
|
|
list(APPEND probe_sources ${SWITCH2_USB_PROBE_DIR}/memory.c)
|
|
target_compile_definitions(${target} PRIVATE SWITCH2_PROBE_MEMORY=1)
|
|
endif()
|
|
target_sources(${target} PRIVATE ${probe_sources})
|
|
set(probe_compile_options -Wall -Wextra -Werror)
|
|
if(SWITCH_PICO_SWITCH2_USB_BRIDGE)
|
|
# Preserve the root firmware's warning policy outside the probe sources.
|
|
set_source_files_properties(${probe_sources} PROPERTIES
|
|
COMPILE_OPTIONS "${probe_compile_options}")
|
|
else()
|
|
target_compile_options(${target} PRIVATE ${probe_compile_options})
|
|
endif()
|
|
target_link_libraries(${target} PRIVATE pico_stdlib hardware_uart hardware_sync
|
|
hardware_flash pico_flash pico_mbedtls_crypto pico_mbedtls_headers)
|
|
if(SWITCH2_PROBE_HUB)
|
|
target_sources(${target} PRIVATE
|
|
${SWITCH2_USB_PROBE_DIR}/../pico_usb_address_probe/router.c
|
|
${SWITCH2_USB_PROBE_DIR}/../../src/firmware/usb/native_hub/native_hub.c)
|
|
target_include_directories(${target} PRIVATE
|
|
${SWITCH2_USB_PROBE_DIR}/../pico_usb_address_probe
|
|
${PICO_SDK_PATH}/lib/tinyusb/src)
|
|
target_compile_definitions(${target} PRIVATE CFG_TUSB_MCU=OPT_MCU_RP2040)
|
|
target_link_libraries(${target} PRIVATE pico_multicore pico_unique_id hardware_irq hardware_resets)
|
|
set_source_files_properties(
|
|
${SWITCH2_USB_PROBE_DIR}/../pico_usb_address_probe/router.c
|
|
${SWITCH2_USB_PROBE_DIR}/../../src/firmware/usb/native_hub/native_hub.c
|
|
PROPERTIES COMPILE_OPTIONS "-O3;-fno-jump-tables;-Wall;-Wextra;-Werror")
|
|
# Core0 now owns the Bluetooth call stack. Reserve16KiB from main
|
|
# SRAM instead of overflowing the SDK's4KiB scratch stack region.
|
|
set(default_linker "${PICO_SDK_PATH}/src/rp2_common/pico_crt0/rp2350/memmap_default.ld")
|
|
file(READ "${default_linker}" hub_linker)
|
|
string(REPLACE "RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k"
|
|
"RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 496k\n MAIN_STACK(rwx) : ORIGIN = 0x2007c000, LENGTH = 16k"
|
|
hub_linker "${hub_linker}")
|
|
string(REPLACE "KEEP(*(.stack*))\n } > SCRATCH_Y"
|
|
"KEEP(*(.stack*))\n } > MAIN_STACK" hub_linker "${hub_linker}")
|
|
string(REPLACE "__StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);"
|
|
"__StackTop = ORIGIN(MAIN_STACK) + LENGTH(MAIN_STACK);" hub_linker "${hub_linker}")
|
|
if(NOT hub_linker MATCHES "MAIN_STACK")
|
|
message(FATAL_ERROR "SDK linker stack layout changed")
|
|
endif()
|
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/native_hub_stack.ld" "${hub_linker}")
|
|
pico_set_linker_script(${target} "${CMAKE_CURRENT_BINARY_DIR}/native_hub_stack.ld")
|
|
else()
|
|
target_link_libraries(${target} PRIVATE tinyusb_device)
|
|
endif()
|
|
pico_enable_stdio_usb(${target} 0)
|
|
pico_enable_stdio_uart(${target} 1)
|
|
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")
|
|
elseif(SWITCH2_BRIDGE_WII_INPUT)
|
|
pico_set_program_name(${target} "Switch 2 Wii IR and native motion bridge")
|
|
elseif(SWITCH_PICO_SWITCH2_USB_BRIDGE AND probe_joycon_left)
|
|
pico_set_program_name(${target} "Switch 2 left Joy-Con Bluetooth bridge")
|
|
elseif(SWITCH_PICO_SWITCH2_USB_BRIDGE)
|
|
pico_set_program_name(${target} "Switch 2 right Joy-Con Bluetooth bridge")
|
|
else()
|
|
pico_set_program_name(${target} "Switch 2 USB initialization capture")
|
|
endif()
|
|
if(SWITCH2_PROBE_NEUTRAL_INPUT)
|
|
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
|
|
pico_set_program_version(${target} "0.110-neutral-hub-${SWITCH2_PROBE_PAIR_COUNT}pair-trace")
|
|
else()
|
|
pico_set_program_version(${target} "0.110-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.110-live-two-pair-trace")
|
|
else()
|
|
pico_set_program_version(${target} "0.110-live-two-pair")
|
|
endif()
|
|
elseif(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
|
|
pico_set_program_version(${target} "0.110-native-digital-stick-trace")
|
|
else()
|
|
pico_set_program_version(${target} "0.110-native-digital-stick")
|
|
endif()
|
|
elseif(SWITCH2_PROBE_HUB)
|
|
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
|
|
pico_set_program_version(${target} "0.110-native-hub-profiles-trace")
|
|
else()
|
|
pico_set_program_version(${target} "0.110-native-hub-profiles")
|
|
endif()
|
|
elseif(SWITCH2_PROBE_JOIN_CHORD_GATE)
|
|
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
|
|
pico_set_program_version(${target} "0.110-pair-chord-trace")
|
|
else()
|
|
pico_set_program_version(${target} "0.110-pair-chord")
|
|
endif()
|
|
elseif(SWITCH2_PROBE_COMPOSITE)
|
|
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
|
|
pico_set_program_version(${target} "0.110-pair-trace")
|
|
else()
|
|
pico_set_program_version(${target} "0.110-pair")
|
|
endif()
|
|
elseif(SWITCH2_PROBE_OMIT_NATIVE_IMU)
|
|
pico_set_program_version(${target} "0.110-no-imu")
|
|
elseif(SWITCH2_PROBE_ZERO_NATIVE_IMU_PAYLOAD)
|
|
pico_set_program_version(${target} "0.110-zero-imu-payload")
|
|
elseif(SWITCH2_BRIDGE_WII_INPUT)
|
|
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
|
|
pico_set_program_version(${target} "0.110-wii-trace")
|
|
else()
|
|
pico_set_program_version(${target} "0.110-wii")
|
|
endif()
|
|
elseif(SWITCH_PICO_SWITCH2_USB_BRIDGE AND probe_joycon_left)
|
|
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
|
|
pico_set_program_version(${target} "0.110-left-trace")
|
|
else()
|
|
pico_set_program_version(${target} "0.110-left")
|
|
endif()
|
|
elseif(SWITCH_PICO_SWITCH2_USB_BRIDGE)
|
|
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
|
|
pico_set_program_version(${target} "0.110-trace")
|
|
else()
|
|
pico_set_program_version(${target} "0.110")
|
|
endif()
|
|
else()
|
|
pico_set_program_version(${target} "0.110")
|
|
endif()
|
|
endfunction()
|