diff --git a/device/.vscode/c_cpp_properties.json b/device/.vscode/c_cpp_properties.json index 84d9d41c2..6ead06990 100644 --- a/device/.vscode/c_cpp_properties.json +++ b/device/.vscode/c_cpp_properties.json @@ -10,7 +10,10 @@ "cppStandard": "gnu++20", "configurationProvider": "nordic-semiconductor.nrf-connect", "browse": { - "limitSymbolsToIncludedHeaders": true + "limitSymbolsToIncludedHeaders": true, + "path": [ + "${workspaceFolder}/src/**" + ] } } ], diff --git a/device/prj.conf.overlays/ble_hid.conf b/device/prj.conf.overlays/ble_hid.conf index 4f5314ffe..b4d182642 100644 --- a/device/prj.conf.overlays/ble_hid.conf +++ b/device/prj.conf.overlays/ble_hid.conf @@ -6,7 +6,7 @@ CONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING=y CONFIG_BT_MAX_PAIRED=20 # use BLE HID over GATT from c2usb -CONFIG_C2USB_BLUETOOTH=y +CONFIG_C2USB_HOGP=y CONFIG_C2USB_HOGP_LOG_LEVEL_DBG=y # battery status to generic client CONFIG_BT_BAS=y diff --git a/device/prj.conf.overlays/c2usb.conf b/device/prj.conf.overlays/c2usb.conf index ef0b41180..3ddd4506b 100644 --- a/device/prj.conf.overlays/c2usb.conf +++ b/device/prj.conf.overlays/c2usb.conf @@ -10,6 +10,7 @@ CONFIG_C2USB_UDC_MAC_LOG_LEVEL_DBG=y CONFIG_WARN_EXPERIMENTAL=n +CONFIG_C2USB_HID_BOOT_PROTOCOL=y CONFIG_C2USB_UDC_MAC=y # Apply config requires deep stack, and since Agent expects response code, diff --git a/device/src/bt_conn.h b/device/src/bt_conn.h index 0610ae6dd..9ed7d0d60 100644 --- a/device/src/bt_conn.h +++ b/device/src/bt_conn.h @@ -28,6 +28,9 @@ #define BLE_ADDR_LEN 6 #define BLE_KEY_LEN 16 + // Holds each host peer's c2usb ble_session (~144 B); size checked in transport_ble.cpp. + #define BLE_HID_SESSION_STORAGE_SIZE 160 + // Typedefs: @@ -46,6 +49,8 @@ struct bt_conn* conn; uint32_t lastSwitchover; uint32_t bleReportIntervalMs; + bool hidSessionActive; + uint8_t hidSessionStorage[BLE_HID_SESSION_STORAGE_SIZE] __attribute__((aligned(8))); } peer_t; typedef enum { diff --git a/device/src/bt_manager.c b/device/src/bt_manager.c index 8046fbb35..6ef358237 100644 --- a/device/src/bt_manager.c +++ b/device/src/bt_manager.c @@ -65,14 +65,6 @@ void BtManager_StartBt() { return; } - if (DEVICE_IS_UHK80_RIGHT) { - if (HOGP_Enable()) { - LOG_INF("HOGP service enabled successfully"); - } else { - LOG_ERR("Failed to enable HOGP service"); - } - } - BtManager_StartScanningAndAdvertising(); } @@ -103,12 +95,6 @@ void BtManager_StopBt() { k_sleep(K_MSEC(10)); - - if (DEVICE_IS_UHK80_RIGHT) { - LOG_WRN("Disabling HOGP service."); - HOGP_Disable(); - } - BtAdvertise_DisableAdvertisingIcon(); LOG_INF("OOB: Bluetooth stopped"); diff --git a/device/src/main.c b/device/src/main.c index 2a63419bb..24395745a 100644 --- a/device/src/main.c +++ b/device/src/main.c @@ -196,6 +196,10 @@ void mainRuntime(void) { } +#if DEVICE_IS_UHK80_RIGHT + HOGP_Register(); // must be before bt_enable() registers static GATT services +#endif + bt_enable(NULL); // has to be before InitSettings // has to be after bt_enable; has to be before ApplyConfig @@ -250,7 +254,6 @@ void mainRuntime(void) { // 6.6mA - HID_SetGamepadActive(false); USB_Enable(); // +2.2mA, 8.8mA; has to be after USB_SetSerialNumber // 8.8mA diff --git a/nrfsdk.code-workspace b/nrfsdk.code-workspace index 0a6f247b9..4baac364c 100644 --- a/nrfsdk.code-workspace +++ b/nrfsdk.code-workspace @@ -9,8 +9,13 @@ "path": "." }, { - "name": "workspace", - "path": ".." + "path": "../zephyr" + }, + { + "path": "../c2usb/c2usb" + }, + { + "path": "../modules/lib/hid-rp/hid-rp" } ], "settings": { @@ -33,6 +38,9 @@ "*.hpp": "cpp", "*.cpp": "cpp" }, + "C_Cpp.files.exclude": { + "${workspaceFolder:c2usb}/c2usb/**/*.test.cpp": true + }, "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", "C_Cpp.workspaceParsingPriority": "high", "C_Cpp.autoAddFileAssociations": false, diff --git a/patches/core/0006-cmake-don-t-flush-top-level-compiler-flags.patch b/patches/core/0001-cmake-don-t-flush-top-level-compiler-flags.patch similarity index 52% rename from patches/core/0006-cmake-don-t-flush-top-level-compiler-flags.patch rename to patches/core/0001-cmake-don-t-flush-top-level-compiler-flags.patch index 4e181c22e..2d08a7009 100644 --- a/patches/core/0006-cmake-don-t-flush-top-level-compiler-flags.patch +++ b/patches/core/0001-cmake-don-t-flush-top-level-compiler-flags.patch @@ -1,24 +1,23 @@ -From 2c8942648651f955bba2990e92354b04a46e07b7 Mon Sep 17 00:00:00 2001 +From c1aa6de8d459d59d6903943f9fcd540ca535604e Mon Sep 17 00:00:00 2001 From: Benedek Kupper -Date: Mon, 23 Feb 2026 20:03:41 +0100 +Date: Fri, 10 Jul 2026 16:19:13 +0200 Subject: [PATCH] cmake: don't flush top-level compiler flags This was preventing CMAKE_C_FLAGS_RELEASE and similar from being set in the presets. --- - cmake/extension/mcux.cmake | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) + cmake/extension/mcux.cmake | 1 - + 1 file changed, 1 deletion(-) diff --git a/cmake/extension/mcux.cmake b/cmake/extension/mcux.cmake -index db62e4e..8b2b203 100644 +index 57c92ef5..5607bace 100644 --- a/cmake/extension/mcux.cmake +++ b/cmake/extension/mcux.cmake -@@ -71,7 +71,7 @@ macro(project project_name) - # valiate compiler version - # The reason to validate compiler version here is that cmake engine code will get CMAKE_C_COMPILER_VERSION and CMAKE_CXX_COMPILER_VERSION just in "project" macro invocation. - _validate_compiler_version() +@@ -133,7 +133,6 @@ macro(project project_name) + if(NOT (DEFINED MCUX_SKIP_COMPILER_CHECKS AND MCUX_SKIP_COMPILER_CHECKS)) + _validate_compiler_version() + endif() - clear_default_added_compiler_flags() -+ # parse arguments set(options NO_DEFAULT_CONFIG) set(single_value PROJECT_BOARD_PORT_PATH PROJECT_DEVICE_PORT_PATH PROJECT_TYPE CUSTOM_LINKER) diff --git a/patches/core/0005-cmake-set-_IS_TOP_LEVEL-project-variables.patch b/patches/core/0005-cmake-set-_IS_TOP_LEVEL-project-variables.patch deleted file mode 100644 index fd6b35255..000000000 --- a/patches/core/0005-cmake-set-_IS_TOP_LEVEL-project-variables.patch +++ /dev/null @@ -1,76 +0,0 @@ -From c12b32684bbbd2bf33dbb0924b40769d02f97739 Mon Sep 17 00:00:00 2001 -From: Benedek Kupper -Date: Tue, 3 Feb 2026 23:57:45 +0100 -Subject: [PATCH] cmake: set _IS_TOP_LEVEL project variables - ---- - cmake/extension/mcux.cmake | 53 ++++++++++++++++++++++++++------------ - 1 file changed, 37 insertions(+), 16 deletions(-) - -diff --git a/cmake/extension/mcux.cmake b/cmake/extension/mcux.cmake -index 96732ef..db62e4e 100644 ---- a/cmake/extension/mcux.cmake -+++ b/cmake/extension/mcux.cmake -@@ -29,22 +29,43 @@ macro(project project_name) - - __project(${${project_ARGV}}) - -- set(PROJECT_NAME -- "${PROJECT_NAME}" -- PARENT_SCOPE) -- set(PROJECT_BINARY_DIR -- "${PROJECT_BINARY_DIR}" -- PARENT_SCOPE) -- set(PROJECT_SOURCE_DIR -- "${PROJECT_SOURCE_DIR}" -- PARENT_SCOPE) -- -- set(${PROJECT_NAME}_BINARY_DIR -- "${${PROJECT_NAME}_BINARY_DIR}" -- PARENT_SCOPE) -- set(${PROJECT_NAME}_SOURCE_DIR -- "${${PROJECT_NAME}_SOURCE_DIR}" -- PARENT_SCOPE) -+ # Set the variables that project() normally sets, documented in the -+ # command's docs. -+ # -+ # https://cmake.org/cmake/help/v3.16/command/project.html -+ # -+ # There is some nuance when it comes to setting version variables in terms of whether -+ # CMP0048 is set to OLD or NEW. However, the proper behavior should have bee already handled by the original -+ # project call, and we're just echoing the values those variables were set to. -+ set(PROJECT_NAME "${PROJECT_NAME}" PARENT_SCOPE) -+ set(PROJECT_BINARY_DIR "${PROJECT_BINARY_DIR}" PARENT_SCOPE) -+ set(PROJECT_SOURCE_DIR "${PROJECT_SOURCE_DIR}" PARENT_SCOPE) -+ set(PROJECT_VERSION "${PROJECT_VERSION}" PARENT_SCOPE) -+ set(PROJECT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}" PARENT_SCOPE) -+ set(PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR}" PARENT_SCOPE) -+ set(PROJECT_VERSION_PATCH "${PROJECT_VERSION_PATCH}" PARENT_SCOPE) -+ set(PROJECT_VERSION_TWEAK "${PROJECT_VERSION_TWEAK}" PARENT_SCOPE) -+ set(PROJECT_DESCRIPTION "${PROJECT_DESCRIPTION}" PARENT_SCOPE) -+ set(PROJECT_HOMEPAGE_URL "${PROJECT_HOMEPAGE_URL}" PARENT_SCOPE) -+ -+ set(${PROJECT_NAME}_BINARY_DIR "${${PROJECT_NAME}_BINARY_DIR}" PARENT_SCOPE) -+ set(${PROJECT_NAME}_SOURCE_DIR "${${PROJECT_NAME}_SOURCE_DIR}" PARENT_SCOPE) -+ set(${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION}" PARENT_SCOPE) -+ set(${PROJECT_NAME}_VERSION_MAJOR "${${PROJECT_NAME}_VERSION_MAJOR}" PARENT_SCOPE) -+ set(${PROJECT_NAME}_VERSION_MINOR "${${PROJECT_NAME}_VERSION_MINOR}" PARENT_SCOPE) -+ set(${PROJECT_NAME}_VERSION_PATCH "${${PROJECT_NAME}_VERSION_PATCH}" PARENT_SCOPE) -+ set(${PROJECT_NAME}_VERSION_TWEAK "${${PROJECT_NAME}_VERSION_TWEAK}" PARENT_SCOPE) -+ set(${PROJECT_NAME}_DESCRIPTION "${${PROJECT_NAME}_DESCRIPTION}" PARENT_SCOPE) -+ set(${PROJECT_NAME}_HOMEPAGE_URL "${${PROJECT_NAME}_HOMEPAGE_URL}" PARENT_SCOPE) -+ if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.21") -+ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) -+ set(PROJECT_IS_TOP_LEVEL ON PARENT_SCOPE) -+ set(${PROJECT_NAME}_IS_TOP_LEVEL ON PARENT_SCOPE) -+ else() -+ set(PROJECT_IS_TOP_LEVEL OFF PARENT_SCOPE) -+ set(${PROJECT_NAME}_IS_TOP_LEVEL OFF PARENT_SCOPE) -+ endif() -+ endif() - endfunction() - - # valiate compiler version --- -2.43.0 - diff --git a/patches/etl/0001-delegate-allow-constructing-from-non-capturing-lambd.patch b/patches/etl/0001-delegate-allow-constructing-from-non-capturing-lambd.patch deleted file mode 100644 index f418a14af..000000000 --- a/patches/etl/0001-delegate-allow-constructing-from-non-capturing-lambd.patch +++ /dev/null @@ -1,183 +0,0 @@ -From bfbb7259e128b6fac0ac3736a8f8297533b332fd Mon Sep 17 00:00:00 2001 -From: Benedek Kupper -Date: Mon, 9 Feb 2026 22:35:13 +0100 -Subject: [PATCH] delegate: allow constructing from non-capturing lambdas - -Signed-off-by: Benedek Kupper ---- - include/etl/private/delegate_cpp11.h | 63 +++++++++++++++++++++++++++- - test/test_delegate.cpp | 47 +++++++++++++++++++-- - 2 files changed, 105 insertions(+), 5 deletions(-) - -diff --git a/include/etl/private/delegate_cpp11.h b/include/etl/private/delegate_cpp11.h -index 288c3a5..c7d41e2 100644 ---- a/include/etl/private/delegate_cpp11.h -+++ b/include/etl/private/delegate_cpp11.h -@@ -155,10 +155,23 @@ namespace etl - - //************************************************************************* - // Delete construction from rvalue reference lambda or functor. -+ // Excludes non-capturing lambdas convertible to a function pointer. - //************************************************************************* -- template ::value && !etl::is_same, TLambda>::value, void>> -+ template ::value && !etl::is_same, TLambda>::value && !etl::is_convertible::value, void>> - ETL_CONSTEXPR14 delegate(TLambda&& instance) = delete; - -+ //************************************************************************* -+ // Construct from non-capturing rvalue lambda convertible to function pointer. -+ // Converts to a function pointer to avoid storing a dangling pointer -+ // to a destroyed temporary. -+ //************************************************************************* -+ template ::value && !etl::is_reference::value && etl::is_convertible::value, int> = 0> -+ delegate(TLambda&& instance) ETL_NOEXCEPT -+ { -+ TReturn(*fp)(TArgs...) = static_cast(instance); -+ assign(reinterpret_cast(fp), function_ptr_stub); -+ } -+ - //************************************************************************* - /// Create from function (Compile time). - //************************************************************************* -@@ -189,6 +202,19 @@ namespace etl - return delegate((void*)(&instance), const_lambda_stub); - } - -+ //************************************************************************* -+ // Create from non-capturing rvalue lambda convertible to function pointer. -+ // Converts to a function pointer to avoid storing a dangling pointer -+ // to a destroyed temporary. -+ //************************************************************************* -+ template ::value && !etl::is_reference::value && etl::is_convertible::value, int> = 0> -+ ETL_NODISCARD -+ static delegate create(TLambda&& instance) ETL_NOEXCEPT -+ { -+ TReturn(*fp)(TArgs...) = static_cast(instance); -+ return delegate(reinterpret_cast(fp), function_ptr_stub); -+ } -+ - //************************************************************************* - /// Create from instance method (Run time). - //************************************************************************* -@@ -305,6 +331,18 @@ namespace etl - assign((void*)(&instance), const_lambda_stub); - } - -+ //************************************************************************* -+ // Set from non-capturing rvalue lambda convertible to function pointer. -+ // Converts to a function pointer to avoid storing a dangling pointer -+ // to a destroyed temporary. -+ //************************************************************************* -+ template ::value && !etl::is_reference::value && etl::is_convertible::value, int> = 0> -+ void set(TLambda&& instance) ETL_NOEXCEPT -+ { -+ TReturn(*fp)(TArgs...) = static_cast(instance); -+ assign(reinterpret_cast(fp), function_ptr_stub); -+ } -+ - //************************************************************************* - /// Set from instance method (Run time). - //************************************************************************* -@@ -494,6 +532,19 @@ namespace etl - return *this; - } - -+ //************************************************************************* -+ // Create from non-capturing rvalue lambda convertible to function pointer. -+ // Converts to a function pointer to avoid storing a dangling pointer -+ // to a destroyed temporary. -+ //************************************************************************* -+ template ::value && !etl::is_reference::value && etl::is_convertible::value, int> = 0> -+ delegate& operator =(TLambda&& instance) ETL_NOEXCEPT -+ { -+ TReturn(*fp)(TArgs...) = static_cast(instance); -+ assign(reinterpret_cast(fp), function_ptr_stub); -+ return *this; -+ } -+ - //************************************************************************* - /// Checks equality. - //************************************************************************* -@@ -679,6 +730,16 @@ namespace etl - return (Method)(etl::forward(args)...); - } - -+ //************************************************************************* -+ /// Stub call for a runtime function pointer stored in the object field. -+ //************************************************************************* -+ static TReturn function_ptr_stub(void* object, TArgs... args) -+ { -+ ETL_STATIC_ASSERT(sizeof(void*) >= sizeof(TReturn(*)(TArgs...)), "etl::delegate: function pointer too large to store in object field"); -+ TReturn(*fp)(TArgs...) = reinterpret_cast(object); -+ return fp(etl::forward(args)...); -+ } -+ - //************************************************************************* - /// Stub call for a lambda or functor function. - //************************************************************************* -diff --git a/test/test_delegate.cpp b/test/test_delegate.cpp -index 57c52e8..3f6d8f5 100644 ---- a/test/test_delegate.cpp -+++ b/test/test_delegate.cpp -@@ -634,16 +634,55 @@ namespace - #endif - - //************************************************************************* -- TEST_FIXTURE(SetupFixture, test_lambda_int_create) -+ TEST_FIXTURE(SetupFixture, test_construct_from_rvalue_non_capturing_lambda) - { -- auto lambda = [](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); }; -+ etl::delegate d([](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); return i + j; }); - -- etl::delegate d(lambda); -+ int result = d(VALUE1, VALUE2); - -- d(VALUE1, VALUE2); -+ CHECK(function_called == FunctionCalled::Lambda_Called); -+ CHECK(parameter_correct); -+ CHECK_EQUAL(result, VALUE1 + VALUE2); -+ } -+ -+ //************************************************************************* -+ TEST_FIXTURE(SetupFixture, test_assign_from_rvalue_non_capturing_lambda) -+ { -+ etl::delegate d; -+ -+ d = [](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); return i + j + 2; }; -+ -+ int result = d(VALUE1, VALUE2); -+ -+ CHECK(function_called == FunctionCalled::Lambda_Called); -+ CHECK(parameter_correct); -+ CHECK_EQUAL(result, VALUE1 + VALUE2 + 2); -+ } -+ -+ //************************************************************************* -+ TEST_FIXTURE(SetupFixture, test_create_from_rvalue_non_capturing_lambda) -+ { -+ auto d = etl::delegate::create([](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); return i + j + 5; }); -+ -+ int result = d(VALUE1, VALUE2); -+ -+ CHECK(function_called == FunctionCalled::Lambda_Called); -+ CHECK(parameter_correct); -+ CHECK_EQUAL(result, VALUE1 + VALUE2 + 5); -+ } -+ -+ //************************************************************************* -+ TEST_FIXTURE(SetupFixture, test_set_from_rvalue_non_capturing_lambda_returning_int) -+ { -+ etl::delegate d; -+ -+ d.set([](int i, int j) { function_called = FunctionCalled::Lambda_Called; parameter_correct = (i == VALUE1) && (j == VALUE2); return i + j + 6; }); -+ -+ int result = d(VALUE1, VALUE2); - - CHECK(function_called == FunctionCalled::Lambda_Called); - CHECK(parameter_correct); -+ CHECK_EQUAL(result, VALUE1 + VALUE2 + 6); - } - - #if ETL_USING_CPP17 --- -2.43.0 - diff --git a/right/prj.conf b/right/prj.conf index 94d9ceedb..43340840b 100644 --- a/right/prj.conf +++ b/right/prj.conf @@ -18,6 +18,7 @@ CONFIG_USB_DEVICE_CONFIG_LOW_POWER_MODE=y CONFIG_USB_DEVICE_CONFIG_REMOTE_WAKEUP=y CONFIG_C2USB_MCUX_MAC=y CONFIG_C2USB_MCUX_USB_COEXISTENCE=y +CONFIG_C2USB_HID_BOOT_PROTOCOL=y # disable board (and device) specific source files, as the project has its own sources # examples/_common/project_segments/common/Kconfig.prjseg diff --git a/right/src/hid/CMakeLists.txt b/right/src/hid/CMakeLists.txt index a6ba9a42f..4b074ccbf 100644 --- a/right/src/hid/CMakeLists.txt +++ b/right/src/hid/CMakeLists.txt @@ -10,8 +10,9 @@ target_sources(${PROJECT_NAME} PRIVATE transport_usb.cpp ) -if(CONFIG_C2USB_BLUETOOTH) +if(CONFIG_C2USB_HOGP) target_sources(${PROJECT_NAME} PRIVATE + ble_app.cpp transport_ble.cpp ) endif() diff --git a/right/src/hid/ble_app.cpp b/right/src/hid/ble_app.cpp new file mode 100644 index 000000000..4bdb24642 --- /dev/null +++ b/right/src/hid/ble_app.cpp @@ -0,0 +1,141 @@ +#include "hid/ble_app.hpp" +#include +extern "C" { +#include "hid/transport.h" +#include "utils.h" +#if __has_include() + #include +#endif +} + +void ble_session::set_report(hid::report::type type, const std::span &data) +{ + if (protocol() == hid::protocol::BOOT) { + if ((type == hid::report::type::OUTPUT) and (data.size() >= sizeof(leds_boot_report))) { + leds_buffer_.leds = reinterpret_cast(data.data())->leds; + + keyboard_leds_changed_callback(*this); + } + } else { + // report protocol, report identified by type and report ID + switch (hid::report::selector(type, data.front())) { + case leds_report::selector(): { + if (data.size() >= sizeof(leds_report)) { + leds_buffer_.leds = reinterpret_cast(data.data())->leds; + + keyboard_leds_changed_callback(*this); + } + break; + } + case resolution_buffer_.selector(): { + if (data.size() >= sizeof(resolution_buffer_)) { + mouse_resolution_changed_callback(*this, resolution_report()); + } + break; + } + case out_buffer_.selector(): + std::ignore = new (in_buffer_.data()) command_session::report_in{}; + UsbProtocolHandler(out_buffer_.payload.data(), in_buffer_.payload.data()); + + if (auto err = send_report(&in_buffer_); err != c2usb::result::ok) { +#if __has_include() + printk("Command response failed to send (%d)\n", std::bit_cast(err)); +#endif + } + break; + } + } + + // reports are received by type, use the largest buffer for each type + if (type == hid::report::type::OUTPUT) { + receive_report(&out_buffer_); + } else if (type == hid::report::type::FEATURE) { + receive_report(&resolution_buffer_); + } +} + +std::span ble_session::get_report( + hid::report::selector select, const std::span &buffer) +{ + if (protocol() == hid::protocol::BOOT) { + if (select == keys_boot_report::selector()) { + assert(buffer.size() >= sizeof(keys_boot_report)); + std::ignore = new (buffer.data()) keys_boot_report{}; + return buffer.subspan(0, sizeof(keys_boot_report)); + } + if (select == leds_boot_report::selector()) { + assert(buffer.size() >= sizeof(leds_boot_report)); + auto *ptr = new (buffer.data()) leds_boot_report{}; + ptr->leds = leds_buffer_.leds; + return buffer.subspan(0, sizeof(leds_boot_report)); + } + { + return {}; + } + } + // reuse in_buffer_ for all input reports, as the provided buffer size is insufficient (at least on Android) + // always send empty reports for simplicity (as the session doesn't own the input report buffers) + switch (select) { + + case keys_6kro_report::selector(): + std::ignore = new (in_buffer_.data()) keys_6kro_report{}; + return std::span(in_buffer_.data(), sizeof(keys_6kro_report)); + + case keys_nkro_report::selector(): + std::ignore = new (in_buffer_.data()) keys_nkro_report{}; + return std::span(in_buffer_.data(), sizeof(keys_nkro_report)); + + case leds_report::selector(): { + assert(buffer.size() >= sizeof(leds_report)); + auto *ptr = new (buffer.data()) leds_report{}; + ptr->leds = leds_buffer_.leds; + return buffer.subspan(0, sizeof(leds_report)); + } + + case mouse_report::selector(): + std::ignore = new (in_buffer_.data()) mouse_report{}; + return std::span(in_buffer_.data(), sizeof(mouse_report)); + + case resolution_buffer_.selector(): + return std::span(resolution_buffer_.data(), sizeof(resolution_buffer_)); + + case out_buffer_.selector(): + std::ignore = new (in_buffer_.data()) decltype(out_buffer_){}; + return std::span(in_buffer_.data(), sizeof(out_buffer_)); + + case in_buffer_.selector(): + std::ignore = new (in_buffer_.data()) decltype(in_buffer_){}; + return std::span(in_buffer_.data(), sizeof(in_buffer_)); + + case controls_report::selector(): + std::ignore = new (in_buffer_.data()) controls_report{}; + return std::span(in_buffer_.data(), sizeof(controls_report)); + + default: + return {}; + } +} + +void ble_session::report_sent(const std::span &data) +{ + if (protocol() == hid::protocol::BOOT) { + keyboard_report_sent_callback(*this); + } else { + switch (data.front()) { + case keys_6kro_report::selector().id(): + case keys_nkro_report::selector().id(): + keyboard_report_sent_callback(*this); + break; + case mouse_report::selector().id(): + mouse_report_sent_callback(*this); + break; + case controls_report::selector().id(): + controls_report_sent_callback(*this); + break; + case command_session::report_in::selector().id(): + break; + default: + break; + } + } +} diff --git a/right/src/hid/ble_app.hpp b/right/src/hid/ble_app.hpp new file mode 100644 index 000000000..6a92b0ef3 --- /dev/null +++ b/right/src/hid/ble_app.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include "command_app.hpp" +#include "controls_app.hpp" +#include "keyboard_app.hpp" +#include "mouse_app.hpp" + +class ble_session : public keyboard_base_session { + public: + using keys_boot_report = hid::app::keyboard::keys_input_report<0>; + using keys_6kro_report = hid::app::keyboard::keys_input_report; + using keys_nkro_report = keyboard_app::keys_nkro_report_base; + using mouse_report = mouse_app::mouse_report_base; + using controls_report = controls_app::controls_report_base; + + ble_session(const hid::session::params &p) : keyboard_base_session(p) + { + receive_report(&resolution_buffer_); + receive_report(&out_buffer_); + } + + leds_boot_report get_leds_report() const override { return leds_buffer_; } + const auto &resolution_report() const { return resolution_buffer_; } + + // definition in transport_ble.cpp + static ble_session *lookup_by_conn(::bt_conn *conn); + ::bt_conn *get_conn(); + + protected: + mouse_session::scroll_resolution_report resolution_buffer_{}; + command_session::report_in in_buffer_{}; + command_session::report_out out_buffer_{}; + leds_boot_report leds_buffer_{}; + + void report_sent(const std::span &data) override; + + std::span get_report( + hid::report::selector select, const std::span &buffer) override; + + void set_report(hid::report::type type, const std::span &data) override; +}; + +class ble_app : public hid::application { + public: + static constexpr auto report_desc() + { + return hid::rdf::descriptor(keyboard_app::report_desc(), mouse_app::report_desc(), + command_app::report_desc(), controls_app::report_desc()); + } + static ble_app &handle() + { + static ble_app app{hid::report_protocol::from_descriptor()}; + return app; + } + + private: + ble_app(const hid::report_protocol &rp) : hid::application(rp) {} + + hid::session &start(const hid::session::params ¶ms) override; + void stop(hid::session &sess) override; +}; diff --git a/right/src/hid/command_app.cpp b/right/src/hid/command_app.cpp index f9dd7277e..ec2465c3a 100644 --- a/right/src/hid/command_app.cpp +++ b/right/src/hid/command_app.cpp @@ -5,27 +5,19 @@ #include #endif -command_app &command_app::usb_handle() +hid::session &command_app::start(const hid::session::params ¶ms) { - static command_app app{}; - return app; + assert(!session_.has_value()); + return session_.emplace(params); } -#if DEVICE_IS_UHK80_RIGHT -command_app &command_app::ble_handle() +void command_app::stop(hid::session &sess) { - static command_app ble_app{}; - return ble_app; -} -#endif - -void command_app::start(hid::protocol prot) -{ - // start receiving reports - receive_report(&out_buffer_[out_buffer_.active_side()]); + assert(&sess == &session_.value()); + return session_.reset(); } -void command_app::set_report(hid::report::type type, const std::span &data) +void command_session::set_report(hid::report::type type, const std::span &data) { if (type != hid::report::type::OUTPUT) { return; @@ -33,51 +25,27 @@ void command_app::set_report(hid::report::type type, const std::span(data.data()); - auto buf_idx = in_buffer_.active_side(); - auto &in = in_buffer_[buf_idx]; - UsbProtocolHandler(const_cast(out.payload.data()), in.payload.data()); - auto err = send_report(&in); - if (err == hid::result::ok) { - in_buffer_.swap_sides(); - } else { + UsbProtocolHandler(out_buffer_.payload.data(), in_buffer_.payload.data()); + auto err = send_report(&in_buffer_); + if (err != c2usb::result::ok) { #if __has_include() - printk("Command app failed to send report with: %d\n", std::bit_cast(err)); + printk("Command response failed to send (%d)\n", std::bit_cast(err)); #endif } + receive_report(&out_buffer_); } -void command_app::get_report(hid::report::selector select, const std::span &buffer) -{ - if (select != report_in::selector()) { - return; - } - // copy to buffer to avoid overwriting data in transit - auto &report = in_buffer_[in_buffer_.inactive_side()]; - assert(buffer.size() >= sizeof(report)); - memcpy(buffer.data(), report.data(), sizeof(report)); - send_report(buffer.subspan(0, sizeof(report))); -} - -void command_app::in_report_sent(const std::span &data) +std::span command_session::get_report( + hid::report::selector select, const std::span &buffer) { -#if DEVICE_IS_UHK80_RIGHT - // On BLE all apps share one merged HOGP interface, so in_report_sent is broadcast - // to every app; act only on our own (command) report ID. The USB handle is a - // standalone interface (whose report may carry no report-ID byte), so the filter - // must not be applied there. - if ((this == &ble_handle()) && (data.front() != report_ids::IN_COMMAND)) { - return; + switch (select.type()) { + case hid::report::type::INPUT: + in_buffer_.payload.fill(0); + return std::span(in_buffer_.data(), sizeof(in_buffer_)); + case hid::report::type::OUTPUT: + out_buffer_.payload.fill(0); + return std::span(out_buffer_.data(), sizeof(out_buffer_)); + default: + return {}; } -#else - if (data.front() != report_ids::IN_COMMAND) { - return; - } -#endif - auto buf_idx = in_buffer_.indexof(data.data()); - in_buffer_.compare_swap(buf_idx); } diff --git a/right/src/hid/command_app.hpp b/right/src/hid/command_app.hpp index 410bc9952..df50daa60 100644 --- a/right/src/hid/command_app.hpp +++ b/right/src/hid/command_app.hpp @@ -1,5 +1,4 @@ -#ifndef __COMMAND_APP_HEADER__ -#define __COMMAND_APP_HEADER__ +#pragma once extern "C" { #include "device.h" @@ -23,15 +22,35 @@ enum class ugl : uint8_t { COMMAND_DATA_IN = 0x0002, COMMAND_DATA_OUT = 0x0003, }; - } // namespace hid::page -class command_app : public hid::application { - command_app() : application(report_protocol()) {} - +class command_session : public hid::session { public: static constexpr size_t MESSAGE_SIZE = USB_COMMAND_BUFFER_LENGTH; + template + struct report_base : public hid::report::base { + std::array payload{}; + }; + using report_in = report_base; + using report_out = report_base; + + command_session(const hid::session::params &p) : hid::session(p) + { + receive_report(&out_buffer_); + } + + private: + C2USB_USB_TRANSFER_ALIGN(report_in, in_buffer_) {}; + C2USB_USB_TRANSFER_ALIGN(report_out, out_buffer_) {}; + + std::span get_report( + hid::report::selector select, const std::span &buffer) override; + void set_report(hid::report::type type, const std::span &data) override; +}; + +class command_app : public hid::application { + public: static constexpr auto report_desc() { using namespace hid::page; @@ -44,7 +63,7 @@ class command_app : public hid::application { collection::application( conditional_report_id(), report_size(8), - report_count(MESSAGE_SIZE), + report_count(command_session::MESSAGE_SIZE), logical_limits<1, 1>(0, 0xff), usage(ugl::COMMAND_DATA_IN), input::buffered_variable(), @@ -53,26 +72,14 @@ class command_app : public hid::application { output::buffered_variable() ) ); - // clang-format off + // clang-format on } - template - struct report_base : public hid::report::base + static command_app &usb_handle() { - std::array payload{}; - }; - using report_in = report_base; - using report_out = report_base; - - static command_app& usb_handle(); -#if DEVICE_IS_UHK80_RIGHT - static command_app& ble_handle(); -#endif - - void start(hid::protocol prot) override; - void set_report(hid::report::type type, const std::span& data) override; - void get_report(hid::report::selector select, const std::span& buffer) override; - void in_report_sent(const std::span& data) override; + static command_app app{}; + return app; + } private: static hid::report_protocol report_protocol() @@ -81,9 +88,9 @@ class command_app : public hid::application { constexpr hid::report_protocol rp{rd}; return rp; } + std::optional session_{}; - double_buffer in_buffer_{}; - double_buffer out_buffer_{}; + command_app() : application(report_protocol()) {} + hid::session &start(const hid::session::params ¶ms) override; + void stop(hid::session &sess) override; }; - -#endif // __COMMAND_APP_HEADER__ diff --git a/right/src/hid/controls_app.cpp b/right/src/hid/controls_app.cpp index b42c54b44..c2068623e 100644 --- a/right/src/hid/controls_app.cpp +++ b/right/src/hid/controls_app.cpp @@ -1,58 +1,33 @@ #include "controls_app.hpp" -controls_app &controls_app::usb_handle() +hid::session &controls_app::start(const hid::session::params ¶ms) { - static controls_app app{}; - return app; + assert(!session_.has_value()); + auto &sess = session_.emplace(params); + return sess; } -#if DEVICE_IS_UHK80_RIGHT -controls_app &controls_app::ble_handle() +void controls_app::stop(hid::session &sess) { - static controls_app ble_app{}; - return ble_app; + assert(&sess == &session_.value()); + return session_.reset(); } -#endif -void controls_app::start(hid::protocol prot) +void controls_session::report_sent(const std::span &data) { - report_buffer_.reset(); + controls_report_sent_callback(*this); } -int controls_app::send_report(const hid_controls_report_t &report) +std::span controls_session::get_report( + hid::report::selector select, const std::span &buffer) { - auto &buf = report_buffer_[report_buffer_.active_side()]; - buf.data = report; - auto result = application::send_report( - std::span(reinterpret_cast(&buf), sizeof(buf))); - if (result == hid::result::ok) { - report_buffer_.swap_sides(); - } - return result.to_int(); -} + using controls_report = controls_app::controls_report_base; -void controls_app::in_report_sent(const std::span &data) -{ -#if DEVICE_IS_UHK80_RIGHT - // On BLE all apps share one merged HOGP interface, so in_report_sent is broadcast - // to every app for every sent report. Act only on our own (controls) report ID. - // The USB handle is a standalone interface, so the filter must not be applied there. - if ((this == &ble_handle()) && (data.front() != report_ids::IN_CONTROLS)) { - return; - } -#endif - Hid_ControlsReportSentCallback((this == &usb_handle()) ? HID_TRANSPORT_USB : HID_TRANSPORT_BLE); -} - -void controls_app::get_report(hid::report::selector select, const std::span &buffer) -{ - if (select != controls_report::selector()) { - return; + if (select == hid::report::selector(hid::report::type::INPUT, report_ids::IN_CONTROLS)) { + assert(buffer.size() >= sizeof(controls_report)); + std::ignore = new (buffer.data()) controls_report{}; + return buffer.subspan(0, sizeof(controls_report)); } - // copy to buffer to avoid overwriting data in transit - auto &report = report_buffer_[report_buffer_.inactive_side()]; - assert(buffer.size() >= sizeof(report)); - memcpy(buffer.data(), &report, sizeof(report)); - application::send_report(buffer.subspan(0, sizeof(report))); + return {}; } diff --git a/right/src/hid/controls_app.hpp b/right/src/hid/controls_app.hpp index 2fc4a2ac7..9cd19c7c2 100644 --- a/right/src/hid/controls_app.hpp +++ b/right/src/hid/controls_app.hpp @@ -1,5 +1,4 @@ -#ifndef __CONTROLS_APP_HEADER__ -#define __CONTROLS_APP_HEADER__ +#pragma once extern "C" { #include "hid/controls_report.h" @@ -16,6 +15,19 @@ extern "C" { using system_code = hid::page::generic_desktop; using consumer_code = hid::page::consumer; +class controls_session : public hid::session { + public: + using hid::session::session; + + protected: + void report_sent(const std::span &data) override; + + std::span get_report( + hid::report::selector select, const std::span &buffer) override; + + void set_report(hid::report::type type, const std::span &data) override {} +}; + class controls_app : public hid::application { static constexpr size_t CONSUMER_CODE_COUNT = HID_CONTROLS_MAX_CONSUMER_KEYS; static constexpr size_t SYSTEM_CODE_COUNT = HID_CONTROLS_MAX_SYSTEM_KEYS; @@ -52,7 +64,7 @@ class controls_app : public hid::application { collection::application( conditional_report_id(), - report_size(sizeof(hid::le_uint16_t) * 8), + report_size(sizeof(c2usb::le_uint16_t) * 8), report_count(CONSUMER_CODE_COUNT), logical_limits<1, 2>(0, get_info().max_usage_id), usage_extended_limits(nullusage, static_cast(get_info().max_usage_id)), @@ -68,28 +80,21 @@ class controls_app : public hid::application { // clang-format on } - static controls_app &usb_handle(); -#if DEVICE_IS_UHK80_RIGHT - static controls_app &ble_handle(); -#endif + static controls_app &usb_handle() + { + static controls_app app{}; + return app; + } - int send_report(const hid_controls_report_t &report); + controls_session *session() { return session_.has_value() ? &*session_ : nullptr; } private: controls_app() : hid::application(hid::report_protocol::from_descriptor()) {} - void start(hid::protocol prot) override; - void get_report(hid::report::selector select, const std::span &buffer) override; - void set_report(hid::report::type type, const std::span &data) override - { - // No OUTPUT or FEATURE reports are used - } - void in_report_sent(const std::span &data) override; + hid::session &start(const hid::session::params ¶ms) override; + void stop(hid::session &sess) override; - using controls_report = controls_report_base; - double_buffer report_buffer_{}; + std::optional session_{}; }; -using controls_buffer = controls_app::controls_report_base<0>; - -#endif // __CONTROLS_APP_HEADER__ +void controls_report_sent_callback(hid::session &session); diff --git a/right/src/hid/controls_report.h b/right/src/hid/controls_report.h index c43ae3579..7507bce85 100644 --- a/right/src/hid/controls_report.h +++ b/right/src/hid/controls_report.h @@ -1,5 +1,4 @@ -#ifndef __HID_CONTROLS_REPORT_H__ -#define __HID_CONTROLS_REPORT_H__ +#pragma once #include "arduino_hid/SystemAPI.h" #include "attributes.h" @@ -29,5 +28,3 @@ bool ControlsReport_ContainsConsumerUsage( bool ControlsReport_ContainsSystemUsage( const hid_controls_report_t *report, uint8_t scancode); bool ControlsReport_HasChanges(const hid_controls_report_t buffer[2]); - -#endif // __HID_CONTROLS_REPORT_H__ diff --git a/right/src/hid/double_buffer.hpp b/right/src/hid/double_buffer.hpp index f0a45c123..ac0f31b6e 100644 --- a/right/src/hid/double_buffer.hpp +++ b/right/src/hid/double_buffer.hpp @@ -1,5 +1,4 @@ -#ifndef _DOUBLE_BUFFER_HPP_ -#define _DOUBLE_BUFFER_HPP_ +#pragma once #include #include @@ -9,7 +8,7 @@ template class double_buffer { struct aligned_storage { - C2USB_USB_TRANSFER_ALIGN(T, m_){}; + C2USB_USB_TRANSFER_ALIGN(T, m_) {}; constexpr bool operator==(const aligned_storage &other) const = default; constexpr bool operator!=(const aligned_storage &other) const = default; }; @@ -60,9 +59,19 @@ class double_buffer { // bool differs() const { return std::memcmp(&buffers_[0] != &buffers_[1], sizeof(T)) != 0; } bool differs() const { return buffers_[0] != buffers_[1]; } + // API specific to hid::report::base types + template + std::span insert(const TArg &report) + { + auto buf_idx = active_side(); + memcpy(reinterpret_cast(&buffers_[buf_idx].m_) + + ((T::selector().id() != 0) ? 1 : 0), + &report, sizeof(report)); + return std::span( + reinterpret_cast(&buffers_[buf_idx].m_), sizeof(T)); + } + private: std::array buffers_{}; std::atomic_uint8_t select_{}; }; - -#endif // _DOUBLE_BUFFER_HPP_ \ No newline at end of file diff --git a/right/src/hid/keyboard_app.cpp b/right/src/hid/keyboard_app.cpp index a9fbdde79..7e592615a 100644 --- a/right/src/hid/keyboard_app.cpp +++ b/right/src/hid/keyboard_app.cpp @@ -1,114 +1,56 @@ #include "keyboard_app.hpp" extern "C" { #include "hid/transport.h" +#include "usb_state.h" #include "utils.h" #if __has_include() #include #endif } -keyboard_app &keyboard_app::usb_handle() -{ - static keyboard_app app{nkro_report_protocol()}; - return app; -} -#if DEVICE_IS_UHK80_RIGHT -keyboard_app &keyboard_app::ble_handle() -{ - static keyboard_app ble_app{hid::report_protocol::from_descriptor()}; - return ble_app; -} -#endif - void keyboard_app::set_rollover(rollover_t mode) { - if (rollover_ == mode) { - return; - } - - rollover_ = mode; - if (this == &usb_handle()) { - // swap the HID report descriptor, which needs USB re-enumeration - report_info_ = - mode == rollover_t::ROLLOVER_N_KEY ? nkro_report_protocol() : default_report_protocol(); - } - if (prot_ == hid::protocol::BOOT) { - return; - } - reset_keys(); -} - -void keyboard_app::reset_keys() -{ - keys_.reset(); - // TODO: make sure that no keys are pressed when this happens - // or send an empty report on the virtual keyboard that is deactivated by this switch? - if ((rollover_ == rollover_t::ROLLOVER_N_KEY) && - (rollover_override_ == rollover_t::ROLLOVER_N_KEY)) { - keys_[0].nkro = {}; - keys_[1].nkro = {}; - } else { - keys_[0].sixkro = {}; - keys_[1].sixkro = {}; - } -} - -void keyboard_app::start(hid::protocol prot) -{ - prot_ = prot; - rollover_override_ = {}; - - // start receiving reports - receive_report(&leds_buffer_); - - reset_keys(); - - Hid_TransportStateChanged( - (this == &usb_handle()) ? HID_TRANSPORT_USB : HID_TRANSPORT_BLE, true); + // swap the HID report descriptor, which needs USB re-enumeration + report_info_ = + mode == rollover_t::ROLLOVER_N_KEY ? nkro_report_protocol() : default_report_protocol(); } -void keyboard_app::stop() +hid::session &keyboard_app::start(const hid::session::params ¶ms) { - Hid_TransportStateChanged( - (this == &usb_handle()) ? HID_TRANSPORT_USB : HID_TRANSPORT_BLE, false); + assert(!session_.has_value()); + UsbState_SetUsbTransportUp(true); + return session_.emplace(params); } -bool keyboard_app::using_nkro() const +void keyboard_app::stop(hid::session &sess) { - return (prot_ == hid::protocol::REPORT) && (rollover_ == rollover_t::ROLLOVER_N_KEY) && - (rollover_override_ == rollover_t::ROLLOVER_N_KEY); + assert(&sess == &session_.value()); + UsbState_SetUsbTransportUp(false); + return session_.reset(); } -int keyboard_app::send_report(const hid_keyboard_report_t &report) +void key_report_buffer::reset_to(hid::protocol prot, rollover_t rollover) { - if (!has_transport()) { - return hid::result(hid::result::not_connected).to_int(); - } - auto buf = move_to_buffer(report); - auto result = application::send_report(buf, hid::report::type::INPUT); - -#if DEVICE_IS_UHK80_RIGHT - if (result == hid::result::not_enough_memory) { - // this only happens on Android with NKRO mode when the transport MTU is too small - assert(using_nkro() and (this == &ble_handle())); - printk("keyboard NKRO mode fails, falling back to 6KRO\n"); - - rollover_override_ = rollover_t::ROLLOVER_6_KEY; - buf = move_to_buffer(report); - result = application::send_report(buf, hid::report::type::INPUT); - } -#endif - if (result == hid::result::ok) { - keys_.swap_sides(); + reset(); + // make sure that no keys are pressed when this happens + // or send an empty report on the virtual keyboard that is deactivated by this switch? + if (prot == hid::protocol::BOOT) { + (*this)[0].boot = {}; + (*this)[1].boot = {}; + } else if (rollover == rollover_t::ROLLOVER_N_KEY) { + (*this)[0].nkro = {}; + (*this)[1].nkro = {}; + } else { + (*this)[0].sixkro = {}; + (*this)[1].sixkro = {}; } - return result.to_int(); } -std::span keyboard_app::move_to_buffer(const hid_keyboard_report_t &report) +std::span key_report_buffer::insert(const hid_keyboard_report_t &report) { - auto buf_idx = keys_.active_side(); - if (using_nkro()) { - auto &keys_nkro = keys_[buf_idx].nkro; + auto buf_idx = active_side(); + if (mode == MODE_NKRO) { + auto &keys_nkro = (*this)[buf_idx].nkro; ::memcpy(&keys_nkro.modifiers, &report.modifiers, sizeof(report.modifiers)); ::memcpy( @@ -118,8 +60,8 @@ std::span keyboard_app::move_to_buffer(const hid_keyboard_report_ reinterpret_cast(&keys_nkro), sizeof(keys_nkro)); } - if (prot_ == hid::protocol::BOOT) { - auto &keys_6kro = keys_[buf_idx].boot; + if (mode == MODE_BOOT) { + auto &keys_6kro = (*this)[buf_idx].boot; ::memcpy(&keys_6kro.modifiers, &report.modifiers, sizeof(report.modifiers)); keys_6kro.scancodes.reset(); @@ -133,7 +75,7 @@ std::span keyboard_app::move_to_buffer(const hid_keyboard_report_ } { - auto &keys_6kro = keys_[buf_idx].sixkro; + auto &keys_6kro = (*this)[buf_idx].sixkro; ::memcpy(&keys_6kro.modifiers, &report.modifiers, sizeof(report.modifiers)); keys_6kro.scancodes.reset(); @@ -147,100 +89,82 @@ std::span keyboard_app::move_to_buffer(const hid_keyboard_report_ } } -keyboard_app::leds_boot_report keyboard_app::get_leds() const +keyboard_session::leds_boot_report keyboard_session::get_leds_report() const { auto *ptr = reinterpret_cast(&leds_buffer_); - if ((prot_ != hid::protocol::BOOT) and (sizeof(leds_report) > sizeof(leds_boot_report))) { + if ((protocol() != hid::protocol::BOOT) and (sizeof(leds_report) > sizeof(leds_boot_report))) { ptr += sizeof(leds_report) - sizeof(leds_boot_report); } return reinterpret_cast(*ptr); } -void keyboard_app::set_report(hid::report::type type, const std::span &data) +void keyboard_session::set_report(hid::report::type type, const std::span &data) { + // only one report is receivable, the LEDs if (type != hid::report::type::OUTPUT) { return; } - if ((prot_ == hid::protocol::REPORT) && (LEDS_REPORT_ID != 0) && - (data.front() != LEDS_REPORT_ID)) { - return; - } - // only one report is receivable, the LEDs - // offset it if report ID is not present due to BOOT protocol - [[maybe_unused]] auto &leds = - *reinterpret_cast(data.data() + static_cast(prot_)); - Hid_KeyboardLedsStateChanged((this == &usb_handle()) ? HID_TRANSPORT_USB : HID_TRANSPORT_BLE); + keyboard_leds_changed_callback(*this); // always keep receiving new reports // if the report data is processed immediately, the same buffer can be used receive_report(&leds_buffer_); } -void keyboard_app::in_report_sent(const std::span &data) +void keyboard_session::report_sent(const std::span &data) { -#if DEVICE_IS_UHK80_RIGHT - if ((this == &ble_handle()) && (prot_ == hid::protocol::REPORT) && - (data.front() != KEYS_NKRO_REPORT_ID) && (data.front() != KEYS_6KRO_REPORT_ID)) { - return; - } -#endif - Hid_KeyboardReportSentCallback((this == &usb_handle()) ? HID_TRANSPORT_USB : HID_TRANSPORT_BLE); + keyboard_report_sent_callback(*this); } -void keyboard_app::get_report(hid::report::selector select, const std::span &buffer) +std::span keyboard_session::get_report( + hid::report::selector select, const std::span &buffer) { - if (prot_ == hid::protocol::REPORT) { - if ((select != keys_6kro_report::selector()) && (select != keys_nkro_report::selector())) { - return; + if (protocol() == hid::protocol::BOOT) { + if (select == keyboard_app::keys_boot_report::selector()) { + assert(buffer.size() >= sizeof(keyboard_app::keys_boot_report)); + std::ignore = new (buffer.data()) keyboard_app::keys_boot_report{}; + return buffer.subspan(0, sizeof(keyboard_app::keys_boot_report)); } - } else { - if (select != keys_boot_report::selector()) { - return; + if (select == leds_boot_report::selector()) { + assert(buffer.size() >= sizeof(leds_boot_report)); + auto *ptr = new (buffer.data()) leds_boot_report{}; + ptr->leds = leds_buffer_.leds; + return buffer.subspan(0, sizeof(leds_boot_report)); + } + return {}; + } + + if (select == leds_report::selector()) { + assert(buffer.size() >= sizeof(leds_report)); + auto *ptr = new (buffer.data()) leds_report{}; + ptr->leds = leds_buffer_.leds; + return buffer.subspan(0, sizeof(leds_report)); + } + if constexpr (report_ids::IN_KEYBOARD_NKRO == 0) { + // no report ID, use the rollover mode to determine which report to send + if (select.type() == hid::report::type::INPUT) { + if (HID_GetKeyboardRollover() == rollover_t::ROLLOVER_N_KEY) { + assert(buffer.size() >= sizeof(keyboard_app::keys_nkro_report)); + std::ignore = new (buffer.data()) keyboard_app::keys_nkro_report{}; + return buffer.subspan(0, sizeof(keyboard_app::keys_nkro_report)); + } else { + assert(buffer.size() >= sizeof(keyboard_app::keys_6kro_report)); + std::ignore = new (buffer.data()) keyboard_app::keys_6kro_report{}; + return buffer.subspan(0, sizeof(keyboard_app::keys_6kro_report)); + } } - } - - // copy to buffer to avoid overwriting data in transit - const auto &keys = keys_[keys_.inactive_side()]; -#if DEVICE_IS_UHK60 - if (using_nkro()) { - auto *report = reinterpret_cast(buffer.data()); - *report = keys.nkro; - application::send_report(report); } else { - static_assert(KEYS_6KRO_REPORT_ID == 0); - auto *report = reinterpret_cast(buffer.data()); - *report = keys.sixkro; - application::send_report(report); - } -#else - switch (select.id()) { - case KEYS_6KRO_REPORT_ID: { - auto *report = reinterpret_cast(buffer.data()); - if (using_nkro()) { - *report = {}; - } else { - *report = keys.sixkro; + if (select == keyboard_app::keys_6kro_report::selector()) { + assert(buffer.size() >= sizeof(keyboard_app::keys_6kro_report)); + std::ignore = new (buffer.data()) keyboard_app::keys_6kro_report{}; + return buffer.subspan(0, sizeof(keyboard_app::keys_6kro_report)); } - application::send_report(report); - break; - } - case KEYS_NKRO_REPORT_ID: { - auto *report = reinterpret_cast(buffer.data()); - if (using_nkro()) { - *report = keys.nkro; - } else { - *report = {}; + if (select == keyboard_app::keys_nkro_report::selector()) { + assert(buffer.size() >= sizeof(keyboard_app::keys_nkro_report)); + std::ignore = new (buffer.data()) keyboard_app::keys_nkro_report{}; + return buffer.subspan(0, sizeof(keyboard_app::keys_nkro_report)); } - application::send_report(report); - break; - } - default: { - auto *report = reinterpret_cast(buffer.data()); - *report = keys.boot; - application::send_report(report); - break; } - } -#endif + return {}; } diff --git a/right/src/hid/keyboard_app.hpp b/right/src/hid/keyboard_app.hpp index c86fd6743..40690d35d 100644 --- a/right/src/hid/keyboard_app.hpp +++ b/right/src/hid/keyboard_app.hpp @@ -1,5 +1,4 @@ -#ifndef __KEYBOARD_APP_HEADER__ -#define __KEYBOARD_APP_HEADER__ +#pragma once extern "C" { #include "hid/keyboard_report.h" @@ -9,10 +8,34 @@ extern "C" { #include "double_buffer.hpp" #include #include -#include using scancode = hid::page::keyboard_keypad; +class keyboard_base_session : public hid::session { + public: + using hid::session::session; + using leds_boot_report = hid::app::keyboard::output_report<0>; + using leds_report = hid::app::keyboard::output_report; + virtual leds_boot_report get_leds_report() const = 0; +}; +class keyboard_session : public keyboard_base_session { + C2USB_USB_TRANSFER_ALIGN(leds_report, leds_buffer_) {}; + + protected: + void report_sent(const std::span &data) override; + std::span get_report( + hid::report::selector select, const std::span &buffer) override; + void set_report(hid::report::type type, const std::span &data) override; + + public: + keyboard_session(const hid::session::params &p) : keyboard_base_session(p) + { + receive_report(&leds_buffer_); + } + + leds_boot_report get_leds_report() const override; +}; + class keyboard_app : public hid::application { static constexpr uint8_t KEYS_6KRO_REPORT_ID = report_ids::IN_KEYBOARD_6KRO; static constexpr uint8_t KEYS_NKRO_REPORT_ID = report_ids::IN_KEYBOARD_NKRO; @@ -21,13 +44,16 @@ class keyboard_app : public hid::application { static constexpr auto NKRO_FIRST_USAGE = (scancode)HID_KEYBOARD_MIN_BITFIELD_SCANCODE; // scancode::KEYBOARD_A; static constexpr auto NKRO_LAST_USAGE = (scancode)HID_KEYBOARD_MAX_BITFIELD_SCANCODE; + + public: static constexpr auto LOWEST_SCANCODE = NKRO_FIRST_USAGE; static constexpr auto HIGHEST_SCANCODE = NKRO_LAST_USAGE; - static constexpr auto NKRO_USAGE_COUNT = 1 + static_cast(HIGHEST_SCANCODE) - static_cast(LOWEST_SCANCODE); - public: + using keys_boot_report = hid::app::keyboard::keys_input_report<0>; + using keys_6kro_report = hid::app::keyboard::keys_input_report; + static constexpr auto report_desc() { using namespace hid::page; @@ -111,14 +137,6 @@ class keyboard_app : public hid::application { hid::app::keyboard::app_report_descriptor()>(); } - static keyboard_app &usb_handle(); -#if DEVICE_IS_UHK80_RIGHT - static keyboard_app &ble_handle(); -#endif - - void set_rollover(rollover_t mode); - rollover_t get_rollover() const { return rollover_; } - template struct keys_nkro_report_base : public hid::report::base { hid::report_bitset; - int send_report(const hid_keyboard_report_t &report); + static keyboard_app &usb_handle() + { + static keyboard_app app{nkro_report_protocol()}; + return app; + } + void set_rollover(rollover_t mode); - using leds_boot_report = hid::app::keyboard::output_report<0>; - leds_boot_report get_leds() const; + keyboard_session *session() { return session_.has_value() ? &*session_ : nullptr; } private: - keyboard_app(const hid::report_protocol &rp) : hid::application(rp) {} - - using keys_boot_report = hid::app::keyboard::keys_input_report<0>; - using keys_6kro_report = hid::app::keyboard::keys_input_report; - using leds_report = hid::app::keyboard::output_report; - - using keys_nkro_report = keys_nkro_report_base; - - bool using_nkro() const; - void start(hid::protocol prot) override; - void stop() override; - void set_report(hid::report::type type, const std::span &data) override; - void in_report_sent(const std::span &data) override; - void get_report(hid::report::selector select, const std::span &buffer) override; - hid::protocol get_protocol() const override { return prot_; } - std::span move_to_buffer(const hid_keyboard_report_t &report); + std::optional session_{}; - void send_6kro_buffer(uint8_t buf_idx); - void send_nkro_buffer(uint8_t buf_idx); + keyboard_app(const hid::report_protocol &rp) : hid::application(rp) {} + hid::session &start(const hid::session::params ¶ms) override; + void stop(hid::session &sess) override; +}; - void reset_keys(); +union keys_report_variants { + keyboard_app::keys_nkro_report nkro{}; + keyboard_app::keys_boot_report boot; + keyboard_app::keys_6kro_report sixkro; +}; - C2USB_USB_TRANSFER_ALIGN(leds_report, leds_buffer_) {}; - hid::protocol prot_{}; - rollover_t rollover_{}; - rollover_t rollover_override_{}; - union keys_reports { - keys_nkro_report nkro{}; - keys_boot_report boot; - keys_6kro_report sixkro; - }; - double_buffer keys_{}; +struct key_report_buffer : double_buffer { + static constexpr auto HIGHEST_SCANCODE = keyboard_app::HIGHEST_SCANCODE; + static constexpr auto LOWEST_SCANCODE = keyboard_app::LOWEST_SCANCODE; + enum mode_t { + MODE_BOOT, + MODE_NKRO, + MODE_6KRO, + } mode{MODE_NKRO}; + key_report_buffer() = default; + + void reset_to(hid::protocol prot, rollover_t override); + std::span insert(const hid_keyboard_report_t &report); }; -#endif // __KEYBOARD_APP_HEADER__ +void keyboard_report_sent_callback(hid::session &session); +void keyboard_leds_changed_callback(keyboard_base_session &session); diff --git a/right/src/hid/keyboard_report.h b/right/src/hid/keyboard_report.h index 1b6eb39dd..c3da4efd6 100644 --- a/right/src/hid/keyboard_report.h +++ b/right/src/hid/keyboard_report.h @@ -1,5 +1,4 @@ -#ifndef __HID_KEYBOARD_REPORT_H__ -#define __HID_KEYBOARD_REPORT_H__ +#pragma once #include "attributes.h" #include "lufa/HIDClassCommon.h" @@ -73,5 +72,3 @@ bool KeyboardReport_FindFirstDifference( #define UsbBasicKeyboard_FindFirstDifference KeyboardReport_FindFirstDifference bool KeyboardReport_HasChange(const hid_keyboard_report_t buffers[2]); - -#endif // __HID_KEYBOARD_REPORT_H__ diff --git a/right/src/hid/mouse_app.cpp b/right/src/hid/mouse_app.cpp index 95de56cb7..11fee2b83 100644 --- a/right/src/hid/mouse_app.cpp +++ b/right/src/hid/mouse_app.cpp @@ -1,79 +1,49 @@ #include "mouse_app.hpp" -mouse_app &mouse_app::usb_handle() -{ - static mouse_app app{}; - return app; -} -#if DEVICE_IS_UHK80_RIGHT -mouse_app &mouse_app::ble_handle() -{ - static mouse_app ble_app{}; - return ble_app; -} -#endif +using mouse_report = mouse_app::mouse_report_base; -void mouse_app::start(hid::protocol prot) +hid::session &mouse_app::start(const hid::session::params ¶ms) { - report_buffer_.reset(); - resolution_buffer_ = {}; - receive_report(&resolution_buffer_); + assert(!session_.has_value()); + auto &sess = session_.emplace(params); + mouse_resolution_changed_callback(sess, sess.resolution_report()); + return sess; } -int mouse_app::send_report(const hid_mouse_report_t &report) +void mouse_app::stop(hid::session &sess) { - auto &buf = report_buffer_[report_buffer_.active_side()]; - buf.data = report; - auto result = hid::application::send_report( - std::span(reinterpret_cast(&buf), sizeof(buf))); - if (result == hid::result::ok) { - report_buffer_.swap_sides(); - } - return result.to_int(); + assert(&sess == &session_.value()); + return session_.reset(); } -void mouse_app::in_report_sent(const std::span &data) +void mouse_session::report_sent(const std::span &data) { -#if DEVICE_IS_UHK80_RIGHT - // On BLE all apps share one merged HOGP interface, so in_report_sent is broadcast - // to every app for every sent report. Act only on our own (mouse) report ID. The - // USB handle is a standalone interface that only ever receives mouse completions, - // so the filter must not be applied there. - if ((this == &ble_handle()) && (data.front() != report_ids::IN_MOUSE)) { - return; - } -#endif - Hid_MouseReportSentCallback((this == &usb_handle()) ? HID_TRANSPORT_USB : HID_TRANSPORT_BLE); + mouse_report_sent_callback(*this); } -void mouse_app::set_report(hid::report::type type, const std::span &data) +void mouse_session::set_report(hid::report::type type, const std::span &data) { if (hid::report::selector(type, data.front()) != resolution_buffer_.selector()) { return; } - resolution_buffer_ = *reinterpret_cast(data.data()); + // data is already received to this buffer + // resolution_buffer_ = *reinterpret_cast(data.data()); + mouse_resolution_changed_callback(*this, resolution_report()); receive_report(&resolution_buffer_); - - Hid_MouseScrollResolutionsChanged( - (this == &usb_handle()) ? HID_TRANSPORT_USB : HID_TRANSPORT_BLE, - resolution_buffer_.vertical_scroll_multiplier(), - resolution_buffer_.horizontal_scroll_multiplier()); } -void mouse_app::get_report(hid::report::selector select, const std::span &buffer) +std::span mouse_session::get_report( + hid::report::selector select, const std::span &buffer) { if (select == resolution_buffer_.selector()) { - hid::application::send_report(&resolution_buffer_); - return; + return std::span(resolution_buffer_.data(), sizeof(resolution_buffer_)); } - if (select != mouse_report::selector()) { - return; + if (select == hid::report::selector(hid::report::type::INPUT, report_ids::IN_MOUSE)) { + assert(buffer.size() >= sizeof(mouse_report)); + std::ignore = new (buffer.data()) mouse_report{}; + return buffer.subspan(0, sizeof(mouse_report)); } - // copy to buffer to avoid overwriting data in transit - auto &report = report_buffer_[report_buffer_.inactive_side()]; - assert(buffer.size() >= sizeof(report)); - memcpy(buffer.data(), &report, sizeof(report)); - hid::application::send_report(buffer.subspan(0, sizeof(report))); + return {}; } diff --git a/right/src/hid/mouse_app.hpp b/right/src/hid/mouse_app.hpp index 941a2f2af..9d39c8099 100644 --- a/right/src/hid/mouse_app.hpp +++ b/right/src/hid/mouse_app.hpp @@ -1,5 +1,4 @@ -#ifndef __MOUSE_APP_HEADER__ -#define __MOUSE_APP_HEADER__ +#pragma once extern "C" { #include "hid/mouse_report.h" @@ -11,13 +10,38 @@ extern "C" { #include #include +class mouse_session : public hid::session { + public: + static constexpr int16_t MAX_SCROLL_RESOLUTION = 120; + using scroll_resolution_report = + hid::app::mouse::resolution_multiplier_report; + + mouse_session(const hid::session::params &p) : hid::session(p) + { + receive_report(&resolution_buffer_); + } + + const auto &resolution_report() const { return resolution_buffer_; } + + protected: + C2USB_USB_TRANSFER_ALIGN(scroll_resolution_report, resolution_buffer_) {}; + + void report_sent(const std::span &data) override; + + std::span get_report( + hid::report::selector select, const std::span &buffer) override; + + void set_report(hid::report::type type, const std::span &data) override; +}; + class mouse_app : public hid::application { static constexpr auto LAST_BUTTON = hid::page::button(HID_MOUSE_BUTTON_COUNT); static constexpr int16_t AXIS_LIMIT = 4096; static constexpr int16_t WHEEL_LIMIT = 32767; public: - static constexpr int16_t MAX_SCROLL_RESOLUTION = 120; + static constexpr int16_t MAX_SCROLL_RESOLUTION = mouse_session::MAX_SCROLL_RESOLUTION; static constexpr auto report_desc() { @@ -65,32 +89,23 @@ class mouse_app : public hid::application { bool operator!=(const mouse_report_base &other) const = default; }; - static mouse_app &usb_handle(); -#if DEVICE_IS_UHK80_RIGHT - static mouse_app &ble_handle(); -#endif + static mouse_app &usb_handle() + { + static mouse_app app{}; + return app; + } - int send_report(const hid_mouse_report_t &report); + mouse_session *session() { return session_.has_value() ? &*session_ : nullptr; } private: - mouse_app() : hid::application(hid::report_protocol::from_descriptor()) {} + mouse_app() : application(hid::report_protocol::from_descriptor()) {} - void start(hid::protocol prot) override; - void set_report(hid::report::type type, const std::span &data) override; - void get_report(hid::report::selector select, const std::span &buffer) override; - void in_report_sent(const std::span &data) override; + hid::session &start(const hid::session::params ¶ms) override; + void stop(hid::session &sess) override; - using mouse_report = mouse_report_base; - double_buffer report_buffer_{}; - using scroll_resolution_report = - hid::app::mouse::resolution_multiplier_report; - C2USB_USB_TRANSFER_ALIGN(scroll_resolution_report, resolution_buffer_) {}; - - public: - const auto &resolution_report() const { return resolution_buffer_; } + std::optional session_{}; }; -using mouse_buffer = mouse_app::mouse_report_base<>; - -#endif // __KEYBOARD_APP_HEADER__ +void mouse_report_sent_callback(hid::session &session); +void mouse_resolution_changed_callback( + hid::session &session, const mouse_session::scroll_resolution_report &report); diff --git a/right/src/hid/mouse_report.h b/right/src/hid/mouse_report.h index 7e0947533..4e500f072 100644 --- a/right/src/hid/mouse_report.h +++ b/right/src/hid/mouse_report.h @@ -1,5 +1,4 @@ -#ifndef __HID_MOUSE_REPORT_H__ -#define __HID_MOUSE_REPORT_H__ +#pragma once #include "attributes.h" #include @@ -21,5 +20,3 @@ float HorizontalScrollMultiplier(void); void MouseReport_MergeReports(hid_mouse_report_t *sourceReport, hid_mouse_report_t *targetReport); bool MouseReport_HasChanges(const hid_mouse_report_t buffer[2], const hid_mouse_report_t *active); - -#endif // __HID_MOUSE_REPORT_H__ diff --git a/right/src/hid/report_ids.h b/right/src/hid/report_ids.h index b74db9998..938992f21 100644 --- a/right/src/hid/report_ids.h +++ b/right/src/hid/report_ids.h @@ -1,5 +1,4 @@ -#ifndef __REPORT_IDS__ -#define __REPORT_IDS__ +#pragma once #include "device.h" @@ -9,8 +8,10 @@ enum report_ids { IN_KEYBOARD_NKRO = 0, OUT_KEYBOARD_LEDS = 0, - IN_MOUSE = 3, - FEATURE_MOUSE = 3, // mouse needs nonzero report ID as workaround for Linux high-res scrolling bug + IN_MOUSE = 1, + FEATURE_MOUSE = + 1, // mouse needs nonzero report ID as workaround for Linux high-res scrolling bug + // https://bugzilla.kernel.org/show_bug.cgi?id=220144 IN_CONTROLS = 0, @@ -39,5 +40,3 @@ enum report_ids { IN_GAMEPAD = 6, #endif }; - -#endif // __REPORT_IDS__ \ No newline at end of file diff --git a/right/src/hid/transport.cpp b/right/src/hid/transport.cpp index 202493c9e..e2f0b0b2b 100644 --- a/right/src/hid/transport.cpp +++ b/right/src/hid/transport.cpp @@ -10,36 +10,42 @@ extern "C" { #endif #include "debug.h" #include "event_scheduler.h" +#include "jitter_test.h" #include "key_states.h" +#include "led_display.h" #include "logger.h" #include "macro_events.h" +#include "test_suite/test_hooks.h" #include "timer.h" #include "trace.h" #include "usb_report_updater.h" -#include "usb_semaphore.h" #include "usb_scheduler.h" -#include "led_display.h" -#include "jitter_test.h" +#include "usb_semaphore.h" #include "usb_state.h" #include "utils.h" -#include "test_suite/test_hooks.h" } #include "command_app.hpp" #include "controls_app.hpp" -// #include "gamepad_app.hpp" #include "keyboard_app.hpp" #include "mouse_app.hpp" +#if DEVICE_IS_UHK80_RIGHT + #include "ble_app.hpp" +#endif #if defined(__ZEPHYR__) && (defined(CONFIG_DEBUG) == defined(NDEBUG)) #error "Either CONFIG_DEBUG or NDEBUG must be defined" #endif #ifdef __ZEPHYR__ -#include + #include LOG_MODULE_REGISTER(Transport, LOG_LEVEL_INF); #endif // On mcux, logger.h provides the LOG_WRN / LOG_ERR / ... redirects. +static key_report_buffer keyboard_buffer; +static double_buffer> mouse_buffer; +static double_buffer> controls_buffer; + // Exponential moving average (alpha=1/8) of the measured delay between a // BLE HID report being handed to the stack and the corresponding sent callback // firing. Populated when DEBUG_BLE_LATENCY_STATS is enabled (in usb_report_sender.c); @@ -55,6 +61,7 @@ extern "C" void HidTransport_NoteNusReportSent(void) UsbScheduler_ReportDelivered(ReportSink_Dongle); } +// TODO: maybe not only return the sink type, but also the session / conn pointer static report_sink_t determineSink() { if (TestHooks_Active) { @@ -93,55 +100,68 @@ static report_sink_t determineSink() #endif } -#ifdef __ZEPHYR__ -static inline connection_id_t hidConnId(hid_transport_t transport) +#if DEVICE_IS_UHK80_RIGHT +// Resolve the BLE HID session bound to the current host connection, if any. +static ble_session *currentHostBleSession() { - if (transport == HID_TRANSPORT_USB) { - if (DEVICE_IS_UHK80_LEFT) { - return ConnectionId_UsbHidLeft; - } - return ConnectionId_UsbHidRight; + connection_id_t connId = ActiveHostConnectionId; + if (connId <= ConnectionId_Invalid || connId >= ConnectionId_Count) { + return nullptr; } - return ConnectionId_BtHid; + uint8_t peerId = Connections[connId].peerId; + if (peerId < PeerIdFirstHost || peerId > PeerIdLastHost) { + return nullptr; + } + struct bt_conn *conn = Peers[peerId].conn; + return conn ? ble_session::lookup_by_conn(conn) : nullptr; } #endif -extern "C" void Hid_TransportStateChanged( - [[maybe_unused]] hid_transport_t transport, [[maybe_unused]] bool enabled) -{ -#ifdef __ZEPHYR__ - connection_id_t connId = hidConnId(transport); - if (connId == ConnectionId_UsbHidRight) { - UsbState_SetUsbTransportUp(enabled); - } else { - Connections_SetStateAsync( hidConnId(transport), enabled ? ConnectionState_Ready : ConnectionState_Disconnected); - } -#else - UsbState_SetUsbTransportUp(enabled); -#endif -} +// TODO: when switching sinks, or when USB only and transport comes up +// keyboard_buffer.reset_to(session->protocol(), (session->channel() == hid::channel::BLE) ? HID_ROLLOVER_N_KEY : HID_GetKeyboardRollover()); +// Deferred: this hooks into the switchover path, which is being reworked in a separate PR - do it there. extern "C" errno_t Hid_SendKeyboardReport(const hid_keyboard_report_t *report) { + // TODO: not needed when dongle is sink + auto payload = keyboard_buffer.insert(*report); + report_sink_t sink = determineSink(); UsbScheduler_ReportAcceptedByTransport(sink); Trace_Printf("z11,%d", sink); - errno_t err; + errno_t err = -ECONNRESET; if (UnreliableTransportTestMode && Utils_Random() % 7 == 0) { return -EAGAIN; } switch (sink) { case ReportSink_Usb: - err = keyboard_app::usb_handle().send_report(*report); + if (auto session = keyboard_app::usb_handle().session(); session) { + err = session->send_report(payload).to_int(); + } break; #if DEVICE_IS_UHK80_RIGHT - case ReportSink_BleHid: - err = keyboard_app::ble_handle().send_report(*report); + case ReportSink_BleHid: { + hid::session *session = currentHostBleSession(); + + if (!session) { + break; + } + err = session->send_report(payload).to_int(); + if (err == -ENOMEM) { + // this only happens on Android with NKRO mode when the transport MTU is too small + printk("keyboard NKRO mode fails, falling back to 6KRO\n"); + + keyboard_buffer.reset_to(hid::protocol::REPORT, rollover_t::ROLLOVER_6_KEY); + payload = keyboard_buffer.insert(*report); + err = session->send_report(payload).to_int(); + } break; + } #endif #if DEVICE_IS_UHK80 case ReportSink_Dongle: - err = Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_SyncableProperty, SyncablePropertyId_KeyboardReport, (const uint8_t *)report, sizeof(*report)); + err = Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_SyncableProperty, + SyncablePropertyId_KeyboardReport, (const uint8_t *)report, sizeof(*report)); if (err != 0) { printk("Failed to send keyboard report to dongle: %d\n", err); } else { @@ -152,7 +172,7 @@ extern "C" errno_t Hid_SendKeyboardReport(const hid_keyboard_report_t *report) case ReportSink_TestSuite: err = 0; TestHooks_CaptureReport(report); - Hid_KeyboardReportSentCallback(HID_TRANSPORT_USB); + Hid_KeyboardReportSentCallback(ReportSink_Usb); break; default: #ifdef __ZEPHYR__ @@ -162,39 +182,61 @@ extern "C" errno_t Hid_SendKeyboardReport(const hid_keyboard_report_t *report) break; } Trace_Printf("z12,%d", err); + if (err == 0) { + // not needed when dongle is sink + keyboard_buffer.swap_sides(); + } return err; } -extern "C" void Hid_KeyboardReportSentCallback(hid_transport_t transport) +extern "C" void Hid_KeyboardReportSentCallback(report_sink_t transport) { if (UnreliableTransportTestMode && Utils_Random() % 7 == 0) { return; } UsbSemaphore_Release(&UsbSemaphore.keyboard); - UsbScheduler_ReportDelivered(transport == HID_TRANSPORT_USB ? ReportSink_Usb : ReportSink_BleHid); + UsbScheduler_ReportDelivered(transport); #if DEVICE_IS_UHK_DONGLE Dongle_SignalUsbReportSent(); #endif } +void keyboard_report_sent_callback(hid::session &session) +{ + Hid_KeyboardReportSentCallback( + session.channel() == hid::channel::USB ? ReportSink_Usb : ReportSink_BleHid); +} + extern "C" errno_t Hid_SendMouseReport(const hid_mouse_report_t *report) { + // TODO: not needed when dongle is sink + auto payload = mouse_buffer.insert(*report); + report_sink_t sink = determineSink(); UsbScheduler_ReportAcceptedByTransport(sink); Trace_Printf("z21,%d", sink); - errno_t err; + errno_t err = -ECONNRESET; switch (sink) { case ReportSink_Usb: - err = mouse_app::usb_handle().send_report(*report); + if (auto session = mouse_app::usb_handle().session(); session) { + err = session->send_report(payload).to_int(); + } break; #if DEVICE_IS_UHK80_RIGHT - case ReportSink_BleHid: - err = mouse_app::ble_handle().send_report(*report); + case ReportSink_BleHid: { + hid::session *session = currentHostBleSession(); + + if (!session) { + break; + } + err = session->send_report(payload).to_int(); break; + } #endif #if DEVICE_IS_UHK80 case ReportSink_Dongle: - err = Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_SyncableProperty, SyncablePropertyId_MouseReport, (const uint8_t *)report, sizeof(*report)); + err = Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_SyncableProperty, + SyncablePropertyId_MouseReport, (const uint8_t *)report, sizeof(*report)); if (err != 0) { printk("Failed to send mouse report to dongle: %d\n", err); } else { @@ -212,14 +254,17 @@ extern "C" errno_t Hid_SendMouseReport(const hid_mouse_report_t *report) Trace_Printf("z22,%d", err); if (err == 0) { JitterTest_RecordMouseX(report->x); + // not needed when dongle is sink + mouse_buffer.swap_sides(); } return err; } -extern "C" void Hid_MouseReportSentCallback(hid_transport_t transport) +void mouse_report_sent_callback(hid::session &session) { UsbSemaphore_Release(&UsbSemaphore.mouse); - UsbScheduler_ReportDelivered( transport == HID_TRANSPORT_USB ? ReportSink_Usb : ReportSink_BleHid); + UsbScheduler_ReportDelivered( + session.channel() == hid::channel::USB ? ReportSink_Usb : ReportSink_BleHid); #if DEVICE_IS_UHK_DONGLE Dongle_SignalUsbReportSent(); #endif @@ -227,22 +272,34 @@ extern "C" void Hid_MouseReportSentCallback(hid_transport_t transport) extern "C" errno_t Hid_SendControlsReport(const hid_controls_report_t *report) { + // TODO: not needed when dongle is sink + auto payload = controls_buffer.insert(*report); + report_sink_t sink = determineSink(); UsbScheduler_ReportAcceptedByTransport(sink); Trace_Printf("z31,%d", sink); - errno_t err; + errno_t err = -ECONNRESET; switch (sink) { case ReportSink_Usb: - err = controls_app::usb_handle().send_report(*report); + if (auto session = controls_app::usb_handle().session(); session) { + err = session->send_report(payload).to_int(); + } break; #if DEVICE_IS_UHK80_RIGHT - case ReportSink_BleHid: - err = controls_app::ble_handle().send_report(*report); + case ReportSink_BleHid: { + hid::session *session = currentHostBleSession(); + + if (!session) { + break; + } + err = session->send_report(payload).to_int(); break; + } #endif #if DEVICE_IS_UHK80 case ReportSink_Dongle: - err = Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_SyncableProperty, SyncablePropertyId_ControlsReport, (const uint8_t *)report, sizeof(*report)); + err = Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_SyncableProperty, + SyncablePropertyId_ControlsReport, (const uint8_t *)report, sizeof(*report)); if (err != 0) { printk("Failed to send controls report to dongle: %d\n", err); } else { @@ -258,13 +315,18 @@ extern "C" errno_t Hid_SendControlsReport(const hid_controls_report_t *report) break; } Trace_Printf("z32,%d", err); + if (err == 0) { + // not needed when dongle is sink + controls_buffer.swap_sides(); + } return err; } -extern "C" void Hid_ControlsReportSentCallback(hid_transport_t transport) +void controls_report_sent_callback(hid::session &session) { UsbSemaphore_Release(&UsbSemaphore.controls); - UsbScheduler_ReportDelivered( transport == HID_TRANSPORT_USB ? ReportSink_Usb : ReportSink_BleHid); + UsbScheduler_ReportDelivered( + session.channel() == hid::channel::USB ? ReportSink_Usb : ReportSink_BleHid); #if DEVICE_IS_UHK_DONGLE Dongle_SignalUsbReportSent(); #endif @@ -308,15 +370,16 @@ static void setKeyboardLedsState(hid::app::keyboard::output_report<0> report) extern "C" void Hid_UpdateKeyboardLedsState() { + keyboard_base_session *session = nullptr; #ifdef __ZEPHYR__ switch (Connections_Type(ActiveHostConnectionId)) { case ConnectionType_UsbHidRight: case ConnectionType_UsbHidLeft: - setKeyboardLedsState(keyboard_app::usb_handle().get_leds()); + session = keyboard_app::usb_handle().session(); break; #if DEVICE_IS_UHK80_RIGHT case ConnectionType_BtHid: - setKeyboardLedsState(keyboard_app::ble_handle().get_leds()); + session = currentHostBleSession(); break; #endif case ConnectionType_NusDongle: @@ -327,30 +390,40 @@ extern "C" void Hid_UpdateKeyboardLedsState() break; } #else - setKeyboardLedsState(keyboard_app::usb_handle().get_leds()); + session = keyboard_app::usb_handle().session(); #endif + if (session) { + setKeyboardLedsState(session->get_leds_report()); + } } -extern "C" void Hid_KeyboardLedsStateChanged(hid_transport_t transport) +void keyboard_leds_changed_callback(keyboard_base_session &session) { #if DEVICE_IS_UHK80_RIGHT - auto value = (transport == HID_TRANSPORT_USB) ? keyboard_app::usb_handle().get_leds() - : keyboard_app::ble_handle().get_leds(); - connection_id_t connectionId = hidConnId(transport); + connection_id_t connectionId; + if (session.channel() == hid::channel::USB) { + connectionId = ConnectionId_UsbHidRight; + } else { + struct bt_conn *conn = static_cast(session).get_conn(); + int8_t peerId = conn ? GetPeerIdByConn(conn) : PeerIdUnknown; + connectionId = (peerId >= PeerIdFirstHost && peerId <= PeerIdLastHost) + ? (connection_id_t)Peers[peerId].connectionId + : ConnectionId_Invalid; + } if (Connections_IsActiveHostConnection(connectionId)) { - setKeyboardLedsState(value); + setKeyboardLedsState(session.get_leds_report()); } #else - setKeyboardLedsState(keyboard_app::usb_handle().get_leds()); + setKeyboardLedsState(session.get_leds_report()); #endif } -extern "C" void Hid_MouseScrollResolutionsChanged( - hid_transport_t transport, float verticalMultiplier, float horizontalMultiplier) +void mouse_resolution_changed_callback( + hid::session &session, const mouse_session::scroll_resolution_report &report) { #if DEVICE_IS_UHK_DONGLE - DongleScrollMultipliers.vertical = verticalMultiplier; - DongleScrollMultipliers.horizontal = horizontalMultiplier; + DongleScrollMultipliers.vertical = report.vertical_scroll_multiplier(); + DongleScrollMultipliers.horizontal = report.horizontal_scroll_multiplier(); StateSync_UpdateProperty(StateSyncPropertyId_DongleScrollMultipliers, NULL); #endif } @@ -361,7 +434,10 @@ extern "C" float VerticalScrollMultiplier(void) switch (Connections_Type(ActiveHostConnectionId)) { #if DEVICE_IS_UHK80_RIGHT case ConnectionType_BtHid: - return mouse_app::ble_handle().resolution_report().vertical_scroll_multiplier(); + if (auto *session = currentHostBleSession(); session) { + return session->resolution_report().vertical_scroll_multiplier(); + } + return 1.f; #endif case ConnectionType_NusDongle: return DongleScrollMultipliers.vertical; @@ -375,7 +451,10 @@ extern "C" float VerticalScrollMultiplier(void) if (USB_IsMsHost()) { return mouse_app::MAX_SCROLL_RESOLUTION; } - return mouse_app::usb_handle().resolution_report().vertical_scroll_multiplier(); + if (auto session = mouse_app::usb_handle().session(); session) { + return session->resolution_report().vertical_scroll_multiplier(); + } + return 1.f; } extern "C" float HorizontalScrollMultiplier(void) @@ -384,7 +463,10 @@ extern "C" float HorizontalScrollMultiplier(void) switch (Connections_Type(ActiveHostConnectionId)) { #if DEVICE_IS_UHK80_RIGHT case ConnectionType_BtHid: - return mouse_app::ble_handle().resolution_report().horizontal_scroll_multiplier(); + if (auto *session = currentHostBleSession(); session) { + return session->resolution_report().horizontal_scroll_multiplier(); + } + return 1.f; #endif case ConnectionType_NusDongle: return DongleScrollMultipliers.horizontal; @@ -398,34 +480,24 @@ extern "C" float HorizontalScrollMultiplier(void) if (USB_IsMsHost()) { return mouse_app::MAX_SCROLL_RESOLUTION; } - return mouse_app::usb_handle().resolution_report().horizontal_scroll_multiplier(); -} - -static bool gamepadActive = false; - -extern "C" bool HID_GetGamepadActive() -{ - return gamepadActive; + if (auto session = mouse_app::usb_handle().session(); session) { + return session->resolution_report().horizontal_scroll_multiplier(); + } + return 1.f; } extern "C" void USB_Reconfigure(void); -extern "C" void HID_SetGamepadActive(bool active) -{ - gamepadActive = active; - USB_Reconfigure(); -} +static rollover_t keyboardRollover = rollover_t::ROLLOVER_N_KEY; extern "C" rollover_t HID_GetKeyboardRollover() { - return keyboard_app::usb_handle().get_rollover(); + return keyboardRollover; } extern "C" void HID_SetKeyboardRollover(rollover_t mode) { + keyboardRollover = mode; keyboard_app::usb_handle().set_rollover(mode); -#if DEVICE_IS_UHK80_RIGHT - keyboard_app::ble_handle().set_rollover(mode); -#endif USB_Reconfigure(); } diff --git a/right/src/hid/transport.h b/right/src/hid/transport.h index e80e403be..f4ed70e9b 100644 --- a/right/src/hid/transport.h +++ b/right/src/hid/transport.h @@ -1,5 +1,4 @@ -#ifndef __HID_TRANSPORT_H__ -#define __HID_TRANSPORT_H__ +#pragma once #include #include "hid/keyboard_report.h" @@ -13,11 +12,6 @@ static inline const char* ErrToStr(errno_t err) { return strerror(-err); } -typedef enum { - HID_TRANSPORT_USB, - HID_TRANSPORT_BLE, -} hid_transport_t; - // Which physical sink a report is dispatched to. Determined by transport.c's // determineSink(); shared so the report-sender module can size transport windows per sink. typedef enum { @@ -34,37 +28,26 @@ typedef enum ROLLOVER_6_KEY = 1, } rollover_t; - extern float HidReportBleLatencyAvgMs; extern bool UnreliableTransportTestMode; -void Hid_TransportStateChanged(hid_transport_t transport, bool enabled); - // report sending errno_t Hid_SendKeyboardReport(const hid_keyboard_report_t* report); errno_t Hid_SendMouseReport(const hid_mouse_report_t* report); errno_t Hid_SendControlsReport(const hid_controls_report_t* report); -void Hid_KeyboardReportSentCallback(hid_transport_t transport); -void Hid_MouseReportSentCallback(hid_transport_t transport); -void Hid_ControlsReportSentCallback(hid_transport_t transport); +void Hid_KeyboardReportSentCallback(report_sink_t transport); // Called from NUS server 'sent' callback on UHK80 right half to feed // dongle-bound reports into the same latency EMA as BLE HID. Assumes any // right-half NUS send corresponds to a USB report being relayed to the dongle. void HidTransport_NoteNusReportSent(void); -void Hid_MouseScrollResolutionsChanged( - hid_transport_t transport, float verticalMultiplier, float horizontalMultiplier); - // num lock, caps lock, scroll lock state handling void Hid_UpdateKeyboardLedsState(void); -void Hid_KeyboardLedsStateChanged(hid_transport_t transport); rollover_t HID_GetKeyboardRollover(void); void HID_SetKeyboardRollover(rollover_t mode); -bool HID_GetGamepadActive(void); -void HID_SetGamepadActive(bool active); // USB management void USB_SetSerialNumber(uint32_t serialNumber); @@ -73,11 +56,8 @@ bool USB_RemoteWakeup(void); void USB_Reconfigure(void); bool USB_IsMsHost(void); - - // HOGP (BLE HID) management -bool HOGP_Enable(void); -void HOGP_Disable(void); +// Instantiates the HOGP GATT service; must be called before bt_enable() so the +// static service entry is populated when Zephyr registers static services. +void HOGP_Register(void); int HOGP_HealthCheck(void); - -#endif // __HID_TRANSPORT_H__ diff --git a/right/src/hid/transport_ble.cpp b/right/src/hid/transport_ble.cpp index d129d20ae..df5201230 100644 --- a/right/src/hid/transport_ble.cpp +++ b/right/src/hid/transport_ble.cpp @@ -1,92 +1,139 @@ -#include "command_app.hpp" -#include "controls_app.hpp" -#include "keyboard_app.hpp" -#include "mouse_app.hpp" -#include "port/zephyr/bluetooth/hid.hpp" -#include +#include "ble_app.hpp" +#include +#include #include #include +extern "C" { +#include "bt_conn.h" +} using namespace magic_enum::bitwise_operators; -template -class multi_hid : public hid::multi_application { - public: - static constexpr auto report_desc() { return hid::rdf::descriptor((Args::report_desc(), ...)); } - - static multi_hid &handle() - { - static multi_hid s; - return s; - } - - private: - std::array app_array_{ - (&Args::ble_handle())..., nullptr}; - constexpr multi_hid() - : multi_application(hid::report_protocol::from_descriptor(), app_array_) - {} -}; - -using multi_hid_nopad = multi_hid; - -struct hogp_manager { - static hogp_manager &instance() - { - static hogp_manager hm; - return hm; - } +static_assert(sizeof(ble_session) <= BLE_HID_SESSION_STORAGE_SIZE, + "BLE_HID_SESSION_STORAGE_SIZE too small for ble_session"); +static_assert(alignof(ble_session) <= 8, "ble_session alignment exceeds peer storage alignment"); - static bool active() { return instance().hogp_nopad_.active(); } +static ble_session *peerSession(peer_t *peer) +{ + return std::launder(reinterpret_cast(peer->hidSessionStorage)); +} - bluetooth::zephyr::hid::service &main_service() { return hogp_nopad_; } +static auto &hog_service() +{ + using namespace magic_enum::bitwise_operators; + using namespace bluetooth::hid_over_gatt; - private: - hogp_manager() {} + // TODO: set values as needed + const auto sec_lvl = bluetooth::security::L4_LE_SC; + static constexpr auto features = flags::NORMALLY_CONNECTABLE | flags::REMOTE_WAKE; - static const auto security = bluetooth::zephyr::hid::security::ENCRYPT; - static const auto features = bluetooth::zephyr::hid::flags::NORMALLY_CONNECTABLE | - bluetooth::zephyr::hid::flags::REMOTE_WAKE; + using hogp_type = service_instance; + static hogp_type hog{ble_app::handle(), sec_lvl, features}; - bluetooth::zephyr::hid::service_instance - hogp_nopad_{multi_hid_nopad::handle(), security, features}; -}; + static const STRUCT_SECTION_ITERABLE(bt_gatt_service_static, keyboard_hogp) = + hog.static_service(); + return hog; +} -extern "C" bool HOGP_Enable() +ble_session *ble_session::lookup_by_conn(::bt_conn *conn) { - bool started = hogp_manager::instance().main_service().start(); - return started; + return static_cast(hog_service().get_session(conn)); } -extern "C" void HOGP_Disable() +::bt_conn *ble_session::get_conn() { - hogp_manager::instance().main_service().stop(); + return hog_service().get_session_conn(*this); } -extern "C" int HOGP_HealthCheck() +hid::session &ble_app::start(const hid::session::params ¶ms) { - auto &hm = hogp_manager::instance(); - auto &svc = hm.main_service(); + ::bt_conn *conn = static_cast(params).conn; + int8_t peerId = GetPeerIdByConn(conn); + if (peerId < PeerIdFirstHost || peerId > PeerIdLastHost) { + // A HOGP session should only ever start for a connected host peer + printk("ble_app::start: no host peer for conn (peerId %d)\n", peerId); + assert(false && "ble_app::start: no host peer for conn"); + static ble_session fallback{params}; + return fallback; + } + peer_t *peer = &Peers[peerId]; - if (!hm.active()) { - printk("HOGP HealthCheck: GATT service NOT registered\n"); - return -1; + // destroy any stale session left in this slot + if (peer->hidSessionActive) { + peerSession(peer)->~ble_session(); + peer->hidSessionActive = false; } - struct bt_conn *peer = svc.peer(); - if (!peer) { - printk("HOGP HealthCheck: service registered, no active peer\n"); - return -2; + ble_session *sess = new (peer->hidSessionStorage) ble_session(params); + peer->hidSessionActive = true; + + // Mirror mouse_app::start: initialise the scroll multiplier from the new session. + mouse_resolution_changed_callback(*sess, sess->resolution_report()); + + if (peer->connectionId != ConnectionId_Invalid) { + printk("ble_app::start: marking BtHid connection %d (peer %d) ready\n", peer->connectionId, + peerId); + Connections_SetStateAsync((connection_id_t)peer->connectionId, ConnectionState_Ready); + } else { + printk("ble_app::start: peer %d has invalid connectionId, cannot mark ready\n", peerId); + } + return *sess; +} + +void ble_app::stop(hid::session &sess) +{ + for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { + peer_t *peer = &Peers[peerId]; + if (!peer->hidSessionActive) { + continue; + } + ble_session *ps = peerSession(peer); + if (static_cast(ps) == &sess) { + // On a real disconnect bt_conn.c may already have cleared connectionId. + if (peer->connectionId != ConnectionId_Invalid) { + printk("ble_app::stop: marking BtHid connection %d (peer %d) disconnected\n", + peer->connectionId, peerId); + Connections_SetStateAsync( + (connection_id_t)peer->connectionId, ConnectionState_Disconnected); + } else { + printk("ble_app::stop: peer %d has invalid connectionId\n", peerId); + } + ps->~ble_session(); + peer->hidSessionActive = false; + return; + } } +} - struct bt_conn_info info; - int err = bt_conn_get_info(peer, &info); - if (err) { - printk("HOGP HealthCheck: active peer has INVALID conn pointer (err %d)\n", err); - return -3; +extern "C" void HOGP_Register() +{ + // Forces the function-local static HOGP service (and its bt_gatt_service_static + // entry) to be constructed so bt_enable() can register it. + (void)hog_service(); +} + +extern "C" int HOGP_HealthCheck() +{ + int activeSessions = 0; + for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { + peer_t *peer = &Peers[peerId]; + if (!peer->hidSessionActive) { + continue; + } + activeSessions++; + if (peer->conn == nullptr) { + printk("HOGP HealthCheck: peer %s has an active session but no conn\n", peer->name); + return -2; + } + struct bt_conn_info info; + int err = bt_conn_get_info(peer->conn, &info); + if (err) { + printk( + "HOGP HealthCheck: peer %s has INVALID conn pointer (err %d)\n", peer->name, err); + return -3; + } } - printk("HOGP HealthCheck: OK (registered, peer connected, interval %u)\n", info.le.interval_us); + printk("HOGP HealthCheck: OK (%d active HID session(s))\n", activeSessions); return 0; } diff --git a/right/src/hid/transport_usb.cpp b/right/src/hid/transport_usb.cpp index ae505c30b..be0869007 100644 --- a/right/src/hid/transport_usb.cpp +++ b/right/src/hid/transport_usb.cpp @@ -3,12 +3,11 @@ extern "C" { #include "key_states.h" #include "logger.h" #include "power_mode.h" -#include "usb_state.h" #include "timer.h" #include "usb_report_updater.h" #include "usb_semaphore.h" +#include "usb_state.h" #include "user_logic.h" -#include "logger.h" #ifdef __ZEPHYR__ #include "device_state.h" #include @@ -25,7 +24,6 @@ extern "C" { #endif #include "command_app.hpp" #include "controls_app.hpp" -// #include "gamepad_app.hpp" #include "keyboard_app.hpp" #include "mouse_app.hpp" #include "usb/df/class/hid.hpp" @@ -55,16 +53,17 @@ struct usb_manager { return um; } - void select_config([[maybe_unused]] bool gamepad_active) + void select_config() { // pretend that the device is disconnected if (device().is_open()) { + const unsigned bus_reset_delay_ms = 100; device().close(); #ifdef __ZEPHYR__ - k_msleep(100); + k_msleep(bus_reset_delay_ms); #else // TODO: use non-blocking delay - SDK_DelayAtLeastUs(100000, SystemCoreClock); + SDK_DelayAtLeastUs(bus_reset_delay_ms * 1000, SystemCoreClock); #endif } @@ -80,30 +79,13 @@ struct usb_manager { const auto shared_config_elems = usb::df::config::join_elements( usb::df::hid::config(usb_kb, speed, usb::endpoint::address(0x81), 1), usb::df::hid::config(usb_mouse, speed, usb::endpoint::address(0x82), 1), - usb::df::hid::config(usb_command, speed, usb::endpoint::address(0x83), 10), + usb::df::hid::config(usb_command, speed, usb::endpoint::address(0x83), 8), usb::df::hid::config(usb_controls, speed, usb::endpoint::address(0x84), 1)); static const auto base_config = usb::df::config::make_config(config_header, shared_config_elems); -#if 0 // gamepad support disabled - static const auto gamepad_config = - usb::df::config::make_config(config_header, shared_config_elems, - usb::df::hid::config(usb_gamepad, speed, usb::endpoint::address(0x85), 1)); - - static const auto xpad_config = - usb::df::config::make_config(config_header, shared_config_elems, - usb::df::microsoft::xconfig( - usb_xpad, usb::endpoint::address(0x85), 1, usb::endpoint::address(0x05), 255)); - - if (conf != Hid_NoGamepad) { - ms_enum_.set_config(xpad_config); - device_.set_config(gamepad_config); - } else -#endif - { - ms_enum_.set_config({}); - device_.set_config(base_config); - } + + device_.set_config(base_config); device_.open(); } @@ -114,7 +96,6 @@ struct usb_manager { if ((ev & event::POWER_STATE_CHANGE) != event::NONE) { switch (dev.power_state()) { case usb::power::state::L2_SUSPEND: - // TODO: use a common API instead of device specific #if DEVICE_IS_UHK60 if (dev.configured()) { UsbState_SetUsbAwake(false); @@ -167,13 +148,13 @@ extern "C" void USB0_IRQHandler(void) extern "C" void USB_Enable() { assert(!usb_manager::active()); - usb_manager::instance().select_config(HID_GetGamepadActive()); + usb_manager::instance().select_config(); } extern "C" void USB_Reconfigure() { if (usb_manager::active()) { - usb_manager::instance().select_config(HID_GetGamepadActive()); + usb_manager::instance().select_config(); } } @@ -181,7 +162,7 @@ extern "C" bool USB_RemoteWakeup() { auto err = usb_manager::instance().device().remote_wakeup(); if (err != usb::result::ok) { - LogErr("USB: remote wakeup request failed: %d\n", std::bit_cast(err)); + LogErr("USB: remote wakeup request failed: %d\n", err.to_int()); } else { LogInf("USB: remote wakeup request succeeded\n"); } diff --git a/submanifests/c2usb.yml b/submanifests/c2usb.yml index a6817addd..34baae14a 100644 --- a/submanifests/c2usb.yml +++ b/submanifests/c2usb.yml @@ -5,5 +5,5 @@ manifest: projects: - name: c2usb remote: IntergatedCircuits - revision: c263b00c48047d3845fd76f5938e0644d905de07 + revision: 5e2faaec10394324db7c1781761d6f07500e9b02 import: true diff --git a/west_mcuxsdk.yml b/west_mcuxsdk.yml index 208c641b7..9adaede95 100644 --- a/west_mcuxsdk.yml +++ b/west_mcuxsdk.yml @@ -14,7 +14,7 @@ manifest: - name: mcuxsdk-manifests remote: nxp-mcuxpresso path: mcuxsdk/manifest_int - revision: v26.03.00 + revision: v26.06.00-LTS clone-depth: 1 import: name-allowlist: