Compare commits
No commits in common. "c2bfaa1d922e075085a24fcffcf732fcfd8f2822" and "4626b13bd8a58c563daa8afde86a47d2c1ca5771" have entirely different histories.
c2bfaa1d92
...
4626b13bd8
104 changed files with 640 additions and 549 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -9,8 +9,3 @@ debug
|
||||||
hid-nintendo.c
|
hid-nintendo.c
|
||||||
__pycache__
|
__pycache__
|
||||||
!.vscode/*
|
!.vscode/*
|
||||||
.omo/*
|
|
||||||
.slim/*
|
|
||||||
pytest-*/*
|
|
||||||
build-*/*
|
|
||||||
.ignore
|
|
||||||
|
|
|
||||||
|
|
@ -427,7 +427,7 @@ with the current identity.
|
||||||
|
|
||||||
#### 4A — Output-driver boundary and production XInput
|
#### 4A — Output-driver boundary and production XInput
|
||||||
|
|
||||||
- replace `SWITCH_PICO_ADAPTER_FEASIBILITY` branches in `src/firmware/main.cpp` with
|
- replace `SWITCH_PICO_ADAPTER_FEASIBILITY` branches in `switch-pico.cpp` with
|
||||||
one fixed static driver interface: descriptors, init/reset, input, task,
|
one fixed static driver interface: descriptors, init/reset, input, task,
|
||||||
readiness/capabilities, and host output
|
readiness/capabilities, and host output
|
||||||
- keep the Phase 3 profile/runtime transform exactly once before serialization
|
- keep the Phase 3 profile/runtime transform exactly once before serialization
|
||||||
|
|
|
||||||
|
|
@ -56,29 +56,26 @@ pico_sdk_init()
|
||||||
|
|
||||||
# Configure BLUEPAD32 input backend if selected
|
# Configure BLUEPAD32 input backend if selected
|
||||||
if(SWITCH_PICO_INPUT_BACKEND STREQUAL "BLUEPAD32")
|
if(SWITCH_PICO_INPUT_BACKEND STREQUAL "BLUEPAD32")
|
||||||
|
# Ensure Python3 is available and execute patch preparation
|
||||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||||
|
|
||||||
set(BLUEPAD32_PREP_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/tools/prepare_bluepad32.py)
|
set(BLUEPAD32_PREP_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/tools/prepare_bluepad32.py)
|
||||||
set(BLUEPAD32_SOURCE ${CMAKE_CURRENT_LIST_DIR}/external/bluepad32)
|
|
||||||
set(BLUEPAD32_PATCH ${CMAKE_CURRENT_LIST_DIR}/patches/bluepad32-sdl3-imu.patch)
|
|
||||||
set(BLUEPAD32_ROOT ${CMAKE_CURRENT_BINARY_DIR}/_deps/bluepad32-src)
|
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND ${Python3_EXECUTABLE} ${BLUEPAD32_PREP_SCRIPT}
|
COMMAND ${Python3_EXECUTABLE} ${BLUEPAD32_PREP_SCRIPT}
|
||||||
--bluepad32 ${BLUEPAD32_SOURCE}
|
|
||||||
--patch ${BLUEPAD32_PATCH}
|
|
||||||
--output ${BLUEPAD32_ROOT}
|
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||||
RESULT_VARIABLE BLUEPAD32_PREP_RESULT
|
RESULT_VARIABLE BLUEPAD32_PREP_RESULT
|
||||||
OUTPUT_VARIABLE BLUEPAD32_PREP_OUTPUT
|
OUTPUT_VARIABLE BLUEPAD32_PREP_OUTPUT
|
||||||
ERROR_VARIABLE BLUEPAD32_PREP_ERROR
|
ERROR_VARIABLE BLUEPAD32_PREP_ERROR
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT BLUEPAD32_PREP_RESULT EQUAL 0)
|
if(NOT BLUEPAD32_PREP_RESULT EQUAL 0)
|
||||||
message(FATAL_ERROR
|
message(FATAL_ERROR
|
||||||
"Failed to prepare isolated Bluepad32 source. "
|
"Failed to prepare Bluepad32: Patch application or validation failed. "
|
||||||
"Details: ${BLUEPAD32_PREP_ERROR}")
|
"Details: ${BLUEPAD32_PREP_ERROR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Configure Bluepad32 include paths and subdirectory
|
||||||
|
set(BLUEPAD32_ROOT ${CMAKE_CURRENT_LIST_DIR}/external/bluepad32)
|
||||||
set(BTSTACK_ROOT ${PICO_SDK_PATH}/lib/btstack)
|
set(BTSTACK_ROOT ${PICO_SDK_PATH}/lib/btstack)
|
||||||
include_directories(
|
include_directories(
|
||||||
${CMAKE_CURRENT_LIST_DIR}/bluepad32_config
|
${CMAKE_CURRENT_LIST_DIR}/bluepad32_config
|
||||||
|
|
@ -92,37 +89,36 @@ if(SWITCH_PICO_INPUT_BACKEND STREQUAL "BLUEPAD32")
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Firmware sources live under one include root and are grouped by responsibility.
|
# Add executable. Default name is the project name, version 0.1
|
||||||
set(SWITCH_PICO_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/src/firmware)
|
|
||||||
|
|
||||||
add_executable(switch-pico
|
add_executable(switch-pico
|
||||||
${SWITCH_PICO_SOURCE_DIR}/main.cpp
|
switch-pico.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/usb/usb_output_driver.cpp
|
switch_pro_driver.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/usb/switch/switch_pro_driver.cpp
|
usb_output_driver.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/usb/switch/switch_haptics.cpp
|
switch_haptics.cpp
|
||||||
)
|
)
|
||||||
if(SWITCH_PICO_INPUT_BACKEND STREQUAL "BLUEPAD32")
|
if(SWITCH_PICO_INPUT_BACKEND STREQUAL "BLUEPAD32")
|
||||||
target_sources(switch-pico PRIVATE
|
target_sources(switch-pico PRIVATE
|
||||||
${SWITCH_PICO_SOURCE_DIR}/adapter/adapter_host_probe.cpp
|
bluepad32_input_backend.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/adapter/adapter_mode_controller.cpp
|
adapter_host_probe.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/configuration/adapter_configuration.cpp
|
adapter_mode_controller.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/configuration/configuration_service.cpp
|
controller_identity.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/configuration/configuration_storage.cpp
|
controller_profile.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/configuration/configuration_transaction.cpp
|
controller_profile_transform.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/core/controller_identity.cpp
|
controller_synthetic_input.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/input/bluepad32_input_backend.cpp
|
controller_profile_runtime.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/platform/pico/bootsel_pairing_button.cpp
|
profile_storage.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/platform/pico/pico_configuration_storage.cpp
|
profile_service.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/platform/pico/pico_profile_storage.cpp
|
pico_profile_storage.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/profile/controller_profile.cpp
|
bootsel_pairing_button.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/profile/controller_profile_runtime.cpp
|
adapter_configuration.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/profile/controller_profile_transform.cpp
|
configuration_storage.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/profile/controller_synthetic_input.cpp
|
configuration_transaction.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/profile/profile_service.cpp
|
configuration_service.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/profile/profile_storage.cpp
|
pico_configuration_storage.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/usb/generic_hid/generic_hid_driver.cpp
|
usb_configuration_management.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/usb/usb_configuration_management.cpp
|
xinput_driver.cpp
|
||||||
${SWITCH_PICO_SOURCE_DIR}/usb/xinput/xinput_driver.cpp
|
generic_hid_driver.cpp
|
||||||
)
|
)
|
||||||
target_compile_definitions(switch-pico PRIVATE
|
target_compile_definitions(switch-pico PRIVATE
|
||||||
SWITCH_PICO_BLUEPAD32=1
|
SWITCH_PICO_BLUEPAD32=1
|
||||||
|
|
@ -170,11 +166,9 @@ if (SWITCH_PICO_LOG)
|
||||||
target_compile_definitions(switch-pico PRIVATE SWITCH_PICO_LOG=1)
|
target_compile_definitions(switch-pico PRIVATE SWITCH_PICO_LOG=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Internal includes are rooted at src/firmware. TinyUSB discovers tusb_config.h
|
# Add the standard include files to the build
|
||||||
# through the Pico platform directory.
|
|
||||||
target_include_directories(switch-pico PRIVATE
|
target_include_directories(switch-pico PRIVATE
|
||||||
${SWITCH_PICO_SOURCE_DIR}
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
${SWITCH_PICO_SOURCE_DIR}/platform/pico
|
|
||||||
)
|
)
|
||||||
|
|
||||||
pico_add_extra_outputs(switch-pico)
|
pico_add_extra_outputs(switch-pico)
|
||||||
|
|
|
||||||
32
README.md
32
README.md
|
|
@ -3,31 +3,11 @@
|
||||||
Raspberry Pi Pico firmware that emulates one or more Switch Pro controllers over USB. Input can come from the SDL3-to-UART computer bridge or, on Pico 2 W, directly from Bluetooth controllers through Bluepad32.
|
Raspberry Pi Pico firmware that emulates one or more Switch Pro controllers over USB. Input can come from the SDL3-to-UART computer bridge or, on Pico 2 W, directly from Bluetooth controllers through Bluepad32.
|
||||||
|
|
||||||
## What you get
|
## What you get
|
||||||
- **Firmware** (`src/firmware/`): acts as a Switch Pro controller (one on standard Pico, four on Pico 2 W AIO), accepting either UART bridge reports or the optional Pico 2 W Bluepad32 backend.
|
- **Firmware** (`switch-pico.cpp` + `switch_pro_driver.*`): acts as a Switch Pro controller (one on standard Pico, four on Pico 2 W AIO), accepting either UART bridge reports or the optional Pico 2 W Bluepad32 backend.
|
||||||
- **Python bridge** (`switch_pico_bridge.controller_uart_bridge` / CLI `controller-uart-bridge`): reads SDL3 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux).
|
- **Python bridge** (`switch_pico_bridge.controller_uart_bridge` / CLI `controller-uart-bridge`): reads SDL3 controllers on the host, sends reports over UART, and applies rumble locally. Hot‑plug friendly and cross‑platform (macOS/Windows/Linux).
|
||||||
- **Color configuration** (`src/firmware/platform/pico/controller_color_config.h`): compile-time RGB colors for emulated controller grips and supported Bluetooth controller LEDs.
|
- **Color configuration** (`controller_color_config.h`): compile-time RGB colors for emulated controller grips and supported Bluetooth controller LEDs.
|
||||||
- **Pico 2 W AIO firmware** (`firmware/switch-pico-aio.uf2`): hosts four concurrent Bluetooth controllers and sends their controls, calibrated motion, rumble, and slot identity through four separate Switch Pro USB interfaces without a computer.
|
- **Pico 2 W AIO firmware** (`firmware/switch-pico-aio.uf2`): hosts four concurrent Bluetooth controllers and sends their controls, calibrated motion, rumble, and slot identity through four separate Switch Pro USB interfaces without a computer.
|
||||||
|
|
||||||
## Source layout
|
|
||||||
|
|
||||||
Firmware code has one include root, `src/firmware`, with responsibility-based
|
|
||||||
modules:
|
|
||||||
|
|
||||||
| Path | Responsibility |
|
|
||||||
|---|---|
|
|
||||||
| `src/firmware/main.cpp` | Firmware entry point and backend orchestration |
|
|
||||||
| `src/firmware/adapter/` | USB mode selection, host probing, and managed reboot |
|
|
||||||
| `src/firmware/configuration/` | Persistent adapter configuration and transactions |
|
|
||||||
| `src/firmware/core/` | Shared controller identity, color, and input-state types |
|
|
||||||
| `src/firmware/input/` | Bluepad32 controller input backend and hotkeys |
|
|
||||||
| `src/firmware/platform/pico/` | Pico flash/BOOTSEL integrations and compile-time board configuration |
|
|
||||||
| `src/firmware/profile/` | Controller profiles, transforms, storage, and runtime |
|
|
||||||
| `src/firmware/usb/` | USB output boundary, management protocol, and per-protocol drivers |
|
|
||||||
|
|
||||||
Internal includes are rooted at `src/firmware`, for example
|
|
||||||
`#include "profile/controller_profile.h"`. Host-side Python remains in
|
|
||||||
`src/switch_pico_bridge/`; native firmware tests remain in `tests/`.
|
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
1. Flash the Pico with `firmware/switch-pico.uf2` (or build your own) using BOOTSEL drag-and-drop (see “Manual UF2 flashing” below).
|
1. Flash the Pico with `firmware/switch-pico.uf2` (or build your own) using BOOTSEL drag-and-drop (see “Manual UF2 flashing” below).
|
||||||
2. Wire Pico UART1 to a USB↔UART adapter (GPIO4 TX, GPIO5 RX, GND) and plug that adapter into your host PC.
|
2. Wire Pico UART1 to a USB↔UART adapter (GPIO4 TX, GPIO5 RX, GND) and plug that adapter into your host PC.
|
||||||
|
|
@ -64,7 +44,7 @@ This uses an isolated `build-aio/` CMake cache and publishes:
|
||||||
|
|
||||||
The default `python3 build.py` command and `firmware/switch-pico.*` artifacts remain the UART/Pico build. The AIO build requires `PICO_BOARD=pico2_w`; it is not interchangeable with the original non-wireless Pico firmware.
|
The default `python3 build.py` command and `firmware/switch-pico.*` artifacts remain the UART/Pico build. The AIO build requires `PICO_BOARD=pico2_w`; it is not interchangeable with the original non-wireless Pico firmware.
|
||||||
|
|
||||||
Both `build.py --aio` and direct AIO CMake configuration copy the pinned Bluepad32 source into the active build directory and apply `patches/bluepad32-sdl3-imu.patch` there before compiling. The patch makes supported motion controllers use SDL3-equivalent axes and fixed-point units before conversion to Nintendo samples. The `external/bluepad32` submodule remains pristine; patch or source-revision drift fails configuration.
|
Both `build.py --aio` and direct AIO CMake configuration apply `patches/bluepad32-sdl3-imu.patch` idempotently before compiling Bluepad32. The patch makes supported motion controllers use SDL3-equivalent axes and fixed-point units before conversion to Nintendo samples. It intentionally leaves the dependency worktree dirty; the committed submodule revision remains Bluepad32 4.2.0.
|
||||||
|
|
||||||
### Pairing up to four controllers
|
### Pairing up to four controllers
|
||||||
|
|
||||||
|
|
@ -182,7 +162,7 @@ Each AIO slot has one color shared by its emulated Switch Pro grips and its phys
|
||||||
3. Yellow `#F6C945`
|
3. Yellow `#F6C945`
|
||||||
4. Green `#2ECC71`
|
4. Green `#2ECC71`
|
||||||
|
|
||||||
When a controller becomes ready, RGB-capable devices such as DualSense and DualShock 4 receive a darker, more saturated RGB value derived automatically from the slot's Switch grip color. Controllers without an RGB light use player indicator 1, 2, 3, or 4 when Bluepad32 exposes player-LED control. Devices without either capability are left unchanged. Edit only the four grip colors in `src/firmware/platform/pico/controller_color_config.h`; rebuilding automatically recalibrates their lightbar colors.
|
When a controller becomes ready, RGB-capable devices such as DualSense and DualShock 4 receive a darker, more saturated RGB value derived automatically from the slot's Switch grip color. Controllers without an RGB light use player indicator 1, 2, 3, or 4 when Bluepad32 exposes player-LED control. Devices without either capability are left unchanged. Edit only the four grip colors in `controller_color_config.h`; rebuilding automatically recalibrates their lightbar colors.
|
||||||
|
|
||||||
### Controller capabilities
|
### Controller capabilities
|
||||||
|
|
||||||
|
|
@ -372,7 +352,7 @@ python3 build.py --grip-color FF00AA
|
||||||
```
|
```
|
||||||
|
|
||||||
Both options update all four slot definitions in
|
Both options update all four slot definitions in
|
||||||
`src/firmware/platform/pico/controller_color_config.h` before building. With no color option, the
|
`controller_color_config.h` before building. With no color option, the
|
||||||
per-slot blue/red/yellow/green palette is left unchanged. Run
|
per-slot blue/red/yellow/green palette is left unchanged. Run
|
||||||
`python3 build.py --help` to see the available command-line options.
|
`python3 build.py --help` to see the available command-line options.
|
||||||
|
|
||||||
|
|
@ -567,7 +547,7 @@ Keep `SwitchImuMode` as a three-state value. Never acknowledge mode 2 and then e
|
||||||
|
|
||||||
#### Mode-2 implementation
|
#### Mode-2 implementation
|
||||||
|
|
||||||
`src/firmware/usb/switch/switch_pro_driver.cpp` implements this in `integrate_motion_sample()` and `fill_quaternion_imu_report_data()`:
|
`switch_pro_driver.cpp` implements this in `integrate_motion_sample()` and `fill_quaternion_imu_report_data()`:
|
||||||
|
|
||||||
1. Reset quaternion state to `(0, 0, 0, 1)` when transitioning into mode 2.
|
1. Reset quaternion state to `(0, 0, 0, 1)` when transitioning into mode 2.
|
||||||
2. Integrate each report's three gyro samples at 5 ms per sample. The Nintendo quaternion axes use sensor `Y, X, Z`, not `X, Y, Z`.
|
2. Integrate each report's three gyro samples at 5 ms per sample. The Nintendo quaternion axes use sensor `Y, X, Z`, not `X, Y, Z`.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include "configuration/adapter_configuration.h"
|
#include "adapter_configuration.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "adapter/adapter_usb_mode.h"
|
#include "adapter_usb_mode.h"
|
||||||
|
|
||||||
constexpr uint16_t ADAPTER_CONFIGURATION_LEGACY_SCHEMA_VERSION = 1;
|
constexpr uint16_t ADAPTER_CONFIGURATION_LEGACY_SCHEMA_VERSION = 1;
|
||||||
constexpr uint16_t ADAPTER_CONFIGURATION_SCHEMA_VERSION = 2;
|
constexpr uint16_t ADAPTER_CONFIGURATION_SCHEMA_VERSION = 2;
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "adapter/adapter_host_probe.h"
|
#include "adapter_host_probe.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#ifdef SWITCH_PICO_LOG
|
#ifdef SWITCH_PICO_LOG
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -7,12 +8,12 @@
|
||||||
#define PROBE_LOG(...) ((void)0)
|
#define PROBE_LOG(...) ((void)0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "adapter/adapter_host_probe_state.h"
|
#include "adapter_host_probe_state.h"
|
||||||
#include "profile/controller_profile_runtime.h"
|
#include "controller_profile_runtime.h"
|
||||||
#include "hardware/structs/watchdog.h"
|
#include "hardware/structs/watchdog.h"
|
||||||
#include "hardware/watchdog.h"
|
#include "hardware/watchdog.h"
|
||||||
#include "pico/time.h"
|
#include "pico/time.h"
|
||||||
#include "usb/xinput/xinput_descriptors.h"
|
#include "xinput_descriptors.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "adapter/adapter_usb_mode.h"
|
#include "adapter_usb_mode.h"
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
|
|
||||||
// Consume watchdog scratch and freeze the active mode. Call exactly once
|
// Consume watchdog scratch and freeze the active mode. Call exactly once
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
#include "adapter/adapter_mode_controller.h"
|
#include "adapter_mode_controller.h"
|
||||||
#include "adapter/adapter_reboot.h"
|
#include "adapter_reboot.h"
|
||||||
#include "configuration/adapter_configuration.h"
|
|
||||||
#include "adapter/adapter_host_probe.h"
|
#include "adapter_configuration.h"
|
||||||
#include "input/bluepad32_input_backend.h"
|
#include "adapter_host_probe.h"
|
||||||
#include "configuration/configuration_service.h"
|
#include "bluepad32_input_backend.h"
|
||||||
#include "profile/controller_profile.h"
|
#include "configuration_service.h"
|
||||||
#include "profile/controller_profile_runtime.h"
|
#include "controller_profile.h"
|
||||||
|
#include "controller_profile_runtime.h"
|
||||||
#include "hardware/watchdog.h"
|
#include "hardware/watchdog.h"
|
||||||
#include "pico/bootrom.h"
|
#include "pico/bootrom.h"
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
#include "usb/usb_output_driver.h"
|
#include "usb_output_driver.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "adapter/adapter_usb_mode.h"
|
#include "adapter_usb_mode.h"
|
||||||
#include "core/controller_state.h"
|
#include "controller_state.h"
|
||||||
|
|
||||||
constexpr uint8_t ADAPTER_MODE_CONTROLLER_SLOT_COUNT = 4;
|
constexpr uint8_t ADAPTER_MODE_CONTROLLER_SLOT_COUNT = 4;
|
||||||
constexpr uint32_t ADAPTER_MODE_CHORD_HOLD_MS = 3000;
|
constexpr uint32_t ADAPTER_MODE_CHORD_HOLD_MS = 3000;
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
#include "input/bluepad32_input_backend.h"
|
#include "bluepad32_input_backend.h"
|
||||||
#include "input/controller_hotkey_config.h"
|
#include "controller_hotkey_config.h"
|
||||||
#include "configuration/configuration_service.h"
|
#include "configuration_service.h"
|
||||||
#include "profile/profile_service.h"
|
#include "profile_service.h"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -14,7 +15,7 @@
|
||||||
#include <pico/stdlib.h>
|
#include <pico/stdlib.h>
|
||||||
#include <uni.h>
|
#include <uni.h>
|
||||||
#ifdef SWITCH_PICO_USB_OUTPUT_MODES
|
#ifdef SWITCH_PICO_USB_OUTPUT_MODES
|
||||||
#include "adapter/adapter_usb_mode.h"
|
#include "adapter_usb_mode.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "core/controller_color.h"
|
#include "controller_color.h"
|
||||||
#include "core/controller_identity.h"
|
#include "controller_identity.h"
|
||||||
#include "profile/controller_profile.h"
|
#include "controller_profile.h"
|
||||||
#include "core/controller_state.h"
|
#include "controller_state.h"
|
||||||
#include "usb/switch/switch_haptics.h"
|
#include "switch_haptics.h"
|
||||||
|
|
||||||
constexpr uint8_t BLUEPAD32_INPUT_BACKEND_SLOT_COUNT = 4;
|
constexpr uint8_t BLUEPAD32_INPUT_BACKEND_SLOT_COUNT = 4;
|
||||||
constexpr uint8_t BLUEPAD32_PAIRING_RECORD_CAPACITY = 16;
|
constexpr uint8_t BLUEPAD32_PAIRING_RECORD_CAPACITY = 16;
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "platform/pico/bootsel_pairing_button.h"
|
#include "bootsel_pairing_button.h"
|
||||||
|
|
||||||
#include "hardware/gpio.h"
|
#include "hardware/gpio.h"
|
||||||
#include "hardware/structs/ioqspi.h"
|
#include "hardware/structs/ioqspi.h"
|
||||||
#include "hardware/structs/sio.h"
|
#include "hardware/structs/sio.h"
|
||||||
6
build.py
6
build.py
|
|
@ -10,8 +10,7 @@ import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||||
FIRMWARE_SOURCE_DIR = SCRIPT_DIR / "src" / "firmware"
|
CONFIG_FILE = SCRIPT_DIR / "controller_color_config.h"
|
||||||
CONFIG_FILE = FIRMWARE_SOURCE_DIR / "platform" / "pico" / "controller_color_config.h"
|
|
||||||
BUILD_DIR = SCRIPT_DIR / "build"
|
BUILD_DIR = SCRIPT_DIR / "build"
|
||||||
AIO_BUILD_DIR = SCRIPT_DIR / "build-aio"
|
AIO_BUILD_DIR = SCRIPT_DIR / "build-aio"
|
||||||
FEASIBILITY_BUILD_DIR = SCRIPT_DIR / "build-feasibility"
|
FEASIBILITY_BUILD_DIR = SCRIPT_DIR / "build-feasibility"
|
||||||
|
|
@ -266,7 +265,7 @@ def parse_args():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Build and flash the project, optionally setting grip colors.",
|
description="Build and flash the project, optionally setting grip colors.",
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
epilog=f"Default behavior leaves {CONFIG_FILE.relative_to(SCRIPT_DIR)} unchanged.",
|
epilog="Default behavior leaves controller_color_config.h unchanged.",
|
||||||
)
|
)
|
||||||
mode_group = parser.add_mutually_exclusive_group()
|
mode_group = parser.add_mutually_exclusive_group()
|
||||||
mode_group.add_argument(
|
mode_group.add_argument(
|
||||||
|
|
@ -366,6 +365,7 @@ def build(
|
||||||
firmware_uf2_path,
|
firmware_uf2_path,
|
||||||
):
|
):
|
||||||
if aio or adapter_feasibility:
|
if aio or adapter_feasibility:
|
||||||
|
run_cmd([sys.executable, str(SCRIPT_DIR / "tools" / "prepare_bluepad32.py")])
|
||||||
definitions = [
|
definitions = [
|
||||||
"-DSWITCH_PICO_LOG=OFF",
|
"-DSWITCH_PICO_LOG=OFF",
|
||||||
"-DPICO_BOARD=pico2_w",
|
"-DPICO_BOARD=pico2_w",
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
#include "configuration/configuration_service.h"
|
#include "configuration_service.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "pico/critical_section.h"
|
#include "pico/critical_section.h"
|
||||||
#include "platform/pico/pico_configuration_storage.h"
|
#include "pico_configuration_storage.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "configuration/adapter_configuration.h"
|
#include "adapter_configuration.h"
|
||||||
#include "configuration/configuration_transaction.h"
|
#include "configuration_transaction.h"
|
||||||
|
|
||||||
constexpr uint32_t CONFIGURATION_SERVICE_INTERNAL_TRANSACTION_ID_MASK =
|
constexpr uint32_t CONFIGURATION_SERVICE_INTERNAL_TRANSACTION_ID_MASK =
|
||||||
0x80000000u;
|
0x80000000u;
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "configuration/configuration_storage.h"
|
#include "configuration_storage.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
#include "configuration/configuration_transaction.h"
|
#include "configuration_transaction.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "configuration/adapter_configuration.h"
|
#include "adapter_configuration.h"
|
||||||
|
|
||||||
ConfigurationTransactionStatus ConfigurationTransaction::begin(
|
ConfigurationTransactionStatus ConfigurationTransaction::begin(
|
||||||
uint32_t transaction_id, uint16_t schema_version, size_t payload_size,
|
uint32_t transaction_id, uint16_t schema_version, size_t payload_size,
|
||||||
uint32_t payload_crc) {
|
uint32_t payload_crc) {
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "configuration/configuration_storage.h"
|
#include "configuration_storage.h"
|
||||||
|
|
||||||
enum class ConfigurationTransactionStatus : uint8_t {
|
enum class ConfigurationTransactionStatus : uint8_t {
|
||||||
kIdle = 0,
|
kIdle = 0,
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "core/controller_identity.h"
|
#include "controller_identity.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "profile/controller_profile.h"
|
#include "controller_profile.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "core/controller_identity.h"
|
#include "controller_identity.h"
|
||||||
|
|
||||||
constexpr uint16_t CONTROLLER_PROFILE_LEGACY_SCHEMA_VERSION = 1;
|
constexpr uint16_t CONTROLLER_PROFILE_LEGACY_SCHEMA_VERSION = 1;
|
||||||
constexpr uint16_t CONTROLLER_PROFILE_SCHEMA_VERSION = 2;
|
constexpr uint16_t CONTROLLER_PROFILE_SCHEMA_VERSION = 2;
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
#include "profile/controller_profile_runtime.h"
|
#include "controller_profile_runtime.h"
|
||||||
#include "configuration/configuration_service.h"
|
|
||||||
#include "core/controller_identity.h"
|
#include "configuration_service.h"
|
||||||
#include "profile/controller_synthetic_input.h"
|
#include "controller_identity.h"
|
||||||
#include "profile/profile_service.h"
|
#include "controller_synthetic_input.h"
|
||||||
|
#include "profile_service.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "adapter/adapter_usb_mode.h"
|
#include "adapter_usb_mode.h"
|
||||||
|
|
||||||
#include "input/bluepad32_input_backend.h"
|
#include "bluepad32_input_backend.h"
|
||||||
#include "profile/controller_profile_transform.h"
|
#include "controller_profile_transform.h"
|
||||||
|
|
||||||
constexpr uint8_t CONTROLLER_PROFILE_RUNTIME_SLOT_COUNT = 4;
|
constexpr uint8_t CONTROLLER_PROFILE_RUNTIME_SLOT_COUNT = 4;
|
||||||
constexpr uint16_t CONTROLLER_PROFILE_DEFAULT_SWITCHING_CHORD =
|
constexpr uint16_t CONTROLLER_PROFILE_DEFAULT_SWITCHING_CHORD =
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "profile/controller_profile_transform.h"
|
#include "controller_profile_transform.h"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "profile/controller_profile.h"
|
#include "controller_profile.h"
|
||||||
#include "core/controller_state.h"
|
#include "controller_state.h"
|
||||||
#include "usb/switch/switch_haptics.h"
|
#include "switch_haptics.h"
|
||||||
|
|
||||||
struct ControllerProfileTransformResult {
|
struct ControllerProfileTransformResult {
|
||||||
ControllerState state{};
|
ControllerState state{};
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include "profile/controller_synthetic_input.h"
|
#include "controller_synthetic_input.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "profile/controller_profile_transform.h"
|
#include "controller_profile_transform.h"
|
||||||
|
|
||||||
struct ControllerSyntheticBindingState {
|
struct ControllerSyntheticBindingState {
|
||||||
bool active = false;
|
bool active = false;
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "core/controller_state.h"
|
#include "controller_state.h"
|
||||||
|
|
||||||
#ifndef SWITCH_PICO_HID_INSTANCE_COUNT
|
#ifndef SWITCH_PICO_HID_INSTANCE_COUNT
|
||||||
#define SWITCH_PICO_HID_INSTANCE_COUNT 1
|
#define SWITCH_PICO_HID_INSTANCE_COUNT 1
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
#include "usb/generic_hid/generic_hid_driver.h"
|
#include "generic_hid_driver.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "usb/generic_hid/generic_hid_descriptors.h"
|
#include "generic_hid_descriptors.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
@ -2,9 +2,8 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "core/controller_state.h"
|
#include "controller_state.h"
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
|
|
||||||
namespace GenericHid {
|
namespace GenericHid {
|
||||||
|
|
||||||
enum class ReportDescriptorVariant : uint8_t {
|
enum class ReportDescriptorVariant : uint8_t {
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "platform/pico/pico_configuration_storage.h"
|
#include "pico_configuration_storage.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "hardware/flash.h"
|
#include "hardware/flash.h"
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "configuration/configuration_storage.h"
|
#include "configuration_storage.h"
|
||||||
|
|
||||||
ConfigurationStorageIo pico_configuration_storage_io();
|
ConfigurationStorageIo pico_configuration_storage_io();
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
#include "platform/pico/pico_profile_storage.h"
|
#include "pico_profile_storage.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "configuration/configuration_storage.h"
|
#include "configuration_storage.h"
|
||||||
#include "hardware/flash.h"
|
#include "hardware/flash.h"
|
||||||
#include "pico/btstack_flash_bank.h"
|
#include "pico/btstack_flash_bank.h"
|
||||||
#include "pico/flash.h"
|
#include "pico/flash.h"
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "profile/profile_storage.h"
|
#include "profile_storage.h"
|
||||||
|
|
||||||
ProfileStorageIo pico_profile_storage_io();
|
ProfileStorageIo pico_profile_storage_io();
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
#include "profile/profile_service.h"
|
#include "profile_service.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "pico/critical_section.h"
|
#include "pico/critical_section.h"
|
||||||
#include "platform/pico/pico_profile_storage.h"
|
#include "pico_profile_storage.h"
|
||||||
#include "profile/profile_storage.h"
|
#include "profile_storage.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "configuration/configuration_transaction.h"
|
#include "configuration_transaction.h"
|
||||||
#include "profile/controller_profile.h"
|
#include "controller_profile.h"
|
||||||
|
|
||||||
constexpr uint8_t PROFILE_SERVICE_LIST_CAPACITY =
|
constexpr uint8_t PROFILE_SERVICE_LIST_CAPACITY =
|
||||||
CONTROLLER_PROFILE_STABLE_IDENTITY_CAPACITY + 1;
|
CONTROLLER_PROFILE_STABLE_IDENTITY_CAPACITY + 1;
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "profile/profile_storage.h"
|
#include "profile_storage.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "profile/controller_profile.h"
|
#include "controller_profile.h"
|
||||||
|
|
||||||
constexpr uint8_t PROFILE_STORAGE_BANK_COUNT = 2;
|
constexpr uint8_t PROFILE_STORAGE_BANK_COUNT = 2;
|
||||||
constexpr size_t PROFILE_STORAGE_SECTOR_SIZE = 4096;
|
constexpr size_t PROFILE_STORAGE_SECTOR_SIZE = 4096;
|
||||||
|
|
@ -2,15 +2,15 @@
|
||||||
#include "bsp/board.h"
|
#include "bsp/board.h"
|
||||||
#include "pico/stdlib.h"
|
#include "pico/stdlib.h"
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
#include "usb/switch/switch_pro_driver.h"
|
#include "switch_pro_driver.h"
|
||||||
#include "usb/usb_output_driver.h"
|
#include "usb_output_driver.h"
|
||||||
#ifndef SWITCH_PICO_BLUEPAD32
|
#ifndef SWITCH_PICO_BLUEPAD32
|
||||||
#include "hardware/uart.h"
|
#include "hardware/uart.h"
|
||||||
#else
|
#else
|
||||||
#include "adapter/adapter_mode_controller.h"
|
#include "adapter_mode_controller.h"
|
||||||
#include "input/bluepad32_input_backend.h"
|
#include "bluepad32_input_backend.h"
|
||||||
#include "platform/pico/bootsel_pairing_button.h"
|
#include "bootsel_pairing_button.h"
|
||||||
#include "profile/controller_profile_runtime.h"
|
#include "controller_profile_runtime.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SWITCH_PICO_LOG
|
#ifdef SWITCH_PICO_LOG
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "usb/switch/switch_haptics.h"
|
#include "switch_haptics.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include "usb/switch/switch_pro_driver.h"
|
#include "switch_pro_driver.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
@ -81,8 +81,8 @@ static SwitchProContext* context_for(uint8_t instance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional compile-time color palette shared with Bluetooth controller LEDs.
|
// Optional compile-time color palette shared with Bluetooth controller LEDs.
|
||||||
#if __has_include("platform/pico/controller_color_config.h")
|
#if __has_include("controller_color_config.h")
|
||||||
#include "platform/pico/controller_color_config.h"
|
#include "controller_color_config.h"
|
||||||
#endif
|
#endif
|
||||||
#ifndef SWITCH_COLOR_BODY_R
|
#ifndef SWITCH_COLOR_BODY_R
|
||||||
#define SWITCH_COLOR_BODY_R 0x1B
|
#define SWITCH_COLOR_BODY_R 0x1B
|
||||||
|
|
@ -8,11 +8,11 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "core/controller_color.h"
|
#include "controller_color.h"
|
||||||
#include "core/controller_state.h"
|
#include "controller_state.h"
|
||||||
#include "usb/switch/switch_haptics.h"
|
#include "switch_haptics.h"
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
#include "usb/switch/switch_pro_descriptors.h"
|
#include "switch_pro_descriptors.h"
|
||||||
// Preserve the pre-neutral-state 35%-of-1023 digital trigger boundary.
|
// Preserve the pre-neutral-state 35%-of-1023 digital trigger boundary.
|
||||||
constexpr uint32_t SWITCH_PRO_LEGACY_TRIGGER_RANGE_MAXIMUM = 1023;
|
constexpr uint32_t SWITCH_PRO_LEGACY_TRIGGER_RANGE_MAXIMUM = 1023;
|
||||||
constexpr uint32_t SWITCH_PRO_LEGACY_TRIGGER_PRESS_THRESHOLD = 358;
|
constexpr uint32_t SWITCH_PRO_LEGACY_TRIGGER_PRESS_THRESHOLD = 358;
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "adapter/adapter_host_probe.h"
|
#include "adapter_host_probe.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
@ -6,7 +7,7 @@
|
||||||
|
|
||||||
#include "hardware/structs/watchdog.h"
|
#include "hardware/structs/watchdog.h"
|
||||||
#include "pico/time.h"
|
#include "pico/time.h"
|
||||||
#include "usb/xinput/xinput_descriptors.h"
|
#include "xinput_descriptors.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
#include "adapter/adapter_mode_controller.h"
|
#include "adapter_mode_controller.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "configuration/adapter_configuration.h"
|
#include "adapter_configuration.h"
|
||||||
#include "adapter/adapter_reboot.h"
|
#include "adapter_reboot.h"
|
||||||
#include "input/bluepad32_input_backend.h"
|
#include "bluepad32_input_backend.h"
|
||||||
#include "configuration/configuration_service.h"
|
#include "configuration_service.h"
|
||||||
#include "profile/controller_profile.h"
|
#include "controller_profile.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <uni.h>
|
#include <uni.h>
|
||||||
#include "platform/pico/controller_color_config.h"
|
#include "../controller_color_config.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
@ -442,9 +442,8 @@ uint32_t btstack_run_loop_get_time_ms() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#include "core/controller_identity.cpp"
|
#include "../controller_identity.cpp"
|
||||||
#include "input/bluepad32_input_backend.cpp"
|
#include "../bluepad32_input_backend.cpp"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void require_clear_completion_pending() {
|
void require_clear_completion_pending() {
|
||||||
if (expected_pending_clear_token != 0) {
|
if (expected_pending_clear_token != 0) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "platform/pico/bootsel_pairing_button.h"
|
#include "bootsel_pairing_button.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
#include "configuration/adapter_configuration.h"
|
#include "adapter_configuration.h"
|
||||||
#include "configuration/configuration_service.h"
|
#include "configuration_service.h"
|
||||||
#include "configuration/configuration_storage.h"
|
#include "configuration_storage.h"
|
||||||
#include "platform/pico/pico_configuration_storage.h"
|
#include "pico_configuration_storage.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "configuration/adapter_configuration.h"
|
#include "adapter_configuration.h"
|
||||||
#include "configuration/configuration_storage.h"
|
#include "configuration_storage.h"
|
||||||
#include "configuration/configuration_transaction.h"
|
#include "configuration_transaction.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "profile/controller_profile.h"
|
#include "controller_profile.h"
|
||||||
|
|
||||||
constexpr uint8_t kLegacyDefaultProfile[CONTROLLER_PROFILE_ENCODED_SIZE] = {
|
constexpr uint8_t kLegacyDefaultProfile[CONTROLLER_PROFILE_ENCODED_SIZE] = {
|
||||||
0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
|
0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
#include "profile/controller_profile_runtime.h"
|
#include "controller_profile_runtime.h"
|
||||||
#include "core/controller_identity.h"
|
|
||||||
#include "profile/controller_profile.h"
|
#include "controller_identity.h"
|
||||||
#include "profile/profile_service.h"
|
#include "controller_profile.h"
|
||||||
|
#include "profile_service.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include "core/controller_identity.h"
|
#include "controller_identity.h"
|
||||||
#include "profile/controller_profile.h"
|
#include "controller_profile.h"
|
||||||
#include "controller_profile_legacy_fixtures.h"
|
#include "tests/controller_profile_legacy_fixtures.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "core/controller_identity.h"
|
#include "controller_identity.h"
|
||||||
#include "profile/controller_profile.h"
|
#include "controller_profile.h"
|
||||||
#include "profile/controller_profile_transform.h"
|
#include "controller_profile_transform.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "profile/controller_synthetic_input.h"
|
#include "controller_synthetic_input.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "usb/generic_hid/generic_hid_descriptors.h"
|
#include "generic_hid_descriptors.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#include "usb/generic_hid/generic_hid_driver.h"
|
#include "generic_hid_driver.h"
|
||||||
#include "usb/generic_hid/generic_hid_descriptors.h"
|
|
||||||
|
#include "generic_hid_descriptors.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
#include "core/controller_identity.h"
|
#include "controller_identity.h"
|
||||||
#include "profile/controller_profile.h"
|
#include "controller_profile.h"
|
||||||
#include "platform/pico/pico_profile_storage.h"
|
#include "pico_profile_storage.h"
|
||||||
#include "profile/profile_service.h"
|
#include "profile_service.h"
|
||||||
#include "profile/profile_storage.h"
|
#include "profile_storage.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#include "core/controller_identity.h"
|
#include "controller_identity.h"
|
||||||
#include "profile/controller_profile.h"
|
#include "controller_profile.h"
|
||||||
#include "profile/profile_storage.h"
|
#include "profile_storage.h"
|
||||||
#include "controller_profile_legacy_fixtures.h"
|
#include "tests/controller_profile_legacy_fixtures.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "usb/switch/switch_haptics.h"
|
#include "switch_haptics.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "usb/switch/switch_pro_descriptors.h"
|
#include "switch_pro_descriptors.h"
|
||||||
#include "platform/pico/tusb_config.h"
|
#include "tusb_config.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include "usb/switch/switch_pro_driver.h"
|
#include "switch_pro_driver.h"
|
||||||
#include "usb/usb_output_driver.h"
|
#include "usb_output_driver.h"
|
||||||
#include "platform/pico/controller_color_config.h"
|
#include "controller_color_config.h"
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
#include "pico/time.h"
|
#include "pico/time.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ def test_adapter_host_probe_native(tmp_path: Path) -> None:
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
"-DSWITCH_PICO_HID_INSTANCE_COUNT=4",
|
"-DSWITCH_PICO_HID_INSTANCE_COUNT=4",
|
||||||
f"-I{root / 'tests' / 'mode_native_stubs'}",
|
f"-I{root / 'tests' / 'mode_native_stubs'}",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "src" / "firmware" / "adapter" / "adapter_host_probe.cpp"),
|
str(root / "adapter_host_probe.cpp"),
|
||||||
str(root / "tests" / "adapter_host_probe_test.cpp"),
|
str(root / "tests" / "adapter_host_probe_test.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ def test_adapter_mode_controller_native(tmp_path: Path) -> None:
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
"-DSWITCH_PICO_HID_INSTANCE_COUNT=4",
|
"-DSWITCH_PICO_HID_INSTANCE_COUNT=4",
|
||||||
f"-I{root / 'tests' / 'mode_native_stubs'}",
|
f"-I{root / 'tests' / 'mode_native_stubs'}",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "src" / "firmware" / "adapter" / "adapter_mode_controller.cpp"),
|
str(root / "adapter_mode_controller.cpp"),
|
||||||
str(root / "tests" / "adapter_mode_controller_test.cpp"),
|
str(root / "tests" / "adapter_mode_controller_test.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ def test_bluepad32_backend_lifecycle_native(tmp_path: Path) -> None:
|
||||||
command.extend(
|
command.extend(
|
||||||
[
|
[
|
||||||
f"-I{root / 'tests' / 'bluepad32_native_stubs'}",
|
f"-I{root / 'tests' / 'bluepad32_native_stubs'}",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "tests" / "bluepad32_backend_lifecycle_test.cpp"),
|
str(root / "tests" / "bluepad32_backend_lifecycle_test.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ def test_bootsel_pairing_button_native(tmp_path: Path) -> None:
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
f"-DPICO_RP2350={rp2350}",
|
f"-DPICO_RP2350={rp2350}",
|
||||||
f"-I{root / 'tests' / 'bootsel_native_stubs'}",
|
f"-I{root / 'tests' / 'bootsel_native_stubs'}",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "src" / "firmware" / "platform" / "pico" / "bootsel_pairing_button.cpp"),
|
str(root / "bootsel_pairing_button.cpp"),
|
||||||
str(root / "tests" / "bootsel_pairing_button_test.cpp"),
|
str(root / "tests" / "bootsel_pairing_button_test.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@ def test_configuration_service_native(tmp_path: Path) -> None:
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
f"-I{root / 'tests' / 'bluepad32_native_stubs'}",
|
f"-I{root / 'tests' / 'bluepad32_native_stubs'}",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "tests" / "configuration_service_test.cpp"),
|
str(root / "tests" / "configuration_service_test.cpp"),
|
||||||
str(root / "src" / "firmware" / "configuration" / "adapter_configuration.cpp"),
|
str(root / "adapter_configuration.cpp"),
|
||||||
str(root / "src" / "firmware" / "configuration" / "configuration_service.cpp"),
|
str(root / "configuration_service.cpp"),
|
||||||
str(root / "src" / "firmware" / "configuration" / "configuration_storage.cpp"),
|
str(root / "configuration_storage.cpp"),
|
||||||
str(root / "src" / "firmware" / "configuration" / "configuration_transaction.cpp"),
|
str(root / "configuration_transaction.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@ def test_configuration_storage_native(tmp_path: Path) -> None:
|
||||||
"-Wextra",
|
"-Wextra",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "tests" / "configuration_storage_test.cpp"),
|
str(root / "tests" / "configuration_storage_test.cpp"),
|
||||||
str(root / "src" / "firmware" / "configuration" / "adapter_configuration.cpp"),
|
str(root / "adapter_configuration.cpp"),
|
||||||
str(root / "src" / "firmware" / "configuration" / "configuration_storage.cpp"),
|
str(root / "configuration_storage.cpp"),
|
||||||
str(root / "src" / "firmware" / "configuration" / "configuration_transaction.cpp"),
|
str(root / "configuration_transaction.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@ def test_controller_profile_native(tmp_path: Path) -> None:
|
||||||
"-Wextra",
|
"-Wextra",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "tests" / "controller_profile_test.cpp"),
|
str(root / "tests" / "controller_profile_test.cpp"),
|
||||||
str(root / "src" / "firmware" / "core" / "controller_identity.cpp"),
|
str(root / "controller_identity.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "controller_profile.cpp"),
|
str(root / "controller_profile.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,13 @@ def test_controller_profile_runtime_native(tmp_path: Path) -> None:
|
||||||
"-Wextra",
|
"-Wextra",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "tests" / "controller_profile_runtime_test.cpp"),
|
str(root / "tests" / "controller_profile_runtime_test.cpp"),
|
||||||
str(root / "src" / "firmware" / "core" / "controller_identity.cpp"),
|
str(root / "controller_identity.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "controller_profile.cpp"),
|
str(root / "controller_profile.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "controller_profile_transform.cpp"),
|
str(root / "controller_profile_transform.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "controller_synthetic_input.cpp"),
|
str(root / "controller_synthetic_input.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "controller_profile_runtime.cpp"),
|
str(root / "controller_profile_runtime.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@ def test_controller_profile_transform_native(tmp_path: Path) -> None:
|
||||||
"-Wextra",
|
"-Wextra",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "tests" / "controller_profile_transform_test.cpp"),
|
str(root / "tests" / "controller_profile_transform_test.cpp"),
|
||||||
str(root / "src" / "firmware" / "core" / "controller_identity.cpp"),
|
str(root / "controller_identity.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "controller_profile.cpp"),
|
str(root / "controller_profile.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "controller_profile_transform.cpp"),
|
str(root / "controller_profile_transform.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,12 @@ def test_controller_synthetic_input_native(tmp_path: Path) -> None:
|
||||||
"-Wextra",
|
"-Wextra",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "tests" / "controller_synthetic_input_test.cpp"),
|
str(root / "tests" / "controller_synthetic_input_test.cpp"),
|
||||||
str(root / "src" / "firmware" / "core" / "controller_identity.cpp"),
|
str(root / "controller_identity.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "controller_profile.cpp"),
|
str(root / "controller_profile.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "controller_profile_transform.cpp"),
|
str(root / "controller_profile_transform.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "controller_synthetic_input.cpp"),
|
str(root / "controller_synthetic_input.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ def compile_cpp(
|
||||||
command.append(f"-I{root / 'tests' / 'native_stubs'}")
|
command.append(f"-I{root / 'tests' / 'native_stubs'}")
|
||||||
command.extend(
|
command.extend(
|
||||||
[
|
[
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
*[str(source) for source in sources],
|
*[str(source) for source in sources],
|
||||||
"-o",
|
"-o",
|
||||||
str(output),
|
str(output),
|
||||||
|
|
@ -98,7 +98,7 @@ def test_generic_hid_driver_contexts(tmp_path: Path) -> None:
|
||||||
host_compiler(),
|
host_compiler(),
|
||||||
executable,
|
executable,
|
||||||
[
|
[
|
||||||
root / "src" / "firmware" / "usb" / "generic_hid" / "generic_hid_driver.cpp",
|
root / "generic_hid_driver.cpp",
|
||||||
root / "tests" / "generic_hid_driver_test.cpp",
|
root / "tests" / "generic_hid_driver_test.cpp",
|
||||||
],
|
],
|
||||||
["SWITCH_PICO_HID_INSTANCE_COUNT=4"],
|
["SWITCH_PICO_HID_INSTANCE_COUNT=4"],
|
||||||
|
|
|
||||||
|
|
@ -1,194 +1,339 @@
|
||||||
from __future__ import annotations
|
"""
|
||||||
|
Tests for prepare_bluepad32.py patch preparation tool.
|
||||||
|
|
||||||
|
Tests cover:
|
||||||
|
- Fresh patch application
|
||||||
|
- Idempotence (second invocation succeeds without changing content)
|
||||||
|
- Missing paths validation
|
||||||
|
- Diverged/ambiguous repository states
|
||||||
|
"""
|
||||||
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import tempfile
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import patch as mock_patch
|
||||||
|
|
||||||
|
import sys
|
||||||
sys.path.insert(0, str(Path(__file__).parent.parent / "tools"))
|
sys.path.insert(0, str(Path(__file__).parent.parent / "tools"))
|
||||||
|
|
||||||
from prepare_bluepad32 import ( # noqa: E402
|
from prepare_bluepad32 import (
|
||||||
PatchError,
|
PatchError,
|
||||||
check_paths,
|
|
||||||
main,
|
|
||||||
prepare_bluepad32,
|
|
||||||
resolve_paths,
|
resolve_paths,
|
||||||
|
check_paths,
|
||||||
|
is_patch_applied,
|
||||||
|
apply_patch,
|
||||||
|
prepare_bluepad32,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_git(repository: Path, *args: str) -> subprocess.CompletedProcess[str]:
|
@pytest.fixture
|
||||||
return subprocess.run(
|
def temp_repo_structure():
|
||||||
["git", "-C", str(repository), *args],
|
"""Create a temporary directory structure with git repositories."""
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
root = Path(tmpdir)
|
||||||
|
|
||||||
|
# Create bluepad32 repo
|
||||||
|
bp_dir = root / "external" / "bluepad32"
|
||||||
|
bp_dir.mkdir(parents=True)
|
||||||
|
subprocess.run(["git", "init"], cwd=bp_dir, check=True, capture_output=True)
|
||||||
|
subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=bp_dir, check=True, capture_output=True)
|
||||||
|
subprocess.run(["git", "config", "user.name", "Test User"], cwd=bp_dir, check=True, capture_output=True)
|
||||||
|
|
||||||
|
# Create a file to patch
|
||||||
|
test_file = bp_dir / "test.txt"
|
||||||
|
test_file.write_text("line 1\n")
|
||||||
|
subprocess.run(["git", "add", "test.txt"], cwd=bp_dir, check=True, capture_output=True)
|
||||||
|
subprocess.run(["git", "commit", "-m", "initial"], cwd=bp_dir, check=True, capture_output=True)
|
||||||
|
|
||||||
|
# Create patches dir
|
||||||
|
patches_dir = root / "patches"
|
||||||
|
patches_dir.mkdir()
|
||||||
|
|
||||||
|
yield root, bp_dir, patches_dir
|
||||||
|
|
||||||
|
|
||||||
|
def create_simple_patch(repo_path: Path, patch_path: Path, file_to_patch: str = "test.txt") -> str:
|
||||||
|
"""
|
||||||
|
Create a simple patch file that modifies a file in the repository.
|
||||||
|
|
||||||
|
Returns the patch content as a string.
|
||||||
|
"""
|
||||||
|
# Create the modification
|
||||||
|
test_file = repo_path / file_to_patch
|
||||||
|
original_content = test_file.read_text()
|
||||||
|
modified_content = original_content + "line 2\n"
|
||||||
|
|
||||||
|
# Generate patch using git diff
|
||||||
|
test_file.write_text(modified_content)
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "diff", file_to_patch],
|
||||||
|
cwd=repo_path,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
|
patch_content = result.stdout
|
||||||
|
|
||||||
|
# Reset the file to original state
|
||||||
|
test_file.write_text(original_content)
|
||||||
|
|
||||||
|
# Write patch to file
|
||||||
|
patch_path.write_text(patch_content)
|
||||||
|
return patch_content
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
def test_resolve_paths_with_defaults():
|
||||||
def bluepad32_fixture(tmp_path: Path) -> tuple[Path, Path, Path, Path]:
|
"""Test that resolve_paths returns expected default paths."""
|
||||||
source = tmp_path / "external" / "bluepad32"
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
source.mkdir(parents=True)
|
bp_path, patch_path = resolve_paths(Path(tmpdir))
|
||||||
run_git(source, "init")
|
assert bp_path == Path(tmpdir) / "external" / "bluepad32"
|
||||||
run_git(source, "config", "user.email", "test@example.com")
|
assert patch_path == Path(tmpdir) / "patches" / "bluepad32-sdl3-imu.patch"
|
||||||
run_git(source, "config", "user.name", "Test User")
|
|
||||||
(source / "test.txt").write_text("line 1\n", encoding="utf-8")
|
|
||||||
run_git(source, "add", "test.txt")
|
|
||||||
run_git(source, "commit", "-m", "initial")
|
|
||||||
|
|
||||||
patches = tmp_path / "patches"
|
|
||||||
patches.mkdir()
|
|
||||||
patch = patches / "bluepad32-sdl3-imu.patch"
|
|
||||||
original = (source / "test.txt").read_text(encoding="utf-8")
|
|
||||||
(source / "test.txt").write_text(f"{original}line 2\n", encoding="utf-8")
|
|
||||||
patch.write_text(run_git(source, "diff", "--", "test.txt").stdout, encoding="utf-8")
|
|
||||||
run_git(source, "restore", "test.txt")
|
|
||||||
|
|
||||||
output = tmp_path / "build-aio" / "_deps" / "bluepad32-src"
|
|
||||||
return tmp_path, source, patch, output
|
|
||||||
|
|
||||||
|
|
||||||
def source_status(source: Path) -> str:
|
def test_resolve_paths_no_root():
|
||||||
return run_git(source, "status", "--porcelain", "--untracked-files=all").stdout
|
"""Test resolve_paths with no root uses current directory."""
|
||||||
|
bp_path, patch_path = resolve_paths()
|
||||||
|
assert bp_path.is_absolute()
|
||||||
|
assert patch_path.is_absolute()
|
||||||
|
|
||||||
|
|
||||||
def test_resolve_paths_uses_project_build_tree(tmp_path: Path) -> None:
|
def test_check_paths_missing_bluepad32(temp_repo_structure):
|
||||||
source, patch, output = resolve_paths(tmp_path)
|
"""Test that check_paths fails if bluepad32 dir is missing."""
|
||||||
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
assert source == tmp_path / "external" / "bluepad32"
|
|
||||||
assert patch == tmp_path / "patches" / "bluepad32-sdl3-imu.patch"
|
# Remove bluepad32
|
||||||
assert output == tmp_path / "build" / "_deps" / "bluepad32-src"
|
import shutil
|
||||||
|
shutil.rmtree(bp_dir)
|
||||||
|
|
||||||
def test_check_paths_rejects_missing_source(
|
patch_file = patches_dir / "test.patch"
|
||||||
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
patch_file.write_text("dummy")
|
||||||
) -> None:
|
|
||||||
_, source, patch, output = bluepad32_fixture
|
|
||||||
shutil.rmtree(source)
|
|
||||||
|
|
||||||
with pytest.raises(PatchError, match="bluepad32 directory does not exist"):
|
with pytest.raises(PatchError, match="bluepad32 directory does not exist"):
|
||||||
check_paths(source, patch, output)
|
check_paths(bp_dir, patch_file)
|
||||||
|
|
||||||
|
|
||||||
def test_check_paths_rejects_missing_patch(
|
def test_check_paths_missing_patch(temp_repo_structure):
|
||||||
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
"""Test that check_paths fails if patch file is missing."""
|
||||||
) -> None:
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
_, source, patch, output = bluepad32_fixture
|
|
||||||
patch.unlink()
|
patch_file = patches_dir / "nonexistent.patch"
|
||||||
|
|
||||||
with pytest.raises(PatchError, match="patch file does not exist"):
|
with pytest.raises(PatchError, match="patch file does not exist"):
|
||||||
check_paths(source, patch, output)
|
check_paths(bp_dir, patch_file)
|
||||||
|
|
||||||
|
|
||||||
def test_check_paths_rejects_non_repository(
|
def test_check_paths_bluepad32_not_git_repo(temp_repo_structure):
|
||||||
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
"""Test that check_paths fails if bluepad32 is not a git repo."""
|
||||||
) -> None:
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
_, source, patch, output = bluepad32_fixture
|
|
||||||
shutil.rmtree(source / ".git")
|
# Remove .git to make it not a git repo
|
||||||
|
import shutil
|
||||||
|
shutil.rmtree(bp_dir / ".git")
|
||||||
|
|
||||||
|
patch_file = patches_dir / "test.patch"
|
||||||
|
patch_file.write_text("dummy")
|
||||||
|
|
||||||
with pytest.raises(PatchError, match="not a git repository"):
|
with pytest.raises(PatchError, match="not a git repository"):
|
||||||
check_paths(source, patch, output)
|
check_paths(bp_dir, patch_file)
|
||||||
|
|
||||||
|
|
||||||
def test_check_paths_rejects_output_inside_source(
|
def test_fresh_patch_application(temp_repo_structure):
|
||||||
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
"""Test applying a fresh patch to a clean repository."""
|
||||||
) -> None:
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
_, source, patch, _ = bluepad32_fixture
|
|
||||||
|
patch_file = patches_dir / "test.patch"
|
||||||
with pytest.raises(PatchError, match="outside the Bluepad32 source tree"):
|
create_simple_patch(bp_dir, patch_file, "test.txt")
|
||||||
check_paths(source, patch, source / "patched")
|
|
||||||
|
# Verify test.txt before patch
|
||||||
|
test_file = bp_dir / "test.txt"
|
||||||
|
original = test_file.read_text()
|
||||||
|
assert "line 2" not in original
|
||||||
|
|
||||||
|
# Apply patch
|
||||||
|
apply_patch(bp_dir, patch_file)
|
||||||
|
|
||||||
|
# Verify test.txt after patch
|
||||||
|
patched = test_file.read_text()
|
||||||
|
assert "line 2" in patched
|
||||||
|
|
||||||
|
|
||||||
def test_prepare_patches_copy_and_preserves_pristine_source(
|
def test_idempotent_patch_application(temp_repo_structure):
|
||||||
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
"""Test that applying the same patch twice succeeds (idempotence)."""
|
||||||
) -> None:
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
_, source, patch, output = bluepad32_fixture
|
|
||||||
|
patch_file = patches_dir / "test.patch"
|
||||||
result = prepare_bluepad32(source, patch, output)
|
create_simple_patch(bp_dir, patch_file, "test.txt")
|
||||||
|
|
||||||
assert result == output
|
# First application
|
||||||
assert (source / "test.txt").read_text(encoding="utf-8") == "line 1\n"
|
apply_patch(bp_dir, patch_file)
|
||||||
assert (output / "test.txt").read_text(encoding="utf-8") == "line 1\nline 2\n"
|
test_file = bp_dir / "test.txt"
|
||||||
assert not (output / ".git").exists()
|
after_first = test_file.read_text()
|
||||||
assert source_status(source) == ""
|
|
||||||
|
# Second application should succeed without changing content
|
||||||
|
apply_patch(bp_dir, patch_file)
|
||||||
|
after_second = test_file.read_text()
|
||||||
|
|
||||||
|
assert after_first == after_second
|
||||||
|
|
||||||
|
|
||||||
def test_prepare_replaces_existing_output_idempotently(
|
def test_is_patch_applied_not_applied(temp_repo_structure):
|
||||||
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
"""Test is_patch_applied returns False for unapplied patch."""
|
||||||
) -> None:
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
_, source, patch, output = bluepad32_fixture
|
|
||||||
prepare_bluepad32(source, patch, output)
|
patch_file = patches_dir / "test.patch"
|
||||||
(output / "stale.txt").write_text("stale", encoding="utf-8")
|
create_simple_patch(bp_dir, patch_file, "test.txt")
|
||||||
|
|
||||||
prepare_bluepad32(source, patch, output)
|
# Patch not applied yet
|
||||||
|
assert is_patch_applied(bp_dir, patch_file) is False
|
||||||
assert not (output / "stale.txt").exists()
|
|
||||||
assert (output / "test.txt").read_text(encoding="utf-8") == "line 1\nline 2\n"
|
|
||||||
assert source_status(source) == ""
|
|
||||||
|
|
||||||
|
|
||||||
def test_prepare_rejects_dirty_source_without_replacing_output(
|
def test_is_patch_applied_already_applied(temp_repo_structure):
|
||||||
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
"""Test is_patch_applied returns True for already applied patch."""
|
||||||
) -> None:
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
_, source, patch, output = bluepad32_fixture
|
|
||||||
output.mkdir(parents=True)
|
patch_file = patches_dir / "test.patch"
|
||||||
sentinel = output / "sentinel.txt"
|
create_simple_patch(bp_dir, patch_file, "test.txt")
|
||||||
sentinel.write_text("keep", encoding="utf-8")
|
|
||||||
(source / "test.txt").write_text("local edit\n", encoding="utf-8")
|
# Apply patch first
|
||||||
|
apply_patch(bp_dir, patch_file)
|
||||||
with pytest.raises(PatchError, match="must be pristine"):
|
|
||||||
prepare_bluepad32(source, patch, output)
|
# Now check should detect it's applied
|
||||||
|
assert is_patch_applied(bp_dir, patch_file) is True
|
||||||
assert sentinel.read_text(encoding="utf-8") == "keep"
|
|
||||||
|
|
||||||
|
|
||||||
def test_prepare_rejects_diverged_source_revision(
|
def test_diverged_repository_state(temp_repo_structure):
|
||||||
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
"""Test that diverged repository (patch doesn't apply cleanly) is rejected."""
|
||||||
) -> None:
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
_, source, patch, output = bluepad32_fixture
|
|
||||||
(source / "test.txt").write_text("different base\n", encoding="utf-8")
|
patch_file = patches_dir / "test.patch"
|
||||||
run_git(source, "add", "test.txt")
|
create_simple_patch(bp_dir, patch_file, "test.txt")
|
||||||
run_git(source, "commit", "-m", "diverge")
|
|
||||||
|
# Diverge the repository by modifying the file such that the patch conflicts
|
||||||
|
test_file = bp_dir / "test.txt"
|
||||||
|
test_file.write_text("completely different line 1\n")
|
||||||
|
subprocess.run(["git", "add", "test.txt"], cwd=bp_dir, check=True, capture_output=True)
|
||||||
|
subprocess.run(["git", "commit", "-m", "divergence"], cwd=bp_dir, check=True, capture_output=True)
|
||||||
|
|
||||||
|
# Try to apply patch - should fail because file content doesn't match
|
||||||
with pytest.raises(PatchError, match="Patch validation failed"):
|
with pytest.raises(PatchError, match="Patch validation failed"):
|
||||||
prepare_bluepad32(source, patch, output)
|
apply_patch(bp_dir, patch_file)
|
||||||
|
|
||||||
assert not output.exists()
|
|
||||||
|
|
||||||
|
|
||||||
def test_prepare_uses_default_paths(
|
def test_missing_bluepad32_path(temp_repo_structure):
|
||||||
bluepad32_fixture: tuple[Path, Path, Path, Path], monkeypatch: pytest.MonkeyPatch
|
"""Test prepare_bluepad32 fails gracefully with missing bluepad32."""
|
||||||
) -> None:
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
root, source, _, _ = bluepad32_fixture
|
|
||||||
monkeypatch.chdir(root)
|
import shutil
|
||||||
|
shutil.rmtree(bp_dir)
|
||||||
output = prepare_bluepad32()
|
|
||||||
|
patch_file = patches_dir / "test.patch"
|
||||||
assert output == root / "build" / "_deps" / "bluepad32-src"
|
patch_file.write_text("dummy")
|
||||||
assert (output / "test.txt").read_text(encoding="utf-8") == "line 1\nline 2\n"
|
|
||||||
assert source_status(source) == ""
|
with pytest.raises(PatchError, match="bluepad32 directory does not exist"):
|
||||||
|
prepare_bluepad32(bp_dir, patch_file)
|
||||||
|
|
||||||
|
|
||||||
def test_cli_accepts_explicit_output(
|
def test_missing_patch_file(temp_repo_structure):
|
||||||
bluepad32_fixture: tuple[Path, Path, Path, Path], monkeypatch: pytest.MonkeyPatch
|
"""Test prepare_bluepad32 fails gracefully with missing patch file."""
|
||||||
) -> None:
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
_, source, patch, output = bluepad32_fixture
|
|
||||||
monkeypatch.setattr(
|
patch_file = patches_dir / "nonexistent.patch"
|
||||||
sys,
|
|
||||||
"argv",
|
with pytest.raises(PatchError, match="patch file does not exist"):
|
||||||
[
|
prepare_bluepad32(bp_dir, patch_file)
|
||||||
"prepare_bluepad32.py",
|
|
||||||
"--bluepad32",
|
|
||||||
str(source),
|
|
||||||
"--patch",
|
|
||||||
str(patch),
|
|
||||||
"--output",
|
|
||||||
str(output),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
assert main() == 0
|
|
||||||
assert (output / "test.txt").read_text(encoding="utf-8") == "line 1\nline 2\n"
|
def test_prepare_bluepad32_full_workflow(temp_repo_structure):
|
||||||
assert source_status(source) == ""
|
"""Test complete prepare_bluepad32 workflow: apply then idempotent re-apply."""
|
||||||
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
|
|
||||||
|
patch_file = patches_dir / "test.patch"
|
||||||
|
create_simple_patch(bp_dir, patch_file, "test.txt")
|
||||||
|
|
||||||
|
test_file = bp_dir / "test.txt"
|
||||||
|
original = test_file.read_text()
|
||||||
|
|
||||||
|
# First prepare (should apply patch)
|
||||||
|
prepare_bluepad32(bp_dir, patch_file)
|
||||||
|
after_first = test_file.read_text()
|
||||||
|
assert after_first != original
|
||||||
|
assert "line 2" in after_first
|
||||||
|
|
||||||
|
# Second prepare (should be idempotent)
|
||||||
|
prepare_bluepad32(bp_dir, patch_file)
|
||||||
|
after_second = test_file.read_text()
|
||||||
|
assert after_first == after_second
|
||||||
|
|
||||||
|
|
||||||
|
def test_prepare_bluepad32_with_defaults(temp_repo_structure):
|
||||||
|
"""Test prepare_bluepad32 uses correct defaults when paths not provided."""
|
||||||
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
|
|
||||||
|
patch_file = patches_dir / "bluepad32-sdl3-imu.patch"
|
||||||
|
create_simple_patch(bp_dir, patch_file, "test.txt")
|
||||||
|
|
||||||
|
# Change to root directory and call with defaults
|
||||||
|
import os
|
||||||
|
original_cwd = os.getcwd()
|
||||||
|
try:
|
||||||
|
os.chdir(root)
|
||||||
|
prepare_bluepad32() # Use defaults
|
||||||
|
finally:
|
||||||
|
os.chdir(original_cwd)
|
||||||
|
|
||||||
|
# Verify patch was applied
|
||||||
|
test_file = bp_dir / "test.txt"
|
||||||
|
assert "line 2" in test_file.read_text()
|
||||||
|
|
||||||
|
|
||||||
|
def test_patch_application_with_conflicting_content(temp_repo_structure):
|
||||||
|
"""Test that patch with conflicting content is rejected."""
|
||||||
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
|
|
||||||
|
# Create a patch that adds a specific change
|
||||||
|
patch_content = """--- a/test.txt
|
||||||
|
+++ b/test.txt
|
||||||
|
@@ -1 +1,3 @@
|
||||||
|
line 1
|
||||||
|
+line 2
|
||||||
|
+line 3
|
||||||
|
"""
|
||||||
|
|
||||||
|
patch_file = patches_dir / "conflict.patch"
|
||||||
|
patch_file.write_text(patch_content)
|
||||||
|
|
||||||
|
# Modify the file to have different content that won't match the patch context
|
||||||
|
test_file = bp_dir / "test.txt"
|
||||||
|
test_file.write_text("modified line 1\n")
|
||||||
|
subprocess.run(["git", "add", "test.txt"], cwd=bp_dir, check=True, capture_output=True)
|
||||||
|
subprocess.run(["git", "commit", "-m", "modify"], cwd=bp_dir, check=True, capture_output=True)
|
||||||
|
|
||||||
|
# Try to apply patch - should fail due to context mismatch
|
||||||
|
with pytest.raises(PatchError):
|
||||||
|
apply_patch(bp_dir, patch_file)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cli_with_explicit_paths(temp_repo_structure):
|
||||||
|
"""Test CLI argument parsing with explicit paths."""
|
||||||
|
root, bp_dir, patches_dir = temp_repo_structure
|
||||||
|
|
||||||
|
patch_file = patches_dir / "test.patch"
|
||||||
|
create_simple_patch(bp_dir, patch_file, "test.txt")
|
||||||
|
|
||||||
|
# Simulate CLI call
|
||||||
|
sys.argv = [
|
||||||
|
"prepare_bluepad32.py",
|
||||||
|
"--bluepad32", str(bp_dir),
|
||||||
|
"--patch", str(patch_file),
|
||||||
|
]
|
||||||
|
|
||||||
|
from prepare_bluepad32 import main
|
||||||
|
|
||||||
|
# Should not raise
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except SystemExit as e:
|
||||||
|
# main() calls sys.exit on success, which we need to catch
|
||||||
|
if e.code != 0:
|
||||||
|
raise
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@ def test_profile_service_native(tmp_path: Path) -> None:
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
f"-I{root / 'tests' / 'bluepad32_native_stubs'}",
|
f"-I{root / 'tests' / 'bluepad32_native_stubs'}",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "tests" / "profile_service_test.cpp"),
|
str(root / "tests" / "profile_service_test.cpp"),
|
||||||
str(root / "src" / "firmware" / "core" / "controller_identity.cpp"),
|
str(root / "controller_identity.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "controller_profile.cpp"),
|
str(root / "controller_profile.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "profile_storage.cpp"),
|
str(root / "profile_storage.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "profile_service.cpp"),
|
str(root / "profile_service.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@ def test_profile_storage_native(tmp_path: Path) -> None:
|
||||||
"-Wextra",
|
"-Wextra",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "tests" / "profile_storage_test.cpp"),
|
str(root / "tests" / "profile_storage_test.cpp"),
|
||||||
str(root / "src" / "firmware" / "core" / "controller_identity.cpp"),
|
str(root / "controller_identity.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "controller_profile.cpp"),
|
str(root / "controller_profile.cpp"),
|
||||||
str(root / "src" / "firmware" / "profile" / "profile_storage.cpp"),
|
str(root / "profile_storage.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ def test_switch_haptics_native(tmp_path: Path) -> None:
|
||||||
"-Wextra",
|
"-Wextra",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "src" / "firmware" / "usb" / "switch" / "switch_haptics.cpp"),
|
str(root / "switch_haptics.cpp"),
|
||||||
str(root / "tests" / "switch_haptics_test.cpp"),
|
str(root / "tests" / "switch_haptics_test.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ def compile_descriptor_test(
|
||||||
command.append(f"-DSWITCH_PICO_HID_INSTANCE_COUNT={configured_count}")
|
command.append(f"-DSWITCH_PICO_HID_INSTANCE_COUNT={configured_count}")
|
||||||
command.extend(
|
command.extend(
|
||||||
[
|
[
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "tests" / "switch_pro_descriptors_test.cpp"),
|
str(root / "tests" / "switch_pro_descriptors_test.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(output),
|
str(output),
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,10 @@ def test_switch_pro_driver_four_contexts_native(tmp_path: Path) -> None:
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
"-DSWITCH_PICO_HID_INSTANCE_COUNT=4",
|
"-DSWITCH_PICO_HID_INSTANCE_COUNT=4",
|
||||||
f"-I{root / 'tests' / 'native_stubs'}",
|
f"-I{root / 'tests' / 'native_stubs'}",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "src" / "firmware" / "usb" / "switch" / "switch_pro_driver.cpp"),
|
str(root / "switch_pro_driver.cpp"),
|
||||||
str(root / "src" / "firmware" / "usb" / "usb_output_driver.cpp"),
|
str(root / "usb_output_driver.cpp"),
|
||||||
str(root / "src" / "firmware" / "usb" / "switch" / "switch_haptics.cpp"),
|
str(root / "switch_haptics.cpp"),
|
||||||
str(root / "tests" / "switch_pro_driver_context_test.cpp"),
|
str(root / "tests" / "switch_pro_driver_context_test.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ def test_usb_configuration_management_native(tmp_path: Path) -> None:
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
f"-I{root / 'tests' / 'usb_management_native_stubs'}",
|
f"-I{root / 'tests' / 'usb_management_native_stubs'}",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "tests" / "usb_configuration_management_test.cpp"),
|
str(root / "tests" / "usb_configuration_management_test.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,13 @@ def test_usb_output_driver_contracts(tmp_path: Path) -> None:
|
||||||
"-DSWITCH_PICO_USB_OUTPUT_MODES=1",
|
"-DSWITCH_PICO_USB_OUTPUT_MODES=1",
|
||||||
*backend_definitions,
|
*backend_definitions,
|
||||||
f"-I{root / 'tests' / 'native_stubs'}",
|
f"-I{root / 'tests' / 'native_stubs'}",
|
||||||
f"-I{root / 'src' / 'firmware'}",
|
f"-I{root}",
|
||||||
str(root / "tests" / "usb_output_driver_test.cpp"),
|
str(root / "tests" / "usb_output_driver_test.cpp"),
|
||||||
str(root / "src" / "firmware" / "usb" / "switch" / "switch_pro_driver.cpp"),
|
str(root / "switch_pro_driver.cpp"),
|
||||||
str(root / "src" / "firmware" / "usb" / "usb_output_driver.cpp"),
|
str(root / "usb_output_driver.cpp"),
|
||||||
str(root / "src" / "firmware" / "usb" / "generic_hid" / "generic_hid_driver.cpp"),
|
str(root / "generic_hid_driver.cpp"),
|
||||||
str(root / "src" / "firmware" / "usb" / "xinput" / "xinput_driver.cpp"),
|
str(root / "xinput_driver.cpp"),
|
||||||
str(root / "src" / "firmware" / "usb" / "switch" / "switch_haptics.cpp"),
|
str(root / "switch_haptics.cpp"),
|
||||||
"-o",
|
"-o",
|
||||||
str(executable),
|
str(executable),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
#include "usb/usb_configuration_management.h"
|
#include "usb_configuration_management.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <tusb.h>
|
#include <tusb.h>
|
||||||
#include "usb/usb_output_driver.h"
|
#include "usb_output_driver.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
@ -787,10 +788,10 @@ bool tud_control_status(uint8_t, const tusb_control_request_t*) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "configuration/adapter_configuration.cpp"
|
#include "../adapter_configuration.cpp"
|
||||||
#include "core/controller_identity.cpp"
|
#include "../controller_identity.cpp"
|
||||||
#include "profile/controller_profile.cpp"
|
#include "../controller_profile.cpp"
|
||||||
#include "usb/usb_configuration_management.cpp"
|
#include "../usb_configuration_management.cpp"
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
current_configuration.state = ConfigurationServiceState::kReady;
|
current_configuration.state = ConfigurationServiceState::kReady;
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,17 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "adapter/adapter_host_probe_state.h"
|
#include "adapter_host_probe_state.h"
|
||||||
#include "usb/generic_hid/generic_hid_descriptors.h"
|
#include "generic_hid_descriptors.h"
|
||||||
#include "usb/generic_hid/generic_hid_driver.h"
|
#include "generic_hid_driver.h"
|
||||||
#include "usb/xinput/xinput_descriptors.h"
|
#include "xinput_descriptors.h"
|
||||||
#include "usb/xinput/xinput_protocol.h"
|
#include "xinput_protocol.h"
|
||||||
#include "device/usbd_pvt.h"
|
#include "device/usbd_pvt.h"
|
||||||
#include "pico/time.h"
|
#include "pico/time.h"
|
||||||
#include "usb/switch/switch_pro_driver.h"
|
#include "switch_pro_driver.h"
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
#include "usb/usb_output_driver.h"
|
#include "usb_output_driver.h"
|
||||||
#include "usb/xinput/xinput_driver.h"
|
#include "xinput_driver.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Create a patched build-local copy of the pinned Bluepad32 source."""
|
"""Apply the project Bluepad32 patch exactly once."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import shutil
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
@ -13,125 +12,69 @@ class PatchError(RuntimeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def resolve_paths(repo_root: Path | None = None) -> tuple[Path, Path, Path]:
|
def resolve_paths(repo_root: Path | None = None) -> tuple[Path, Path]:
|
||||||
root = Path.cwd() if repo_root is None else Path(repo_root)
|
root = Path.cwd() if repo_root is None else Path(repo_root)
|
||||||
return (
|
return (
|
||||||
root / "external" / "bluepad32",
|
root / "external" / "bluepad32",
|
||||||
root / "patches" / "bluepad32-sdl3-imu.patch",
|
root / "patches" / "bluepad32-sdl3-imu.patch",
|
||||||
root / "build" / "_deps" / "bluepad32-src",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _run(command: list[str], *, cwd: Path | None = None) -> subprocess.CompletedProcess[str]:
|
def check_paths(bluepad32_path: Path, patch_path: Path) -> None:
|
||||||
|
if not bluepad32_path.is_dir():
|
||||||
|
raise PatchError(f"bluepad32 directory does not exist: {bluepad32_path}")
|
||||||
|
if not (bluepad32_path / ".git").exists():
|
||||||
|
raise PatchError(f"Bluepad32 is not a git repository: {bluepad32_path}")
|
||||||
|
if not patch_path.is_file():
|
||||||
|
raise PatchError(f"patch file does not exist: {patch_path}")
|
||||||
|
|
||||||
|
|
||||||
|
def git_apply(bluepad32_path: Path, patch_path: Path, *args: str) -> subprocess.CompletedProcess[str]:
|
||||||
return subprocess.run(
|
return subprocess.run(
|
||||||
command,
|
["git", "-C", str(bluepad32_path), "apply", *args, str(patch_path)],
|
||||||
cwd=cwd,
|
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
check=False,
|
check=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def check_paths(source_path: Path, patch_path: Path, output_path: Path) -> None:
|
def is_patch_applied(bluepad32_path: Path, patch_path: Path) -> bool:
|
||||||
if not source_path.is_dir():
|
return git_apply(bluepad32_path, patch_path, "--reverse", "--check").returncode == 0
|
||||||
raise PatchError(f"bluepad32 directory does not exist: {source_path}")
|
|
||||||
if not patch_path.is_file():
|
|
||||||
raise PatchError(f"patch file does not exist: {patch_path}")
|
|
||||||
|
|
||||||
repository_check = _run(
|
|
||||||
["git", "-C", str(source_path), "rev-parse", "--is-inside-work-tree"]
|
|
||||||
)
|
|
||||||
if repository_check.returncode != 0:
|
|
||||||
raise PatchError(f"Bluepad32 is not a git repository: {source_path}")
|
|
||||||
|
|
||||||
source = source_path.resolve()
|
|
||||||
output = output_path.resolve()
|
|
||||||
if output == source or source in output.parents:
|
|
||||||
raise PatchError("patched output must be outside the Bluepad32 source tree")
|
|
||||||
|
|
||||||
|
|
||||||
def validate_pristine_source(source_path: Path, patch_path: Path) -> None:
|
def apply_patch(bluepad32_path: Path, patch_path: Path) -> None:
|
||||||
status = _run(
|
if is_patch_applied(bluepad32_path, patch_path):
|
||||||
[
|
return
|
||||||
"git",
|
|
||||||
"-C",
|
|
||||||
str(source_path),
|
|
||||||
"status",
|
|
||||||
"--porcelain",
|
|
||||||
"--untracked-files=all",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
if status.returncode != 0:
|
|
||||||
detail = status.stderr.strip() or "git status failed"
|
|
||||||
raise PatchError(f"Could not inspect Bluepad32 source: {detail}")
|
|
||||||
if status.stdout.strip():
|
|
||||||
raise PatchError(
|
|
||||||
"Bluepad32 source must be pristine; patches are applied only to the build-local copy"
|
|
||||||
)
|
|
||||||
|
|
||||||
check = _run(
|
check = git_apply(bluepad32_path, patch_path, "--check")
|
||||||
["git", "-C", str(source_path), "apply", "--check", str(patch_path)]
|
|
||||||
)
|
|
||||||
if check.returncode != 0:
|
if check.returncode != 0:
|
||||||
detail = check.stderr.strip() or "patch does not apply"
|
detail = check.stderr.strip() or "patch does not apply"
|
||||||
raise PatchError(f"Patch validation failed (source revision may have diverged):\n{detail}")
|
raise PatchError(f"Patch validation failed (repository may be diverged):\n{detail}")
|
||||||
|
|
||||||
|
result = git_apply(bluepad32_path, patch_path)
|
||||||
def _copy_and_patch(source_path: Path, patch_path: Path, output_path: Path) -> None:
|
if result.returncode != 0:
|
||||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
detail = result.stderr.strip() or "git apply failed"
|
||||||
staging_path = output_path.with_name(f".{output_path.name}.tmp")
|
raise PatchError(f"Could not patch Bluepad32: {detail}")
|
||||||
|
|
||||||
if staging_path.exists():
|
|
||||||
shutil.rmtree(staging_path)
|
|
||||||
|
|
||||||
try:
|
|
||||||
shutil.copytree(
|
|
||||||
source_path,
|
|
||||||
staging_path,
|
|
||||||
symlinks=True,
|
|
||||||
ignore=shutil.ignore_patterns(".git"),
|
|
||||||
)
|
|
||||||
result = _run(
|
|
||||||
["git", "apply", "--no-index", str(patch_path.resolve())],
|
|
||||||
cwd=staging_path,
|
|
||||||
)
|
|
||||||
if result.returncode != 0:
|
|
||||||
detail = result.stderr.strip() or "git apply failed"
|
|
||||||
raise PatchError(f"Could not patch build-local Bluepad32 copy: {detail}")
|
|
||||||
|
|
||||||
if output_path.exists():
|
|
||||||
shutil.rmtree(output_path)
|
|
||||||
staging_path.replace(output_path)
|
|
||||||
except Exception:
|
|
||||||
if staging_path.exists():
|
|
||||||
shutil.rmtree(staging_path)
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_bluepad32(
|
def prepare_bluepad32(
|
||||||
source_path: Path | None = None,
|
bluepad32_path: Path | None = None,
|
||||||
patch_path: Path | None = None,
|
patch_path: Path | None = None,
|
||||||
output_path: Path | None = None,
|
) -> None:
|
||||||
) -> Path:
|
default_bluepad32, default_patch = resolve_paths()
|
||||||
default_source, default_patch, default_output = resolve_paths()
|
dependency = Path(bluepad32_path or default_bluepad32)
|
||||||
source = Path(source_path or default_source)
|
|
||||||
patch = Path(patch_path or default_patch)
|
patch = Path(patch_path or default_patch)
|
||||||
output = Path(output_path or default_output)
|
check_paths(dependency, patch)
|
||||||
|
apply_patch(dependency, patch)
|
||||||
check_paths(source, patch, output)
|
|
||||||
validate_pristine_source(source, patch)
|
|
||||||
_copy_and_patch(source, patch, output)
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
parser.add_argument("--bluepad32", type=Path)
|
parser.add_argument("--bluepad32", type=Path)
|
||||||
parser.add_argument("--patch", type=Path)
|
parser.add_argument("--patch", type=Path)
|
||||||
parser.add_argument("--output", type=Path)
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
try:
|
try:
|
||||||
prepare_bluepad32(args.bluepad32, args.patch, args.output)
|
prepare_bluepad32(args.bluepad32, args.patch)
|
||||||
except PatchError as exc:
|
except PatchError as exc:
|
||||||
print(f"error: {exc}", file=sys.stderr)
|
print(f"error: {exc}", file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
#include "usb/usb_configuration_management.h"
|
#include "usb_configuration_management.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "configuration/adapter_configuration.h"
|
#include "adapter_configuration.h"
|
||||||
#include "adapter/adapter_host_probe.h"
|
#include "adapter_host_probe.h"
|
||||||
#include "adapter/adapter_reboot.h"
|
#include "adapter_reboot.h"
|
||||||
#include "adapter/adapter_usb_mode.h"
|
#include "adapter_usb_mode.h"
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
#include "usb/usb_output_driver.h"
|
#include "usb_output_driver.h"
|
||||||
|
|
||||||
namespace UsbConfigurationManagement {
|
namespace UsbConfigurationManagement {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
|
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
|
|
||||||
#include "input/bluepad32_input_backend.h"
|
#include "bluepad32_input_backend.h"
|
||||||
#include "configuration/configuration_service.h"
|
#include "configuration_service.h"
|
||||||
#include "profile/profile_service.h"
|
#include "profile_service.h"
|
||||||
|
|
||||||
namespace UsbConfigurationManagement {
|
namespace UsbConfigurationManagement {
|
||||||
|
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
#include "usb/usb_output_driver.h"
|
#include "usb_output_driver.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "device/usbd_pvt.h"
|
#include "device/usbd_pvt.h"
|
||||||
#include "usb/switch/switch_pro_driver.h"
|
#include "switch_pro_driver.h"
|
||||||
#include "usb/switch/switch_pro_descriptors.h"
|
#include "switch_pro_descriptors.h"
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
|
|
||||||
#ifdef SWITCH_PICO_BLUEPAD32
|
#ifdef SWITCH_PICO_BLUEPAD32
|
||||||
#include "usb/usb_configuration_management.h"
|
#include "usb_configuration_management.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SWITCH_PICO_USB_OUTPUT_MODES
|
#ifdef SWITCH_PICO_USB_OUTPUT_MODES
|
||||||
#include "adapter/adapter_host_probe.h"
|
#include "adapter_host_probe.h"
|
||||||
#include "usb/generic_hid/generic_hid_descriptors.h"
|
#include "generic_hid_descriptors.h"
|
||||||
#include "usb/generic_hid/generic_hid_driver.h"
|
#include "generic_hid_driver.h"
|
||||||
#include "usb/xinput/xinput_descriptors.h"
|
#include "xinput_descriptors.h"
|
||||||
#include "usb/xinput/xinput_driver.h"
|
#include "xinput_driver.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SWITCH_PICO_LOG
|
#ifdef SWITCH_PICO_LOG
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "adapter/adapter_usb_mode.h"
|
#include "adapter_usb_mode.h"
|
||||||
#include "core/controller_state.h"
|
#include "controller_state.h"
|
||||||
#include "usb/switch/switch_haptics.h"
|
#include "switch_haptics.h"
|
||||||
|
|
||||||
// Mode capability bits published in USB management info. Input is present in
|
// Mode capability bits published in USB management info. Input is present in
|
||||||
// every current mode; rumble/motion describe host-visible USB capabilities.
|
// every current mode; rumble/motion describe host-visible USB capabilities.
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue