chore: scaffold CMake build system
Add the CMake build for the Swift + C++ macOS port, mirroring the refix layout: deps/ modules (Platform, Flags, Sanitizers, FindUnity), per-module static libraries (usb, auth, mt76, gip, hid, api), a Swift app entry point with a pure C bridge header, and a Unity test suite behind BUILD_TESTING. Swift requires the Ninja or Xcode generator; a guard in CMakeLists.txt rejects anything else. Co-Authored-By: qwen (qwen/qwen3.8-27b@q2_k_xl): scaffolded CMake build + tests
This commit is contained in:
+40
@@ -0,0 +1,40 @@
|
|||||||
|
# CMake
|
||||||
|
build
|
||||||
|
build-*
|
||||||
|
cmake-build-*
|
||||||
|
__cmake*
|
||||||
|
|
||||||
|
# Firmware binaries (downloaded via scripts/download-firmware.sh)
|
||||||
|
firmware/*.bin
|
||||||
|
|
||||||
|
# Xcode
|
||||||
|
*.xcworkspace
|
||||||
|
*.xcodeproj
|
||||||
|
|
||||||
|
# Visual Studio
|
||||||
|
*.sln
|
||||||
|
*.vcxproj
|
||||||
|
*.vcxproj.filters
|
||||||
|
*.vcxproj.user
|
||||||
|
.vs
|
||||||
|
|
||||||
|
# Makefile
|
||||||
|
Makefile
|
||||||
|
*.make
|
||||||
|
|
||||||
|
# nvim
|
||||||
|
.ccls
|
||||||
|
.ccls-cache
|
||||||
|
.cache
|
||||||
|
compile_commands.json
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
# CMake supports Swift only with the Ninja and Xcode generators.
|
||||||
|
if(NOT CMAKE_GENERATOR MATCHES "^(Ninja|Xcode)$")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"This project requires the Ninja or Xcode generator for Swift support.\n"
|
||||||
|
" Configure with: cmake -S . -B build -G Ninja")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
project(xone_macos VERSION 0.1.0 LANGUAGES CXX Swift)
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
# Add the deps directory to the module path
|
||||||
|
# Required for find_package injection
|
||||||
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/deps")
|
||||||
|
|
||||||
|
# Platform and compiler detection
|
||||||
|
include(Platform)
|
||||||
|
include(Flags)
|
||||||
|
include(Sanitizers)
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# Module libraries (bottom-up layering, mirrors medusalix/xone structure)
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# USB transport (IOKit / IOUSBFamily)
|
||||||
|
add_library(xone_usb STATIC
|
||||||
|
"src/usb/usb_transport.cpp"
|
||||||
|
)
|
||||||
|
target_include_directories(xone_usb PUBLIC "${CMAKE_SOURCE_DIR}/include")
|
||||||
|
target_compile_features(xone_usb PRIVATE cxx_std_23)
|
||||||
|
target_compile_options(xone_usb PRIVATE ${BASE_OPTIONS})
|
||||||
|
target_compile_definitions(xone_usb PRIVATE ${BASE_DEFINITIONS})
|
||||||
|
|
||||||
|
# Auth + crypto (AES-CCMP key setup, ECDH, RSA — port from auth/)
|
||||||
|
add_library(xone_auth STATIC
|
||||||
|
"src/auth/crypto.cpp"
|
||||||
|
)
|
||||||
|
target_include_directories(xone_auth PUBLIC "${CMAKE_SOURCE_DIR}/include")
|
||||||
|
target_compile_features(xone_auth PRIVATE cxx_std_23)
|
||||||
|
target_compile_options(xone_auth PRIVATE ${BASE_OPTIONS})
|
||||||
|
target_compile_definitions(xone_auth PRIVATE ${BASE_DEFINITIONS})
|
||||||
|
|
||||||
|
# MT76 chip protocol (firmware load, EFUSE, radio init, beacons)
|
||||||
|
add_library(xone_mt76 STATIC
|
||||||
|
"src/mt76/mt76.cpp"
|
||||||
|
)
|
||||||
|
target_include_directories(xone_mt76 PUBLIC "${CMAKE_SOURCE_DIR}/include")
|
||||||
|
target_compile_features(xone_mt76 PRIVATE cxx_std_23)
|
||||||
|
target_compile_options(xone_mt76 PRIVATE ${BASE_OPTIONS})
|
||||||
|
target_compile_definitions(xone_mt76 PRIVATE ${BASE_DEFINITIONS})
|
||||||
|
target_link_libraries(xone_mt76 PUBLIC xone_usb)
|
||||||
|
|
||||||
|
# GIP protocol (client lifecycle, handshake, status reports)
|
||||||
|
add_library(xone_gip STATIC
|
||||||
|
"src/gip/protocol.cpp"
|
||||||
|
)
|
||||||
|
target_include_directories(xone_gip PUBLIC "${CMAKE_SOURCE_DIR}/include")
|
||||||
|
target_compile_features(xone_gip PRIVATE cxx_std_23)
|
||||||
|
target_compile_options(xone_gip PRIVATE ${BASE_OPTIONS})
|
||||||
|
target_compile_definitions(xone_gip PRIVATE ${BASE_DEFINITIONS})
|
||||||
|
target_link_libraries(xone_gip PUBLIC xone_auth xone_mt76)
|
||||||
|
|
||||||
|
# Virtual HID gamepad presentation
|
||||||
|
add_library(xone_hid STATIC
|
||||||
|
"src/hid/hid_device.cpp"
|
||||||
|
)
|
||||||
|
target_include_directories(xone_hid PUBLIC "${CMAKE_SOURCE_DIR}/include")
|
||||||
|
target_compile_features(xone_hid PRIVATE cxx_std_23)
|
||||||
|
target_compile_options(xone_hid PRIVATE ${BASE_OPTIONS})
|
||||||
|
target_compile_definitions(xone_hid PRIVATE ${BASE_DEFINITIONS})
|
||||||
|
target_link_libraries(xone_hid PUBLIC xone_gip)
|
||||||
|
|
||||||
|
# C ABI bridge consumed by the Swift app and the test suite
|
||||||
|
add_library(xone_api STATIC
|
||||||
|
"src/app/api.cpp"
|
||||||
|
)
|
||||||
|
target_include_directories(xone_api PUBLIC "${CMAKE_SOURCE_DIR}/include")
|
||||||
|
target_compile_features(xone_api PRIVATE cxx_std_23)
|
||||||
|
target_compile_options(xone_api PRIVATE ${BASE_OPTIONS})
|
||||||
|
target_compile_definitions(xone_api PRIVATE XONE_VERSION="${PROJECT_VERSION}")
|
||||||
|
target_link_libraries(xone_api PUBLIC xone_hid)
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# macOS app (Swift entry point)
|
||||||
|
# ==============================================================================
|
||||||
|
add_executable(xone_app
|
||||||
|
"src/app/main.swift"
|
||||||
|
)
|
||||||
|
target_include_directories(xone_app PRIVATE "${CMAKE_SOURCE_DIR}/include")
|
||||||
|
target_compile_definitions(xone_app PRIVATE ${BASE_DEFINITIONS})
|
||||||
|
target_link_libraries(xone_app PRIVATE xone_api ${BASE_LIBRARIES})
|
||||||
|
|
||||||
|
# Swift sees the C ABI via a bridging header (no module map required)
|
||||||
|
target_compile_options(xone_app PRIVATE
|
||||||
|
-import-objc-header "${CMAKE_SOURCE_DIR}/include/app/xone_api.h"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# Tests
|
||||||
|
# ==============================================================================
|
||||||
|
option(BUILD_TESTING OFF "Build Unity test suites")
|
||||||
|
if (BUILD_TESTING)
|
||||||
|
enable_testing()
|
||||||
|
find_package(Unity REQUIRED)
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
@@ -7,6 +7,28 @@ See [PLAN.md](PLAN.md) for full architecture and implementation phases.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Requires CMake ≥ 3.21, Xcode (or Command Line Tools), and Ninja.
|
||||||
|
Swift is only supported by the **Ninja** and **Xcode** generators in CMake,
|
||||||
|
so configure with one of them explicitly:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cmake -S . -B build -G Ninja -DBUILD_TESTING=ON
|
||||||
|
cmake --build build
|
||||||
|
./build/xone_app
|
||||||
|
cmake --build build --target check # run the test suite
|
||||||
|
```
|
||||||
|
|
||||||
|
Layout (mirrors `../refix/`):
|
||||||
|
|
||||||
|
- `deps/` — CMake modules: `Platform.cmake`, `Flags.cmake`, `Sanitizers.cmake`, `FindUnity.cmake`
|
||||||
|
- `src/{usb,mt76,gip,auth,hid}` — C++ protocol stack (ported from `medusalix/xone`)
|
||||||
|
- `src/app/` — Swift app entry point + C ABI bridge (`include/app/xone_api.h`)
|
||||||
|
- `tests/` — Unity test suite (`BUILD_TESTING=ON`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Investigation Checklist
|
## Investigation Checklist
|
||||||
|
|
||||||
These items need research on macOS before implementation can begin.
|
These items need research on macOS before implementation can begin.
|
||||||
|
|||||||
Vendored
+67
@@ -0,0 +1,67 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# Find Unity
|
||||||
|
# ==============================================================================
|
||||||
|
# This module fetches the Unity unit testing framework.
|
||||||
|
#
|
||||||
|
# Targets provided:
|
||||||
|
# Unity::Unity - The Unity library target
|
||||||
|
#
|
||||||
|
# Variables set:
|
||||||
|
# Unity_FOUND - TRUE if Unity is available
|
||||||
|
# Unity_LIBRARIES - The Unity library target (Unity::Unity)
|
||||||
|
# Unity_INCLUDE_DIR - Include directories for Unity
|
||||||
|
# Unity_VERSION - Version of Unity (if available)
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
if (DEFINED _FINDUNITY_INCLUDED)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
set(_FINDUNITY_INCLUDED TRUE)
|
||||||
|
|
||||||
|
if (DEFINED Unity_FIND_VERSION AND NOT Unity_FIND_VERSION STREQUAL "")
|
||||||
|
set(UNITY_VERSION "${Unity_FIND_VERSION}")
|
||||||
|
else()
|
||||||
|
set(UNITY_VERSION "2.6.1")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "Fetching Unity ${UNITY_VERSION}")
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
unity
|
||||||
|
URL https://github.com/ThrowTheSwitch/Unity/archive/refs/tags/v${UNITY_VERSION}.zip
|
||||||
|
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(unity)
|
||||||
|
|
||||||
|
# Unity sets INTERFACE_SYSTEM_INCLUDE_DIRECTORIES to a path inside the build
|
||||||
|
# tree, which CMake rejects on newer versions. The path stays in
|
||||||
|
# INTERFACE_INCLUDE_DIRECTORIES so headers are still found.
|
||||||
|
if (TARGET unity)
|
||||||
|
set_target_properties(unity PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT TARGET Unity::Unity)
|
||||||
|
if (TARGET unity)
|
||||||
|
add_library(Unity::Unity ALIAS unity)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Could not fetch Unity; no target unity or Unity::Unity available")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(Unity_FOUND TRUE)
|
||||||
|
set(Unity_LIBRARIES Unity::Unity)
|
||||||
|
set(Unity_VERSION "${UNITY_VERSION}")
|
||||||
|
set(Unity_INCLUDE_DIR "${unity_SOURCE_DIR}/src")
|
||||||
|
|
||||||
|
|
||||||
|
if (TARGET unity)
|
||||||
|
target_compile_definitions(unity PUBLIC
|
||||||
|
UNITY_OUTPUT_COLOR
|
||||||
|
UNITY_INCLUDE_PRINT_FORMATTED
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(UNITY_LICENSE_FILE "${unity_SOURCE_DIR}/LICENSE.txt" CACHE FILEPATH "Path to Unity license file")
|
||||||
Vendored
+82
@@ -0,0 +1,82 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# Compiler and Linker Flags
|
||||||
|
# ==============================================================================
|
||||||
|
# This module sets platform-specific and compiler-specific flags, libraries,
|
||||||
|
# and definitions used throughout the build system.
|
||||||
|
#
|
||||||
|
# Variables set:
|
||||||
|
# BASE_LIBRARIES - Libraries to link based on target platform
|
||||||
|
# BASE_DEFINITIONS - Preprocessor definitions based on target platform
|
||||||
|
# BASE_OPTIONS - Compiler warning flags (C/C++ only, not applied to Swift)
|
||||||
|
#
|
||||||
|
# Requires: Platform.cmake must be included first (for IS_* variables)
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Framework Detection Helper
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# find_and_link_framework(<OUT_VAR> <FRAMEWORK_NAME> <DISPLAY_NAME>)
|
||||||
|
#
|
||||||
|
# Searches for an Apple framework and, if found, appends -framework <name> to
|
||||||
|
# the output list.
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# - OUT_VAR Name of the list variable to append the flag to
|
||||||
|
# - FRAMEWORK_NAME The framework name to search for (e.g. "IOKit")
|
||||||
|
# - DISPLAY_NAME Human-readable name for status messages
|
||||||
|
function(find_and_link_framework OUT_VAR FRAMEWORK_NAME DISPLAY_NAME)
|
||||||
|
find_library(_FOUND_LIB ${FRAMEWORK_NAME})
|
||||||
|
if (_FOUND_LIB)
|
||||||
|
message(STATUS " [x] ${DISPLAY_NAME} (-framework ${FRAMEWORK_NAME})")
|
||||||
|
list(APPEND ${OUT_VAR} "-framework ${FRAMEWORK_NAME}")
|
||||||
|
set(${OUT_VAR} "${${OUT_VAR}}" PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
message(STATUS " [ ] ${DISPLAY_NAME} (-framework ${FRAMEWORK_NAME})")
|
||||||
|
endif()
|
||||||
|
unset(_FOUND_LIB CACHE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Platform-Specific Link Libraries
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# These frameworks are required for USB access, crypto and (later) audio.
|
||||||
|
# They are linked into the final executable; static libraries reference the
|
||||||
|
# symbols and resolution happens at final link time.
|
||||||
|
|
||||||
|
set(BASE_LIBRARIES "")
|
||||||
|
if (IS_MACOS)
|
||||||
|
message(STATUS "macOS link libraries:")
|
||||||
|
find_and_link_framework(BASE_LIBRARIES "IOKit" "IOKit (USB transport)")
|
||||||
|
find_and_link_framework(BASE_LIBRARIES "CoreFoundation" "Core Foundation")
|
||||||
|
find_and_link_framework(BASE_LIBRARIES "Security" "Security (crypto/ECDH)")
|
||||||
|
elseif (IS_IOS)
|
||||||
|
message(FATAL_ERROR "iOS is not supported yet!")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Platform-Specific Definitions
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
set(BASE_DEFINITIONS "")
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Compiler Warning Flags
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# These are stored in BASE_OPTIONS and applied per-target via
|
||||||
|
# target_compile_options() to avoid polluting external dependencies.
|
||||||
|
# NOTE: Applied to C/C++ targets only; swiftc does not share these flags.
|
||||||
|
set(BASE_OPTIONS "")
|
||||||
|
if (IS_CLANG_OR_GCC)
|
||||||
|
set(BASE_OPTIONS
|
||||||
|
"-Wall" # Enable all common warnings
|
||||||
|
"-Wextra" # Enable extra warnings
|
||||||
|
"-Werror" # Treat warnings as errors
|
||||||
|
)
|
||||||
|
elseif (IS_MSVC)
|
||||||
|
set(BASE_OPTIONS
|
||||||
|
"/W4" # Warning level 4 (high)
|
||||||
|
"/WX" # Treat warnings as errors
|
||||||
|
"/utf-8" # Set source and execution character set to UTF-8
|
||||||
|
"/Zc:__cplusplus" # Report correct C++ standard version in __cplusplus
|
||||||
|
)
|
||||||
|
endif()
|
||||||
Vendored
+54
@@ -0,0 +1,54 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# Platform Detection
|
||||||
|
# ==============================================================================
|
||||||
|
# This module detects the current platform and compiler, setting IS_* variables
|
||||||
|
# that can be used throughout the build system for conditional logic.
|
||||||
|
#
|
||||||
|
# Compiler flags set:
|
||||||
|
# IS_CLANG_OR_GCC - TRUE if using Clang or GCC compiler
|
||||||
|
# IS_MSVC - TRUE if using Microsoft Visual C++ compiler
|
||||||
|
#
|
||||||
|
# Platform flags set:
|
||||||
|
# IS_WINDOWS - TRUE if building for Windows
|
||||||
|
# IS_LINUX - TRUE if building for Linux
|
||||||
|
# IS_MACOS - TRUE if building for macOS
|
||||||
|
# IS_IOS - TRUE if building for iOS
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Compiler Detection
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
set(IS_CLANG_OR_GCC FALSE)
|
||||||
|
set(IS_MSVC FALSE)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
set(IS_MSVC TRUE)
|
||||||
|
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
||||||
|
set(IS_CLANG_OR_GCC TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Platform Detection
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
set(IS_WINDOWS FALSE)
|
||||||
|
set(IS_LINUX FALSE)
|
||||||
|
set(IS_MACOS FALSE)
|
||||||
|
set(IS_IOS FALSE)
|
||||||
|
|
||||||
|
if(ANDROID)
|
||||||
|
message(FATAL_ERROR "Android is not supported by xone_macos!")
|
||||||
|
elseif(APPLE)
|
||||||
|
if(IOS)
|
||||||
|
message(STATUS "Platform: iOS")
|
||||||
|
set(IS_IOS TRUE)
|
||||||
|
else()
|
||||||
|
message(STATUS "Platform: macOS")
|
||||||
|
set(IS_MACOS TRUE)
|
||||||
|
endif()
|
||||||
|
elseif(WIN32)
|
||||||
|
message(FATAL_ERROR "xone_macos only builds on macOS!")
|
||||||
|
elseif(UNIX)
|
||||||
|
message(FATAL_ERROR "xone_macos only builds on macOS!")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unknown platform!")
|
||||||
|
endif()
|
||||||
Vendored
+28
@@ -0,0 +1,28 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# Sanitizers
|
||||||
|
# ==============================================================================
|
||||||
|
# AddressSanitizer (ASan) support.
|
||||||
|
# Works with GCC/Clang on Linux and macOS, Clang on Windows (requires
|
||||||
|
# compiler-rt with sanitizers), and MSVC on Windows (/fsanitize=address).
|
||||||
|
#
|
||||||
|
# Usage: cmake -DENABLE_ASAN=ON ...
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
option(ENABLE_ASAN "Build with AddressSanitizer" OFF)
|
||||||
|
|
||||||
|
if (ENABLE_ASAN)
|
||||||
|
if (ENABLE_COVERAGE)
|
||||||
|
message(FATAL_ERROR "ENABLE_ASAN and ENABLE_COVERAGE cannot be used together")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
add_compile_options(/fsanitize=address)
|
||||||
|
message(STATUS "ASan: enabled (MSVC)")
|
||||||
|
elseif (CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
|
||||||
|
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
|
||||||
|
add_link_options(-fsanitize=address)
|
||||||
|
message(STATUS "ASan: enabled (${CMAKE_C_COMPILER_ID})")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "ENABLE_ASAN: unsupported compiler ${CMAKE_C_COMPILER_ID}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// ==============================================================================
|
||||||
|
// C ABI bridge between the C++ protocol stack and the Swift app layer
|
||||||
|
// ==============================================================================
|
||||||
|
// This header is imported into Swift via -import-objc-header, so it must stay
|
||||||
|
// pure C (no C++ types). The C++ modules implement these entry points.
|
||||||
|
// ==============================================================================
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Human-readable version string of the protocol stack.
|
||||||
|
const char *xone_version(void);
|
||||||
|
|
||||||
|
// TRUE if an Xbox Wireless Dongle is connected, FALSE otherwise.
|
||||||
|
bool xone_dongle_present(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// ==============================================================================
|
||||||
|
// Auth + crypto
|
||||||
|
// ==============================================================================
|
||||||
|
// Port target: medusalix/xone auth/auth.c + auth/crypto.c.
|
||||||
|
// ECDH (P-256) key agreement, RSA encryption, SHA transcript/PRF for the GIP
|
||||||
|
// authentication handshake. AES-CCMP frame encryption itself runs on-chip;
|
||||||
|
// this layer only derives and installs keys into the MT76 WCID registers.
|
||||||
|
// ==============================================================================
|
||||||
|
|
||||||
|
namespace xone::auth {
|
||||||
|
|
||||||
|
// TODO(phase 1): ECDH/RSA/SHA primitives (CommonCrypto / Security.framework).
|
||||||
|
|
||||||
|
} // namespace xone::auth
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// ==============================================================================
|
||||||
|
// Platform types and endianness helpers (user-space)
|
||||||
|
// ==============================================================================
|
||||||
|
// Replaces the kernel's __le16/__le32/guid_t machinery for user-space use.
|
||||||
|
// All wire formats of the MT76 chip and the GIP protocol are little-endian;
|
||||||
|
// these byte-wise helpers stay correct on any host endianness.
|
||||||
|
// ==============================================================================
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace xone {
|
||||||
|
|
||||||
|
// 128-bit GUID used by the GIP protocol (port of the kernel guid_t).
|
||||||
|
struct guid_t {
|
||||||
|
std::uint8_t data[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
inline auto load_le16(void const *p) -> std::uint16_t
|
||||||
|
{
|
||||||
|
auto const b = static_cast<std::uint8_t const *>(p);
|
||||||
|
return static_cast<std::uint16_t>(b[0] | (b[1] << 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto store_le16(void *p, std::uint16_t v) -> void
|
||||||
|
{
|
||||||
|
auto *b = static_cast<std::uint8_t *>(p);
|
||||||
|
b[0] = static_cast<std::uint8_t>(v & 0xFF);
|
||||||
|
b[1] = static_cast<std::uint8_t>((v >> 8) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto load_le32(void const *p) -> std::uint32_t
|
||||||
|
{
|
||||||
|
auto const b = static_cast<std::uint8_t const *>(p);
|
||||||
|
return static_cast<std::uint32_t>(
|
||||||
|
b[0] | (b[1] << 8) | (b[2] << 16) | (static_cast<std::uint32_t>(b[3]) << 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto store_le32(void *p, std::uint32_t v) -> void
|
||||||
|
{
|
||||||
|
auto *b = static_cast<std::uint8_t *>(p);
|
||||||
|
b[0] = static_cast<std::uint8_t>(v & 0xFF);
|
||||||
|
b[1] = static_cast<std::uint8_t>((v >> 8) & 0xFF);
|
||||||
|
b[2] = static_cast<std::uint8_t>((v >> 16) & 0xFF);
|
||||||
|
b[3] = static_cast<std::uint8_t>((v >> 24) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xone
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// ==============================================================================
|
||||||
|
// GIP protocol (Game Input Protocol)
|
||||||
|
// ==============================================================================
|
||||||
|
// Port target: medusalix/xone bus/protocol.c + bus/bus.c.
|
||||||
|
// Client lifecycle, ANNOUNCE/IDENTIFY handshake, auth handshake, status
|
||||||
|
// reports (battery, connected), HID report pass-through.
|
||||||
|
// ==============================================================================
|
||||||
|
|
||||||
|
namespace xone::gip {
|
||||||
|
|
||||||
|
// TODO(phase 1): GIP frame types, client lifecycle, handshake state machine.
|
||||||
|
|
||||||
|
} // namespace xone::gip
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// ==============================================================================
|
||||||
|
// Virtual HID gamepad presentation
|
||||||
|
// ==============================================================================
|
||||||
|
// Exposes connected controllers to macOS so games recognize them natively.
|
||||||
|
// Preferred path: DriverKit extension implementing IOHIDDriver with an
|
||||||
|
// Xbox 360/One-compatible report descriptor (input reports from GIP, output
|
||||||
|
// reports for rumble relayed back via GIP).
|
||||||
|
// ==============================================================================
|
||||||
|
|
||||||
|
namespace xone::hid {
|
||||||
|
|
||||||
|
// TODO(phase 4): report descriptor + virtual device presentation.
|
||||||
|
|
||||||
|
} // namespace xone::hid
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// ==============================================================================
|
||||||
|
// MT76 chip protocol (MediaTek MT76xx radio in the dongle)
|
||||||
|
// ==============================================================================
|
||||||
|
// Port target: medusalix/xone transport/mt76.c + mt76.h + mt76_defs.h.
|
||||||
|
// Firmware load (0x3800-byte chunks), EFUSE read, radio init (~80 register
|
||||||
|
// writes), channel evaluation, beacon transmission, pairing mode, client
|
||||||
|
// association and WCID/AES-CCMP key setup (encryption runs on-chip).
|
||||||
|
// ==============================================================================
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace xone::mt76 {
|
||||||
|
|
||||||
|
// Load the firmware image for the given dongle PID into the chip.
|
||||||
|
// TODO(phase 3): port from xone_mt76_load_firmware().
|
||||||
|
auto load_firmware(std::uint16_t pid, char const *firmware_path) -> bool;
|
||||||
|
|
||||||
|
} // namespace xone::mt76
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// ==============================================================================
|
||||||
|
// USB transport (IOKit / IOUSBFamily)
|
||||||
|
// ==============================================================================
|
||||||
|
// Owns the physical Xbox Wireless Dongle:
|
||||||
|
// VID 0x045E, PIDs 0x02E6 (old), 0x02FE (new), 0x02F9 (ASUS/Lenovo built-in),
|
||||||
|
// 0x091E (Surface Book 2).
|
||||||
|
//
|
||||||
|
// Port target: medusalix/xone transport/dongle.c + transport/mt76.c USB calls.
|
||||||
|
// Linux usb_control_msg()/usb_bulk_msg() map to IOUSBDeviceInterface
|
||||||
|
// DeviceRequest / IOUSBInterfaceInterface WritePipe+ReadPipe.
|
||||||
|
// ==============================================================================
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace xone::usb {
|
||||||
|
|
||||||
|
// Probe for a connected dongle and claim its WLAN interface
|
||||||
|
// (bInterfaceNumber 1).
|
||||||
|
// TODO(phase 2): IOKit implementation (IOServiceMatching, interface open,
|
||||||
|
// EP 0x04 bulk in/out + EP 0x05 interrupt in).
|
||||||
|
auto probe() -> bool;
|
||||||
|
|
||||||
|
} // namespace xone::usb
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// C ABI bridge between the C++ protocol stack and the Swift app layer.
|
||||||
|
|
||||||
|
#include "app/xone_api.h"
|
||||||
|
|
||||||
|
#include "usb/usb_transport.hpp"
|
||||||
|
|
||||||
|
#ifndef XONE_VERSION
|
||||||
|
#define XONE_VERSION "0.1.0"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C" const char *xone_version(void)
|
||||||
|
{
|
||||||
|
return XONE_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" bool xone_dongle_present(void)
|
||||||
|
{
|
||||||
|
return xone::usb::probe();
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// xone_macos — macOS app entry point (Swift)
|
||||||
|
// TODO(phase 5): device discovery, pairing UI, status display, menubar icon.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
let version = String(cString: xone_version())
|
||||||
|
print("xone_macos \(version)")
|
||||||
|
|
||||||
|
if xone_dongle_present() {
|
||||||
|
print("Xbox Wireless Dongle detected")
|
||||||
|
} else {
|
||||||
|
print("No Xbox Wireless Dongle detected (plug in the dongle and retry)")
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Auth + crypto
|
||||||
|
// TODO(phase 1): port from medusalix/xone auth/auth.c + auth/crypto.c.
|
||||||
|
|
||||||
|
#include "auth/crypto.hpp"
|
||||||
|
|
||||||
|
namespace xone::auth {
|
||||||
|
} // namespace xone::auth
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// GIP protocol (Game Input Protocol)
|
||||||
|
// TODO(phase 1): port from medusalix/xone bus/protocol.c + bus/bus.c.
|
||||||
|
|
||||||
|
#include "gip/protocol.hpp"
|
||||||
|
|
||||||
|
namespace xone::gip {
|
||||||
|
} // namespace xone::gip
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Virtual HID gamepad presentation
|
||||||
|
// TODO(phase 4): DriverKit IOHIDDriver implementation.
|
||||||
|
|
||||||
|
#include "hid/hid_device.hpp"
|
||||||
|
|
||||||
|
namespace xone::hid {
|
||||||
|
} // namespace xone::hid
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// MT76 chip protocol (MediaTek MT76xx radio in the dongle)
|
||||||
|
// TODO(phase 3): port from medusalix/xone transport/mt76.c.
|
||||||
|
|
||||||
|
#include "mt76/mt76.hpp"
|
||||||
|
|
||||||
|
namespace xone::mt76 {
|
||||||
|
|
||||||
|
auto load_firmware(std::uint16_t pid, char const *firmware_path) -> bool
|
||||||
|
{
|
||||||
|
(void)pid;
|
||||||
|
(void)firmware_path;
|
||||||
|
// Not implemented yet: no USB transport in place.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xone::mt76
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// USB transport (IOKit / IOUSBFamily)
|
||||||
|
// TODO(phase 2): port from medusalix/xone transport/dongle.c + mt76.c USB calls.
|
||||||
|
|
||||||
|
#include "usb/usb_transport.hpp"
|
||||||
|
|
||||||
|
namespace xone::usb {
|
||||||
|
|
||||||
|
auto probe() -> bool
|
||||||
|
{
|
||||||
|
// Not implemented yet: no IOKit device matching in place.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xone::usb
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
set(TEST_TARGETS "")
|
||||||
|
|
||||||
|
add_executable(test_version test_version.cpp)
|
||||||
|
target_include_directories(test_version PRIVATE "${CMAKE_SOURCE_DIR}/include")
|
||||||
|
target_link_libraries(test_version PRIVATE xone_api Unity::Unity)
|
||||||
|
target_compile_features(test_version PRIVATE cxx_std_23)
|
||||||
|
target_compile_definitions(test_version PRIVATE XONE_VERSION="${PROJECT_VERSION}")
|
||||||
|
add_test(NAME test_version COMMAND test_version)
|
||||||
|
list(APPEND TEST_TARGETS test_version)
|
||||||
|
|
||||||
|
add_executable(test_types test_types.cpp)
|
||||||
|
target_include_directories(test_types PRIVATE "${CMAKE_SOURCE_DIR}/include")
|
||||||
|
target_link_libraries(test_types PRIVATE Unity::Unity)
|
||||||
|
target_compile_features(test_types PRIVATE cxx_std_23)
|
||||||
|
add_test(NAME test_types COMMAND test_types)
|
||||||
|
list(APPEND TEST_TARGETS test_types)
|
||||||
|
|
||||||
|
add_custom_target(check
|
||||||
|
COMMAND ${CMAKE_CTEST_COMMAND}
|
||||||
|
--test-dir "${CMAKE_BINARY_DIR}"
|
||||||
|
--output-on-failure
|
||||||
|
--progress
|
||||||
|
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||||
|
USES_TERMINAL
|
||||||
|
DEPENDS ${TEST_TARGETS}
|
||||||
|
)
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
#include "unity.h"
|
||||||
|
|
||||||
|
#include "common/types.hpp"
|
||||||
|
|
||||||
|
void setUp() {}
|
||||||
|
void tearDown() {}
|
||||||
|
|
||||||
|
void test_store_le16_little_endian(void)
|
||||||
|
{
|
||||||
|
std::uint8_t buf[2];
|
||||||
|
xone::store_le16(buf, 0x1234);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0x34, buf[0]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0x12, buf[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_load_le16_roundtrip(void)
|
||||||
|
{
|
||||||
|
std::uint8_t buf[2] = { 0xCD, 0xAB };
|
||||||
|
TEST_ASSERT_EQUAL_UINT16(0xABCD, xone::load_le16(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_store_le32_little_endian(void)
|
||||||
|
{
|
||||||
|
std::uint8_t buf[4];
|
||||||
|
xone::store_le32(buf, 0xDEADBEEF);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0xEF, buf[0]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0xBE, buf[1]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0xAD, buf[2]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0xDE, buf[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_load_le32_roundtrip(void)
|
||||||
|
{
|
||||||
|
std::uint8_t buf[4] = { 0x01, 0x02, 0x03, 0x04 };
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(0x04030201, xone::load_le32(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
UNITY_BEGIN();
|
||||||
|
RUN_TEST(test_store_le16_little_endian);
|
||||||
|
RUN_TEST(test_load_le16_roundtrip);
|
||||||
|
RUN_TEST(test_store_le32_little_endian);
|
||||||
|
RUN_TEST(test_load_le32_roundtrip);
|
||||||
|
return UNITY_END();
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#include "unity.h"
|
||||||
|
|
||||||
|
#include "app/xone_api.h"
|
||||||
|
|
||||||
|
#ifndef XONE_VERSION
|
||||||
|
#define XONE_VERSION "0.1.0"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void setUp() {}
|
||||||
|
void tearDown() {}
|
||||||
|
|
||||||
|
void test_version_matches_project(void)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQUAL_STRING(XONE_VERSION, xone_version());
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
UNITY_BEGIN();
|
||||||
|
RUN_TEST(test_version_matches_project);
|
||||||
|
return UNITY_END();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user