From a7d7d0f25336c25303f65643a47b6aa90ce75655 Mon Sep 17 00:00:00 2001 From: portersky Date: Mon, 17 Aug 2026 14:18:03 +0200 Subject: [PATCH] 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 --- .gitignore | 40 +++++++++++++ CMakeLists.txt | 107 ++++++++++++++++++++++++++++++++++ README.md | 22 +++++++ deps/FindUnity.cmake | 67 +++++++++++++++++++++ deps/Flags.cmake | 82 ++++++++++++++++++++++++++ deps/Platform.cmake | 54 +++++++++++++++++ deps/Sanitizers.cmake | 28 +++++++++ firmware/.gitkeep | 0 include/app/xone_api.h | 24 ++++++++ include/auth/crypto.hpp | 16 +++++ include/common/types.hpp | 49 ++++++++++++++++ include/gip/protocol.hpp | 15 +++++ include/hid/hid_device.hpp | 16 +++++ include/mt76/mt76.hpp | 20 +++++++ include/usb/usb_transport.hpp | 25 ++++++++ src/app/api.cpp | 19 ++++++ src/app/main.swift | 13 +++++ src/auth/crypto.cpp | 7 +++ src/gip/protocol.cpp | 7 +++ src/hid/hid_device.cpp | 7 +++ src/mt76/mt76.cpp | 16 +++++ src/usb/usb_transport.cpp | 14 +++++ tests/CMakeLists.txt | 26 +++++++++ tests/test_types.cpp | 46 +++++++++++++++ tests/test_version.cpp | 22 +++++++ 25 files changed, 742 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 deps/FindUnity.cmake create mode 100644 deps/Flags.cmake create mode 100644 deps/Platform.cmake create mode 100644 deps/Sanitizers.cmake create mode 100644 firmware/.gitkeep create mode 100644 include/app/xone_api.h create mode 100644 include/auth/crypto.hpp create mode 100644 include/common/types.hpp create mode 100644 include/gip/protocol.hpp create mode 100644 include/hid/hid_device.hpp create mode 100644 include/mt76/mt76.hpp create mode 100644 include/usb/usb_transport.hpp create mode 100644 src/app/api.cpp create mode 100644 src/app/main.swift create mode 100644 src/auth/crypto.cpp create mode 100644 src/gip/protocol.cpp create mode 100644 src/hid/hid_device.cpp create mode 100644 src/mt76/mt76.cpp create mode 100644 src/usb/usb_transport.cpp create mode 100644 tests/CMakeLists.txt create mode 100644 tests/test_types.cpp create mode 100644 tests/test_version.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..98a7636 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1413372 --- /dev/null +++ b/CMakeLists.txt @@ -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() diff --git a/README.md b/README.md index 06a79ed..ecf5b2a 100644 --- a/README.md +++ b/README.md @@ -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 These items need research on macOS before implementation can begin. diff --git a/deps/FindUnity.cmake b/deps/FindUnity.cmake new file mode 100644 index 0000000..6d3edb5 --- /dev/null +++ b/deps/FindUnity.cmake @@ -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") diff --git a/deps/Flags.cmake b/deps/Flags.cmake new file mode 100644 index 0000000..53f7f23 --- /dev/null +++ b/deps/Flags.cmake @@ -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( ) +# +# Searches for an Apple framework and, if found, appends -framework 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() diff --git a/deps/Platform.cmake b/deps/Platform.cmake new file mode 100644 index 0000000..320c859 --- /dev/null +++ b/deps/Platform.cmake @@ -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() diff --git a/deps/Sanitizers.cmake b/deps/Sanitizers.cmake new file mode 100644 index 0000000..d793dcd --- /dev/null +++ b/deps/Sanitizers.cmake @@ -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() diff --git a/firmware/.gitkeep b/firmware/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/include/app/xone_api.h b/include/app/xone_api.h new file mode 100644 index 0000000..4c6417d --- /dev/null +++ b/include/app/xone_api.h @@ -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 + +#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 diff --git a/include/auth/crypto.hpp b/include/auth/crypto.hpp new file mode 100644 index 0000000..f1dc904 --- /dev/null +++ b/include/auth/crypto.hpp @@ -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 diff --git a/include/common/types.hpp b/include/common/types.hpp new file mode 100644 index 0000000..c2f37f2 --- /dev/null +++ b/include/common/types.hpp @@ -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 + +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(p); + return static_cast(b[0] | (b[1] << 8)); +} + +inline auto store_le16(void *p, std::uint16_t v) -> void +{ + auto *b = static_cast(p); + b[0] = static_cast(v & 0xFF); + b[1] = static_cast((v >> 8) & 0xFF); +} + +inline auto load_le32(void const *p) -> std::uint32_t +{ + auto const b = static_cast(p); + return static_cast( + b[0] | (b[1] << 8) | (b[2] << 16) | (static_cast(b[3]) << 24)); +} + +inline auto store_le32(void *p, std::uint32_t v) -> void +{ + auto *b = static_cast(p); + b[0] = static_cast(v & 0xFF); + b[1] = static_cast((v >> 8) & 0xFF); + b[2] = static_cast((v >> 16) & 0xFF); + b[3] = static_cast((v >> 24) & 0xFF); +} + +} // namespace xone diff --git a/include/gip/protocol.hpp b/include/gip/protocol.hpp new file mode 100644 index 0000000..ce537bc --- /dev/null +++ b/include/gip/protocol.hpp @@ -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 diff --git a/include/hid/hid_device.hpp b/include/hid/hid_device.hpp new file mode 100644 index 0000000..3d43d5a --- /dev/null +++ b/include/hid/hid_device.hpp @@ -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 diff --git a/include/mt76/mt76.hpp b/include/mt76/mt76.hpp new file mode 100644 index 0000000..4fc3be7 --- /dev/null +++ b/include/mt76/mt76.hpp @@ -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 + +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 diff --git a/include/usb/usb_transport.hpp b/include/usb/usb_transport.hpp new file mode 100644 index 0000000..1c38bb0 --- /dev/null +++ b/include/usb/usb_transport.hpp @@ -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 + +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 diff --git a/src/app/api.cpp b/src/app/api.cpp new file mode 100644 index 0000000..95acad1 --- /dev/null +++ b/src/app/api.cpp @@ -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(); +} diff --git a/src/app/main.swift b/src/app/main.swift new file mode 100644 index 0000000..e940691 --- /dev/null +++ b/src/app/main.swift @@ -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)") +} diff --git a/src/auth/crypto.cpp b/src/auth/crypto.cpp new file mode 100644 index 0000000..f37d669 --- /dev/null +++ b/src/auth/crypto.cpp @@ -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 diff --git a/src/gip/protocol.cpp b/src/gip/protocol.cpp new file mode 100644 index 0000000..1ea4e15 --- /dev/null +++ b/src/gip/protocol.cpp @@ -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 diff --git a/src/hid/hid_device.cpp b/src/hid/hid_device.cpp new file mode 100644 index 0000000..880e8f1 --- /dev/null +++ b/src/hid/hid_device.cpp @@ -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 diff --git a/src/mt76/mt76.cpp b/src/mt76/mt76.cpp new file mode 100644 index 0000000..aee1e8f --- /dev/null +++ b/src/mt76/mt76.cpp @@ -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 diff --git a/src/usb/usb_transport.cpp b/src/usb/usb_transport.cpp new file mode 100644 index 0000000..0a34104 --- /dev/null +++ b/src/usb/usb_transport.cpp @@ -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 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..aacc4d3 --- /dev/null +++ b/tests/CMakeLists.txt @@ -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} +) diff --git a/tests/test_types.cpp b/tests/test_types.cpp new file mode 100644 index 0000000..f2b960f --- /dev/null +++ b/tests/test_types.cpp @@ -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(); +} diff --git a/tests/test_version.cpp b/tests/test_version.cpp new file mode 100644 index 0000000..384fa90 --- /dev/null +++ b/tests/test_version.cpp @@ -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(); +}