From d18eb6412b55e70d8de9975a58822866f4941eb5 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 13:44:17 +0100 Subject: [PATCH 01/46] Ensure to hide pairing screen if race conditions occur. --- device/src/bt_conn.c | 13 +++++++++++-- device/src/keyboard/oled/screens/pairing_screen.c | 6 ++++++ device/src/keyboard/oled/screens/pairing_screen.h | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 107fa8ceb..6eff675ac 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -21,6 +21,7 @@ #include "nus_server.h" #include "device.h" #include "keyboard/oled/screens/pairing_screen.h" +#include "keyboard/oled/screens/screen_manager.h" #include "usb/usb.h" #include "keyboard/oled/widgets/widgets.h" #include @@ -37,7 +38,6 @@ #include "bt_health.h" #include "macros/status_buffer.h" - LOG_MODULE_REGISTER(Bt, LOG_LEVEL_INF); bool Bt_NewPairedDevice = false; @@ -58,6 +58,7 @@ struct bt_conn *auth_conn; #define BT_REASON_UNSPECIFIED BT_HCI_ERR_UNSPECIFIED static void disconnectAllHids(); +static void auth_cancel(struct bt_conn *conn); peer_t Peers[PeerCount] = { { @@ -143,7 +144,13 @@ static struct bt_conn* unsetAuthConn(bool cancel_auth) { static void safeDisconnect(struct bt_conn *conn, int reason) { if (conn == auth_conn) { - unsetAuthConn(true); + unsetAuthConn(true); // called by auth_cancel + + #if DEVICE_HAS_OLED + if (ActiveScreen == ScreenId_Pairing) { + PairingScreen_Cancel(); + } + #endif } else { struct bt_conn_info info; int err = bt_conn_get_info(conn, &info); @@ -905,6 +912,8 @@ static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason) { PairingScreen_Feedback(false); } + safeDisconnect(conn, BT_REASON_PERMANENT); + LOG_WRN("Pairing failed: %s, reason %d\n", GetPeerStringByConn(conn), reason); } diff --git a/device/src/keyboard/oled/screens/pairing_screen.c b/device/src/keyboard/oled/screens/pairing_screen.c index fc2b54149..c5c637d41 100644 --- a/device/src/keyboard/oled/screens/pairing_screen.c +++ b/device/src/keyboard/oled/screens/pairing_screen.c @@ -65,6 +65,12 @@ void PairingScreen_Feedback(bool success) } } +void PairingScreen_Cancel(void) +{ + passwordCharCount = 0; + ScreenManager_SwitchScreenEvent(); +} + const rgb_t* PairingScreen_ActionColor(key_action_t* action) { static const rgb_t black = { 0, 0, 0 }; static const rgb_t green = { 0, 0xff, 0 }; diff --git a/device/src/keyboard/oled/screens/pairing_screen.h b/device/src/keyboard/oled/screens/pairing_screen.h index d8643a4c0..f3c84deda 100644 --- a/device/src/keyboard/oled/screens/pairing_screen.h +++ b/device/src/keyboard/oled/screens/pairing_screen.h @@ -24,6 +24,7 @@ void PairingScreen_RegisterScancode(uint8_t scancode); void PairingScreen_AskForPassword(void); void PairingScreen_Feedback(bool success); + void PairingScreen_Cancel(void); const rgb_t* PairingScreen_ActionColor(key_action_t* action); #endif From 9a61dc3243032b12b22b7b5ec6bb7f407f5eb9e3 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 13:45:49 +0100 Subject: [PATCH 02/46] Log reboots better. --- right/src/usb_commands/usb_command_reenumerate.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/right/src/usb_commands/usb_command_reenumerate.c b/right/src/usb_commands/usb_command_reenumerate.c index 20ad7d483..10df69289 100644 --- a/right/src/usb_commands/usb_command_reenumerate.c +++ b/right/src/usb_commands/usb_command_reenumerate.c @@ -25,6 +25,8 @@ void Reboot(bool rebootPeripherals) { StateWormhole.wasReboot = true; Trace_Printc("Rebooting..."); #ifdef __ZEPHYR__ + printk("Rebooting..."); + k_sleep (K_MSEC(100)); //let it flush logs if (rebootPeripherals) { if (DEVICE_IS_UHK80_RIGHT) { Messenger_Send2(DeviceId_Uhk80_Left, MessageId_Command, MessengerCommand_Reboot, NULL, 0); @@ -55,8 +57,10 @@ void UsbCommand_Reenumerate(const uint8_t *GenericHidOutBuffer, uint8_t *Generic { StateWormhole_Open(); StateWormhole.wasReboot = true; - Trace_Printc("Rebooting..."); + Trace_Printc("Reenumerating..."); #ifdef __ZEPHYR__ + printk("Reenumerating..."); + k_sleep (K_MSEC(100)); //let it flush logs bootmode_set(BOOT_MODE_TYPE_BOOTLOADER); sys_reboot(SYS_REBOOT_COLD); #else From 1714486c9c536bc93be99236dade7eb000ea5cd5 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 13:49:51 +0100 Subject: [PATCH 03/46] Respond immediately in pairing modes. --- device/src/bt_manager.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/device/src/bt_manager.c b/device/src/bt_manager.c index 6231997bc..3333f939e 100644 --- a/device/src/bt_manager.c +++ b/device/src/bt_manager.c @@ -92,14 +92,19 @@ void BtManager_StartScanningAndAdvertisingAsync(bool wasAggresive, const char* e BT_TRACE_AND_ASSERT("bm4"); uint32_t maxDelay = 5000; uint32_t minDelay = 100; + uint32_t expDelay; static int8_t aggressiveTries = 0; - aggressiveTries = wasAggresive ? aggressiveTries + 1 : 0; - aggressiveTries = MAX(0, aggressiveTries); - aggressiveTries = MIN(aggressiveTries, 16); - uint32_t expDelay = MIN(maxDelay, minDelay << aggressiveTries); + if (BtPair_PairingMode == PairingMode_Oob || BtPair_PairingMode == PairingMode_PairHid) { + expDelay = minDelay; + } else { + aggressiveTries = wasAggresive ? aggressiveTries + 1 : 0; + aggressiveTries = MAX(0, aggressiveTries); + aggressiveTries = MIN(aggressiveTries, 16); + expDelay = MIN(maxDelay, minDelay << aggressiveTries); + } LOG_INF("btManager: BtManager_StartScanningAndAdvertisingAsync because %s, delay %d\n", eventLabel, expDelay); EventScheduler_Reschedule(Timer_GetCurrentTime() + expDelay, EventSchedulerEvent_BtStartScanningAndAdvertising, eventLabel); From 27f7eea791be66cfcb2d76c7ae797a8c27769ea4 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 10:32:54 +0100 Subject: [PATCH 04/46] DOCS: testing scenarios, notes --- doc-dev/other/testing-scenarios.md | 36 ++++++++++++++++++++++++++++++ doc-dev/other/zephyr_logging.md | 24 ++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 doc-dev/other/testing-scenarios.md create mode 100644 doc-dev/other/zephyr_logging.md diff --git a/doc-dev/other/testing-scenarios.md b/doc-dev/other/testing-scenarios.md new file mode 100644 index 000000000..b82aac755 --- /dev/null +++ b/doc-dev/other/testing-scenarios.md @@ -0,0 +1,36 @@ +Following is a list testing suggestions. Not all have to be tested each time. + +## Bluetooth: + +- Pair ble hid. Unpair. Pair again. Save it into connections. Make sure the Agent doesn't offer saving the same device multiple times. +- When two ble hid devices are paired, test that we can `switchHost` between them. +- When a device is paired, check that we can disconnect it and connect it again - i.e., that the bonding information didn't get corrupted. (Optionally, throw in uhk restart, bluetooth restart, test this with multiple devices.) +- Test that we can switch between two dongles and that their leds light up accordingly. +- Issue `switchHost device` against a device that is not present. `switchHost` to current usb. See that uhk now advertises hid (pairing icon) and that a ble hid host can connect. +- Test that we can add a paired device into the host connections. Try pair and add multiple devices within one Agent session. +- Unpair+pair dongle. (Optionally, let a bluetooth hid active while doing so.) +- Pair two ble hids using `bluetooth pair` while all devices are active. + +## Usb suspend: + +In all scenarios make sure that uhk works afterwards. + +Test with a pc and with a mac separately. + +- suspend and wake the pc when connected via usb, via pc's button(s). +- suspend and wake the pc when connected via usb, by tapping any key. +- suspend and wake the pc when connected via usb, by tapping the wake key. +- suspend and wake the pc when connected via ble, via pc's button(s). + +## Troubleshooting: + +- Use the `panic` macro to check that crash logs are generated as expected. (`set devMode true` is needed on uhk80) + +## Macros: + +These are obscure scenarios that are not worth to test often, but may be good to test from time to time. + +- test `oneShot` modifiers with multiple oneShot keys. When keystrokeDelay = 250ms and oneshotTimeout = 5000, you have to see modifiers release only after the oneShot key is pressed. +- test `overlayLayer` - test that overlaid keys are working as expected, are in the right places and that their neighbours didn't get affected. + +- diff --git a/doc-dev/other/zephyr_logging.md b/doc-dev/other/zephyr_logging.md new file mode 100644 index 000000000..c9ad46da9 --- /dev/null +++ b/doc-dev/other/zephyr_logging.md @@ -0,0 +1,24 @@ +# Crash logs - users + +## Uhk80 + +Crash logs have to be enabled via `set devMode true` in `$onInit` + +## Uhk60 + +Crash logs are enabled by default. However, `set devMode true` is suggested as it enables logging in cases of ESD events. + +(We don't log esd reboots because they are often and these crash logs are significantly more disturbing than a quick reboot.) + +# Developer notes + +In order to fit all our uart applications onto two controllers, we use the async uart driver. Unfortunatelly, the async serial backend is significantly less reliable than the default interrupt backend. Especially it is not available at all during early boot phases. When these log are needed, the interrupt driver has to be used: + +- Comment out pin wiring stuff in device/src/main.c. + +- Comment out this in uhk-80-right.conf: + +``` +# CONFIG_SHELL_BACKEND_SERIAL_API_ASYNC=y +``` + From 9b222a9ed74381839e2edd4697f03dfbb08f19ee Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 14:40:31 +0100 Subject: [PATCH 05/46] Prevent blue dongle on double switchHost. --- device/src/connections.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/device/src/connections.c b/device/src/connections.c index dd10e98b8..0ab7eba11 100644 --- a/device/src/connections.c +++ b/device/src/connections.c @@ -382,8 +382,8 @@ bool Connections_IsActiveHostConnection(connection_id_t connectionId) { return resolveAliases(connectionId) == ActiveHostConnectionId; } -static void setCurrentDongleToStandby() { - if (Connections_IsHostConnection(ActiveHostConnectionId) && HostConnection(ActiveHostConnectionId)->type == HostConnectionType_Dongle) { +static void setDongleToStandby(connection_id_t connectionId) { + if (Connections_IsHostConnection(connectionId) && HostConnection(connectionId)->type == HostConnectionType_Dongle) { bool standby = true; Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_StateSync, StateSyncPropertyId_DongleStandby, (const uint8_t*)&standby, 1); } @@ -428,7 +428,9 @@ void Connections_HandleSwitchover(connection_id_t connectionId, bool forceSwitch bool connectionIsSelected = SelectedHostConnectionId == connectionId; bool noHostIsConnected = ActiveHostConnectionId == ConnectionId_Invalid; if (hostConnection->switchover || noHostIsConnected || forceSwitch || connectionIsSelected) { - setCurrentDongleToStandby(); + if (connectionId != ActiveHostConnectionId) { + setDongleToStandby(ActiveHostConnectionId); + } switchOver(connectionId); reportConnectionState(connectionId, "Conn state"); } From 7531b74856881775613ee749a4f3b9780f94693e Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 15:48:52 +0100 Subject: [PATCH 06/46] Comment reason codes. --- device/src/bt_conn.c | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 6eff675ac..73a17f66e 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -47,13 +47,39 @@ struct bt_conn *auth_conn; #define BLE_KEY_LEN 16 #define BLE_ADDR_LEN 6 -// BT_HCI_ERR_REMOTE_LOW_RESOURCES - it is *their* resources that are low, so we probably shouldn't use this -// BT_HCI_ERR_LOCALHOST_TERM_CONN - this is our decision, bond should remain valid -// BT_HCI_ERR_REMOTE_USER_TERM_CONN - this is user's decision, bond should remain valid -// BT_HCI_ERR_AUTH_FAIL - permanent failure, bond should be removed - -#define BT_REASON_HID_GIVE_US_BREAK BT_HCI_ERR_LOCALHOST_TERM_CONN -#define BT_REASON_NOT_SELECTED BT_HCI_ERR_LOCALHOST_TERM_CONN +/* + * Reason codes are named from the perspective of the party that receives them. + * + * BT_HCI_ERR_REMOTE_LOW_RESOURCES + * - it is our resources that are low + * + * BT_HCI_ERR_LOCALHOST_TERM_CONN + * - this is their decision + * - we never send this. + * - don't trust llms on it. + * - if we actually use it, the connection will not actually disconnect + * + * BT_HCI_ERR_REMOTE_USER_TERM_CONN + * - this is probably the right thing to send + * - bond should remain valid + * + * BT_HCI_ERR_AUTH_FAIL + * - permanent failure + * - bond should be removed + * + * LLM says that according to the core spec, following disconnect reasons are valid: + * + * Code: 0x08 Zephyr Constant: BT_HCI_ERR_AUTH_FAIL Meaning: Authentication failure - signals permanent bond problem + * Code: 0x13 Zephyr Constant: BT_HCI_ERR_REMOTE_USER_TERM_CONN Meaning: Standard disconnect - "I'm disconnecting you" + * Code: 0x14 Zephyr Constant: BT_HCI_ERR_REMOTE_LOW_RESOURCES Meaning: Low resources - signals temporary resource issue + * Code: 0x15 Zephyr Constant: BT_HCI_ERR_REMOTE_POWER_OFF Meaning: Power off - device is shutting down + * Code: 0x1A Zephyr Constant: BT_HCI_ERR_UNSUPP_REMOTE_FEATURE Meaning: Unsupported feature + * Code: 0x29 Zephyr Constant: BT_HCI_ERR_PAIRING_NOT_SUPPORTED Meaning: Pairing not supported + * Code: 0x3B Zephyr Constant: BT_HCI_ERR_UNACCEPT_CONN_PARAM + * */ + +#define BT_REASON_HID_GIVE_US_BREAK BT_HCI_ERR_REMOTE_USER_TERM_CONN +#define BT_REASON_NOT_SELECTED BT_HCI_ERR_REMOTE_USER_TERM_CONN #define BT_REASON_PERMANENT BT_HCI_ERR_AUTH_FAIL #define BT_REASON_UNSPECIFIED BT_HCI_ERR_UNSPECIFIED @@ -404,6 +430,8 @@ static bool isWantedInHidPairingMode(struct bt_conn *conn, bool alreadyAuthentic if (!result) { LOG_INF(" Not wanted in HID pairing mode: isLeft: %d, isBonded: %d, alreadyAuth: %d", isLeftConnection, isBonded, alreadyAuthenticated); + } else { + LOG_INF(" Is wanted in HID pairing mode: isLeft: %d, isBonded: %d, alreadyAuth: %d", isLeftConnection, isBonded, alreadyAuthenticated); } return result; From 8265c5c1525c067141b336932aaffae780368d4c Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 16:28:45 +0100 Subject: [PATCH 07/46] Minor registered/unregistered fix. --- right/src/usb_commands/usb_command_get_new_pairings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/right/src/usb_commands/usb_command_get_new_pairings.c b/right/src/usb_commands/usb_command_get_new_pairings.c index eb3611def..c4773e03b 100644 --- a/right/src/usb_commands/usb_command_get_new_pairings.c +++ b/right/src/usb_commands/usb_command_get_new_pairings.c @@ -24,7 +24,7 @@ static void bt_foreach_bond_cb(const struct bt_bond_info *info, void *user_data) return; } - if (HostConnections_IsKnownBleAddress(&info->addr) == HostKnown_Registered) { + if (HostConnections_IsKnownBleAddress(&info->addr) != HostKnown_Unregistered) { return; } From fa62e955878e3452e9a9beaa18ed33093873f443 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 16:44:57 +0100 Subject: [PATCH 08/46] DOCS: Mention $macroArg limits. --- doc-dev/reference-manual.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc-dev/reference-manual.md b/doc-dev/reference-manual.md index 8c000a940..08d9c2c13 100644 --- a/doc-dev/reference-manual.md +++ b/doc-dev/reference-manual.md @@ -590,7 +590,7 @@ Internally, values are saved in one of the following types, and types are automa Key actions can be parametrized with macro arguments. These arguments can be expanded in two ways: -- `$macroArg.` - in which case they are parsed as a single value. This is the normal and safe variant that prevents context corruption. +- `$macroArg.` - in which case they are parsed as a single value - in the sense of valid macro variable expressions. This is the normal and safe variant that prevents context corruption. Abbreviations, shortcuts, and keys generally are not supported by this variant - use the `&` one. - `¯oArg.` - this variant substitutes the argument into current parser context. These allow substituing any string, including full commands or their parts. This is an experimental feature and might be unsafe in some contexts. Following limitations apply: - the argument bounds must correspond to token bounds in the fully expanded string - the argument cannot span multiple lines From 85dc494b9a926a534acfdb24391ec8516bc0c720 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 18:06:07 +0100 Subject: [PATCH 09/46] Update changelog --- CHANGELOG.md | 6 ++++++ scripts/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1020aa2a..f27fefd7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to the [UHK Versioning](VERSIONING.md) conventions. +## [16.1.1] - 2026-01-16 + +- Fix: fix bluetooth issues caused by returning bad disconnect reasons - failing oob pairing, connected but otherwise unhandled connections. +- Fix: when the same dongle is switched to multiple times, it now remains greed instead of turning blue. +- Fix: exempt pairing scenarios from exponential advertising delay backoff. + ## [16.1.0] - 2026-01-16 Device Protocol: 4.17.0 | Module Protocol: 4.3.0 | Dongle Protocol: 1.0.2 | User Config: 13.0.0 | Hardware Config: 1.0.0 | Smart Macros: 3.**12.0** diff --git a/scripts/package.json b/scripts/package.json index 3cd735c0c..16422c78f 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -18,7 +18,7 @@ "serialport": "^12.0.0", "shelljs": "^0.8.4" }, - "firmwareVersion": "16.1.0", + "firmwareVersion": "16.1.1", "deviceProtocolVersion": "4.17.0", "moduleProtocolVersion": "4.3.0", "dongleProtocolVersion": "1.0.2", From 65819ad7b02703cb04c7d029c8fb67427dcebab6 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 18:15:47 +0100 Subject: [PATCH 10/46] Revert pairing fail disconnect. --- device/src/bt_conn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 73a17f66e..a5e6a399c 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -940,6 +940,7 @@ static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason) { PairingScreen_Feedback(false); } + // should we here? safeDisconnect(conn, BT_REASON_PERMANENT); LOG_WRN("Pairing failed: %s, reason %d\n", GetPeerStringByConn(conn), reason); From 03c72e4de84f80fe96ed6fd1913e036968ddf6a8 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 19:15:55 +0100 Subject: [PATCH 11/46] Stash. --- device/prj.conf.overlays/nrf_shared.conf | 4 +++- device/src/bt_conn.c | 8 +++++--- device/src/bt_pair.c | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/device/prj.conf.overlays/nrf_shared.conf b/device/prj.conf.overlays/nrf_shared.conf index 6442b02f3..17906b140 100644 --- a/device/prj.conf.overlays/nrf_shared.conf +++ b/device/prj.conf.overlays/nrf_shared.conf @@ -21,7 +21,9 @@ CONFIG_BT=y # CONFIG_BT_DEBUG_LOG=y CONFIG_BT_SMP=y -CONFIG_BT_L2CAP_TX_BUF_COUNT=5 +CONFIG_BT_ATT_TX_COUNT=4 +CONFIG_BT_CONN_TX_MAX=4 +CONFIG_BT_L2CAP_TX_BUF_COUNT=8 CONFIG_BT_FILTER_ACCEPT_LIST=y # negotiate larger MTU for NUS diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index a5e6a399c..88b4df4c2 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -81,7 +81,7 @@ struct bt_conn *auth_conn; #define BT_REASON_HID_GIVE_US_BREAK BT_HCI_ERR_REMOTE_USER_TERM_CONN #define BT_REASON_NOT_SELECTED BT_HCI_ERR_REMOTE_USER_TERM_CONN #define BT_REASON_PERMANENT BT_HCI_ERR_AUTH_FAIL -#define BT_REASON_UNSPECIFIED BT_HCI_ERR_UNSPECIFIED +#define BT_REASON_UNSPECIFIED BT_HCI_ERR_REMOTE_USER_TERM_CONN static void disconnectAllHids(); static void auth_cancel(struct bt_conn *conn); @@ -170,6 +170,7 @@ static struct bt_conn* unsetAuthConn(bool cancel_auth) { static void safeDisconnect(struct bt_conn *conn, int reason) { if (conn == auth_conn) { + LOG_INF("Unauthenticating %s\n", GetPeerStringByConn(conn)); unsetAuthConn(true); // called by auth_cancel #if DEVICE_HAS_OLED @@ -181,6 +182,7 @@ static void safeDisconnect(struct bt_conn *conn, int reason) { struct bt_conn_info info; int err = bt_conn_get_info(conn, &info); if (err == 0 && info.state == BT_CONN_STATE_CONNECTED) { + LOG_INF("Disconnecting %s\n", GetPeerStringByConn(conn)); bt_conn_disconnect(conn, reason); } } @@ -284,7 +286,7 @@ static void configureLatency(struct bt_conn *conn, latency_mode_t latencyMode) { const struct bt_le_conn_param conn_params = BT_LE_CONN_PARAM_INIT( 6, 6, // keep it low, lowest allowed is 6 (7.5ms), lowest supported widely is 9 (11.25ms) 0, // keeping it higher allows power saving on peripheral when there's nothing to send (keep it under 30 though) - 100 // connection timeout (*10ms) + 30 // connection timeout (*10ms) ); setLatency(conn, &conn_params); } @@ -296,7 +298,7 @@ static void configureLatency(struct bt_conn *conn, latency_mode_t latencyMode) { const struct bt_le_conn_param conn_params = BT_LE_CONN_PARAM_INIT( 6, 9, // keep it low, lowest allowed is 6 (7.5ms), lowest supported widely is 9 (11.25ms) 0, // keeping it higher allows power saving on peripheral when there's nothing to send (keep it under 30 though) - 100 // connection timeout (*10ms) + 30 // connection timeout (*10ms) ); setLatency(conn, &conn_params); } diff --git a/device/src/bt_pair.c b/device/src/bt_pair.c index 003edfd0d..e26368331 100644 --- a/device/src/bt_pair.c +++ b/device/src/bt_pair.c @@ -242,6 +242,8 @@ void BtPair_Unpair(const bt_addr_le_t addr) { // Update settings Settings_Reload(); UsbCommand_UpdateNewPairingsFlag(); + + BT_TRACE_AND_ASSERT("bpr6"); } static void bt_foreach_bond_cb_delete_non_lr(const struct bt_bond_info *info, void *user_data) { From e8b478ab348880dff32d9fccc8c19e7e7f254c11 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 19:58:23 +0100 Subject: [PATCH 12/46] Deduplicate host connections. --- .../src/config_parser/parse_host_connection.c | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/right/src/config_parser/parse_host_connection.c b/right/src/config_parser/parse_host_connection.c index d82939d80..fb30597c9 100644 --- a/right/src/config_parser/parse_host_connection.c +++ b/right/src/config_parser/parse_host_connection.c @@ -7,6 +7,8 @@ #include "config_parser/basic_types.h" #include "host_connection.h" #include "parse_config.h" +#include "bt_conn.h" +#include static parser_error_t parseHostConnection(config_buffer_t* buffer, host_connection_t* hostConnection) { host_connection_type_t hostType = ReadUInt8(buffer); @@ -56,6 +58,44 @@ static parser_error_t parseHostConnection(config_buffer_t* buffer, host_connecti return ParserError_Success; } +static void deduplicateUnregisteredConnections(void) { +#ifdef __ZEPHYR__ + if (ParserRunDry) { + return; + } + + for (uint8_t i = 0; i < SERIALIZED_HOST_CONNECTION_COUNT_MAX; i++) { + host_connection_t* conn = &HostConnections[i]; + + if (conn->type != HostConnectionType_UnregisteredBtHid) { + continue; + } + + for (uint8_t j = 0; j < SERIALIZED_HOST_CONNECTION_COUNT_MAX; j++) { + if (i == j) { + continue; + } + + host_connection_t* other = &HostConnections[j]; + + if (other->type != HostConnectionType_BtHid && + other->type != HostConnectionType_Dongle && + other->type != HostConnectionType_UnregisteredBtHid) { + continue; + } + + if (BtAddrEq(&conn->bleAddress, &other->bleAddress)) { + conn->type = HostConnectionType_Empty; + memset(&conn->bleAddress, 0, sizeof(bt_addr_le_t)); + conn->name = (string_segment_t){ .start = NULL, .end = NULL }; + conn->switchover = false; + break; + } + } + } +#endif +} + parser_error_t ParseHostConnections(config_buffer_t *buffer) { int errorCode; @@ -73,5 +113,7 @@ parser_error_t ParseHostConnections(config_buffer_t *buffer) { ); } + deduplicateUnregisteredConnections(); + return ParserError_Success; } From 863d19d07eb757d80c0ddbb8f577ea954d25c0b4 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Wed, 7 Jan 2026 15:26:08 +0100 Subject: [PATCH 13/46] Add jj configuration --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 66dfb0be9..2d48b6104 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ node_modules .cache build .vscode/settings.json +.jj mcux_include.json From 32e3af2d5b9f61bda865fe52bfe3a86e5e8f523e Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Wed, 14 Jan 2026 17:05:23 +0100 Subject: [PATCH 14/46] Fix compile_commands.json. Typo --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 4b9bc2e69..f265866e3 100755 --- a/build.sh +++ b/build.sh @@ -232,7 +232,7 @@ function createCentralCompileCommands() { echo creating central compile_commands.json - local existing_jsons=` $ROOT/device/build/ $ROOT/right/build/ $ROOT/trackball/build $ROOT/trackpoint/build $ROOT/keycluster/build -name "compile_commands.json" 2>/dev/null` + local existing_jsons=`find $ROOT/device/build/ $ROOT/right/build/ $ROOT/trackball/build $ROOT/trackpoint/build $ROOT/keycluster/build -name "compile_commands.json" 2>/dev/null` if [ "$existing_jsons" != "" ] then From cb3ae14653d0ed48d0e3b8572f4059353246c0ed Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 19:15:55 +0100 Subject: [PATCH 15/46] Ble fixes: increase ble buff counts. --- device/prj.conf.overlays/nrf_shared.conf | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/device/prj.conf.overlays/nrf_shared.conf b/device/prj.conf.overlays/nrf_shared.conf index 6442b02f3..33aadcba5 100644 --- a/device/prj.conf.overlays/nrf_shared.conf +++ b/device/prj.conf.overlays/nrf_shared.conf @@ -21,9 +21,14 @@ CONFIG_BT=y # CONFIG_BT_DEBUG_LOG=y CONFIG_BT_SMP=y -CONFIG_BT_L2CAP_TX_BUF_COUNT=5 + CONFIG_BT_FILTER_ACCEPT_LIST=y +# increase these to make multiple connections more reliable +CONFIG_BT_ATT_TX_COUNT=10 +CONFIG_BT_CONN_TX_MAX=6 +CONFIG_BT_L2CAP_TX_BUF_COUNT=12 + # negotiate larger MTU for NUS CONFIG_BT_USER_DATA_LEN_UPDATE=y CONFIG_BT_BUF_ACL_RX_SIZE=251 From bc991d185b01552fbfd5089570240d6137f3bc1d Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 5 Jan 2026 13:57:48 +0100 Subject: [PATCH 16/46] Ble fixes: don't disconnect connections that are cncted but not rdy. --- device/src/bt_conn.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 714d8497e..5aa2b4bd3 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -384,10 +384,12 @@ static bool isWantedInHidPairingMode(struct bt_conn *conn, bool alreadyAuthentic } static bool isWantedInNormalMode(struct bt_conn *conn, connection_id_t connectionId, connection_type_t connectionType) { + const bt_addr_le_t* addr = bt_conn_get_dst(conn); + bool selectedConnectionIsBleHid = Connections_Type(SelectedHostConnectionId) == ConnectionType_BtHid; - bool isHidCollision = connectionType == ConnectionType_BtHid && BtConn_ConnectedHidCount() > 0; - bool isSelectedConnection = BtAddrEq(bt_conn_get_dst(conn), &HostConnection(SelectedHostConnectionId)->bleAddress); + bool isHidCollision = connectionType == ConnectionType_BtHid && BtConn_ConnectedHidCount(addr) > 0; + bool isSelectedConnection = BtAddrEq(addr, &HostConnection(SelectedHostConnectionId)->bleAddress); bool weHaveSlotToSpare = BtConn_UnusedPeripheralConnectionCount() > 1 || SelectedHostConnectionId == ConnectionId_Invalid; bool isLeftConnection = connectionType == ConnectionType_NusLeft; @@ -959,7 +961,8 @@ ATTR_UNUSED static void disconnectAllHids() { void BtConn_ReserveConnections() { #if DEVICE_IS_UHK80_RIGHT bool hostSelected = SelectedHostConnectionId != ConnectionId_Invalid; - bool hostActive = hostSelected && Connections_IsReady(SelectedHostConnectionId); + uint8_t hostState = hostSelected ? Connections_GetState(SelectedHostConnectionId) : ConnectionState_Disconnected; + bool hostActive = hostSelected && hostState >= ConnectionState_Connected; bool selectionIsSatisfied = !hostSelected || hostActive; if (!selectionIsSatisfied) { @@ -981,6 +984,13 @@ void BtConn_ReserveConnections() { BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in ReserveConnections"); } WIDGET_REFRESH(&TargetWidget); + } else { + // TODO: what should we do if the selected connection is connected but not ready? + // Is this even a legal code path? + // Just log for now. + if (hostState == ConnectionState_Connected) { + LOG_WRN("Selected host is connected but not yet ready in ReserveConnections. Waiting...\n"); + } } #endif } From 8adf636c5e9460e8ea89402516958d6e1feaedcb Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Sat, 17 Jan 2026 13:56:19 +0100 Subject: [PATCH 17/46] Ble fixes: lower latency timeouts. Let's believe the author for once. --- device/src/bt_conn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 5aa2b4bd3..9822563c4 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -242,7 +242,7 @@ static void configureLatency(struct bt_conn *conn, latency_mode_t latencyMode) { const struct bt_le_conn_param conn_params = BT_LE_CONN_PARAM_INIT( 6, 6, // keep it low, lowest allowed is 6 (7.5ms), lowest supported widely is 9 (11.25ms) 0, // keeping it higher allows power saving on peripheral when there's nothing to send (keep it under 30 though) - 100 // connection timeout (*10ms) + 30 // connection timeout (*10ms) ); setLatency(conn, &conn_params); } @@ -254,7 +254,7 @@ static void configureLatency(struct bt_conn *conn, latency_mode_t latencyMode) { const struct bt_le_conn_param conn_params = BT_LE_CONN_PARAM_INIT( 6, 9, // keep it low, lowest allowed is 6 (7.5ms), lowest supported widely is 9 (11.25ms) 0, // keeping it higher allows power saving on peripheral when there's nothing to send (keep it under 30 though) - 100 // connection timeout (*10ms) + 30 // connection timeout (*10ms) ); setLatency(conn, &conn_params); } From d8ad0f9f44e2caa2d283a1a787bfcaccd3e3940f Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 29 Dec 2025 17:03:57 +0100 Subject: [PATCH 18/46] Ble fixes: Refactor handling of unknown connections. --- device/src/bt_pair.c | 2 +- device/src/connections.c | 3 +++ right/src/config_parser/parse_config.c | 6 ++++-- right/src/host_connection.c | 17 +++++++++++------ right/src/host_connection.h | 8 +++++++- .../usb_commands/usb_command_get_new_pairings.c | 2 +- 6 files changed, 27 insertions(+), 11 deletions(-) diff --git a/device/src/bt_pair.c b/device/src/bt_pair.c index c24ce54a7..25ec07f1a 100644 --- a/device/src/bt_pair.c +++ b/device/src/bt_pair.c @@ -293,7 +293,7 @@ bool BtPair_IsDeviceBonded(const bt_addr_le_t *addr) } void deleteBondIfUnknown(const struct bt_bond_info *info, void *user_data) { - if (!HostConnections_IsKnownBleAddress(&info->addr)) { + if (HostConnections_IsKnownBleAddress(&info->addr) != HostKnown_Registered) { printk(" - Deleting an unknown bond!\n"); deleteBond(info); } else { diff --git a/device/src/connections.c b/device/src/connections.c index 02df299a2..4b595f062 100644 --- a/device/src/connections.c +++ b/device/src/connections.c @@ -15,6 +15,7 @@ #include #include "config_manager.h" #include "bt_pair.h" +#include "usb_commands/usb_command_get_new_pairings.h" connection_t Connections[ConnectionId_Count] = { [ConnectionId_UsbHidRight] = { .isAlias = true }, @@ -434,6 +435,8 @@ void Connections_PrintInfo(log_target_t target) { LogTo(DEVICE_ID, target, "Configured peripheral count: %d\n", Cfg.Bt_MaxPeripheralConnections); LogTo(DEVICE_ID, target, "Pairing mode: %d\n", BtPair_PairingMode); LogTo(DEVICE_ID, target, "Directed advertising enabled: %d\n", Cfg.Bt_DirectedAdvertisingAllowed); + LogTo(DEVICE_ID, target, "New connection: %d\n", Bt_NewPairedDevice); + HostConnections_ListKnownBleConnections(target); BtConn_ListAllBonds(target); BtConn_ListCurrentConnections(target); diff --git a/right/src/config_parser/parse_config.c b/right/src/config_parser/parse_config.c index 04fcbd6ef..59737a0e8 100644 --- a/right/src/config_parser/parse_config.c +++ b/right/src/config_parser/parse_config.c @@ -35,6 +35,7 @@ #ifdef __ZEPHYR__ +#include "usb_commands/usb_command_get_new_pairings.h" #include "state_sync.h" #else #include "segment_display.h" @@ -384,11 +385,12 @@ parser_error_t parseConfig(config_buffer_t *buffer) #ifdef __ZEPHYR__ StateSync_UpdateProperty(StateSyncPropertyId_BatteryStationaryMode, &Cfg.BatteryStationaryMode); + // BtPair_ClearUnknownBonds(); + BtConn_UpdateHostConnectionPeerAllocations(); + UsbCommand_UpdateNewPairingsFlag(); #endif StateWormhole.devMode = Cfg.DevMode; LedManager_FullUpdate(); - BtPair_ClearUnknownBonds(); - BtConn_UpdateHostConnectionPeerAllocations(); MacroVariables_Reset(); WIDGET_REFRESH(&TargetWidget); // the target may have been renamed diff --git a/right/src/host_connection.c b/right/src/host_connection.c index f7de2f05f..7e8c4ab3a 100644 --- a/right/src/host_connection.c +++ b/right/src/host_connection.c @@ -22,19 +22,24 @@ host_connection_t HostConnections[HOST_CONNECTION_COUNT_MAX] = { }, }; -bool HostConnections_IsKnownBleAddress(const bt_addr_le_t *address) { +host_known_t HostConnections_IsKnownBleAddress(const bt_addr_le_t *address) { for (int i = 0; i < HOST_CONNECTION_COUNT_MAX; i++) { - switch (HostConnections[i].type) { + host_connection_type_t type = HostConnections[i].type; + switch (type) { case HostConnectionType_Empty: case HostConnectionType_UsbHidRight: case HostConnectionType_UsbHidLeft: - case HostConnectionType_NewBtHid: case HostConnectionType_Count: break; + case HostConnectionType_NewBtHid: + if (BtAddrEq(address, &HostConnections[i].bleAddress)) { + return HostKnown_Unregistered; + } + break; case HostConnectionType_Dongle: case HostConnectionType_BtHid: if (BtAddrEq(address, &HostConnections[i].bleAddress)) { - return true; + return HostKnown_Registered; } break; } @@ -44,11 +49,11 @@ bool HostConnections_IsKnownBleAddress(const bt_addr_le_t *address) { // Do check devices that are paired via settings - left, right, dongle for (int peerIdx = 0; peerIdx < PeerIdFirstHost; peerIdx++) { if (BtAddrEq(address, &Peers[peerIdx].addr)) { - return true; + return HostKnown_Registered; } } - return false; + return HostKnown_Unknown; } host_connection_t* HostConnection(uint8_t connectionId) { diff --git a/right/src/host_connection.h b/right/src/host_connection.h index b77c98dc4..a0cf65d12 100644 --- a/right/src/host_connection.h +++ b/right/src/host_connection.h @@ -52,13 +52,19 @@ bool switchover; } ATTR_PACKED host_connection_t; + typedef enum { + HostKnown_Unknown = 0, + HostKnown_Unregistered = 1, + HostKnown_Registered = 2, + } host_known_t; + // Variables: extern host_connection_t HostConnections[HOST_CONNECTION_COUNT_MAX]; // Functions: - bool HostConnections_IsKnownBleAddress(const bt_addr_le_t *address); + host_known_t HostConnections_IsKnownBleAddress(const bt_addr_le_t *address); host_connection_t* HostConnection(uint8_t connectionId); void HostConnections_ListKnownBleConnections(); diff --git a/right/src/usb_commands/usb_command_get_new_pairings.c b/right/src/usb_commands/usb_command_get_new_pairings.c index a18a5375b..c4773e03b 100644 --- a/right/src/usb_commands/usb_command_get_new_pairings.c +++ b/right/src/usb_commands/usb_command_get_new_pairings.c @@ -24,7 +24,7 @@ static void bt_foreach_bond_cb(const struct bt_bond_info *info, void *user_data) return; } - if (HostConnections_IsKnownBleAddress(&info->addr)) { + if (HostConnections_IsKnownBleAddress(&info->addr) != HostKnown_Unregistered) { return; } From 1d8492719bd0253eaa628d4d61ec092fdd36be09 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 29 Dec 2025 17:28:41 +0100 Subject: [PATCH 19/46] Ble fixes: When moving connections, exchange them to not terminate the old one. --- device/src/connections.c | 50 ++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/device/src/connections.c b/device/src/connections.c index 4b595f062..8caf8285e 100644 --- a/device/src/connections.c +++ b/device/src/connections.c @@ -316,21 +316,51 @@ connection_id_t Connections_GetNewBtHidConnectionId() { } void Connections_MoveConnection(uint8_t peerId, connection_id_t oldConnectionId, connection_id_t newConnectionId) { - bool isActiveConnection = oldConnectionId == ActiveHostConnectionId; - bool isSelectedConnection = oldConnectionId == SelectedHostConnectionId; + bool isOldActive = oldConnectionId == ActiveHostConnectionId; + bool isOldSelected = oldConnectionId == SelectedHostConnectionId; + bool isNewActive = newConnectionId == ActiveHostConnectionId; + bool isNewSelected = newConnectionId == SelectedHostConnectionId; - Connections[newConnectionId].peerId = Connections[oldConnectionId].peerId; - Connections[newConnectionId].state = Connections[oldConnectionId].state; + // Save both connection states + uint8_t oldPeerId = Connections[oldConnectionId].peerId; + connection_state_t oldState = Connections[oldConnectionId].state; - Peers[Connections[oldConnectionId].peerId].connectionId = newConnectionId; + uint8_t newPeerId = Connections[newConnectionId].peerId; + connection_state_t newState = Connections[newConnectionId].state; - Connections[oldConnectionId].peerId = PeerIdUnknown; - Connections[oldConnectionId].state = ConnectionState_Disconnected; + ASSERT(oldPeerId == peerId); + ASSERT(otherPeerId == PeerIdUnknown || newPeerId > peerId); - ActiveHostConnectionId = isActiveConnection ? newConnectionId : ActiveHostConnectionId; - SelectedHostConnectionId = isSelectedConnection ? newConnectionId : SelectedHostConnectionId; + // Exchange connection data + Connections[newConnectionId].peerId = oldPeerId; + Connections[newConnectionId].state = oldState; - if (isActiveConnection) { + Connections[oldConnectionId].peerId = newPeerId; + Connections[oldConnectionId].state = newState; + + // Update peer references for both connections + if (oldPeerId != PeerIdUnknown) { + Peers[oldPeerId].connectionId = newConnectionId; + } + if (newPeerId != PeerIdUnknown) { + Peers[newPeerId].connectionId = oldConnectionId; + } + + // Update active connection ID + if (isOldActive) { + ActiveHostConnectionId = newConnectionId; + } else if (isNewActive) { + ActiveHostConnectionId = oldConnectionId; + } + + // Update selected connection ID + if (isOldSelected) { + SelectedHostConnectionId = newConnectionId; + } else if (isNewSelected) { + SelectedHostConnectionId = oldConnectionId; + } + + if (isOldActive || isNewActive) { WIDGET_REFRESH(&TargetWidget); } } From 0468d027c11aa5ebb56b65849c1fd13b5851fe90 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Fri, 16 Jan 2026 14:40:31 +0100 Subject: [PATCH 20/46] Ble fixes: Prevent blue dongle on double switchHost. --- device/src/connections.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/device/src/connections.c b/device/src/connections.c index 8caf8285e..a4c334924 100644 --- a/device/src/connections.c +++ b/device/src/connections.c @@ -378,8 +378,8 @@ bool Connections_IsActiveHostConnection(connection_id_t connectionId) { return resolveAliases(connectionId) == ActiveHostConnectionId; } -static void setCurrentDongleToStandby() { - if (Connections_IsHostConnection(ActiveHostConnectionId) && HostConnection(ActiveHostConnectionId)->type == HostConnectionType_Dongle) { +static void setDongleToStandby(connection_id_t connectionId) { + if (Connections_IsHostConnection(connectionId) && HostConnection(connectionId)->type == HostConnectionType_Dongle) { bool standby = true; Messenger_Send2(DeviceId_Uhk_Dongle, MessageId_StateSync, StateSyncPropertyId_DongleStandby, (const uint8_t*)&standby, 1); } @@ -424,7 +424,9 @@ void Connections_HandleSwitchover(connection_id_t connectionId, bool forceSwitch bool connectionIsSelected = SelectedHostConnectionId == connectionId; bool noHostIsConnected = ActiveHostConnectionId == ConnectionId_Invalid; if (hostConnection->switchover || noHostIsConnected || forceSwitch || connectionIsSelected) { - setCurrentDongleToStandby(); + if (connectionId != ActiveHostConnectionId) { + setDongleToStandby(ActiveHostConnectionId); + } switchOver(connectionId); reportConnectionState(connectionId, "Conn state"); } From 1ef7200e888bebe40b52ac16e6a547784f0f54fd Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 5 Jan 2026 11:33:57 +0100 Subject: [PATCH 21/46] Ble fixes: add logging. --- device/src/bt_conn.c | 22 +++++++++++++++++++--- device/src/messenger.c | 2 +- right/src/bt_defs.h | 8 ++++---- right/src/slave_scheduler.c | 4 ++-- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 9822563c4..2672b47e6 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -134,11 +134,13 @@ static struct bt_conn* unsetAuthConn(bool cancel_auth) { static void safeDisconnect(struct bt_conn *conn, int reason) { if (conn == auth_conn) { + LOG_INF("Unauthenticating %s\n", GetPeerStringByConn(conn)); unsetAuthConn(true); } else { struct bt_conn_info info; int err = bt_conn_get_info(conn, &info); if (err == 0 && info.state == BT_CONN_STATE_CONNECTED) { + LOG_INF("Disconnecting %s\n", GetPeerStringByConn(conn)); bt_conn_disconnect(conn, reason); } } @@ -416,12 +418,23 @@ static bool isWantedInNormalMode(struct bt_conn *conn, connection_id_t connectio } } */ + + // We can get here during pairing where the connection collides with itself. + LOG_INF(" Not wanted: this is HID collision\n"); return false; } else if (selectedConnectionIsBleHid) { + if (!isSelectedConnection) { + LOG_INF(" Not wanted: selected connection is BLE HID, this is not it: %d != %d (%d)", SelectedHostConnectionId, connectionId, connectionType); + } return isSelectedConnection; } else { - return weHaveSlotToSpare || isSelectedConnection || isLeftConnection; + bool result = weHaveSlotToSpare || isSelectedConnection || isLeftConnection; + if (!result) { + LOG_INF(" Not wanted: haveSlot: %d, isSelected: %d (%d != %d (%d)), isLeft: %d", weHaveSlotToSpare, isSelectedConnection, + SelectedHostConnectionId, connectionId, connectionType, isLeftConnection); + } + return result; } } @@ -625,7 +638,7 @@ static bool isUhkDeviceConnection(connection_type_t connectionType) { static void connectAuthenticatedConnection(struct bt_conn *conn, connection_id_t connectionId, connection_type_t connectionType) { // in case we don't have free connection slots and this is not the selected connection, then refuse if (!isWanted(conn, true, connectionId, connectionType)) { - LOG_WRN("Refusing authenticated connenction %d (this is not a selected connection)\n", connectionId); + LOG_WRN("Refusing authenticated connenction %d (this is not a selected connection(%d))\n", connectionId, SelectedHostConnectionId); youAreNotWanted(conn); return; } @@ -806,6 +819,8 @@ static void pairing_complete(struct bt_conn *conn, bool bonded) { HostConnections_SelectByHostConnIndex(connectionId - ConnectionId_HostConnectionFirst); + LOG_INF("Pairing complete, passing connection %d to authenticatedConnection handler. Selected conn is %d\n", connectionId, SelectedHostConnectionId); + // we have to connect from here, because central changes its address *after* setting security connectAuthenticatedConnection(conn, connectionId, connectionType); } @@ -870,9 +885,10 @@ static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason) { LOG_WRN("Pairing failed: %s, reason %d\n", GetPeerStringByConn(conn), reason); } + static struct bt_conn_auth_info_cb conn_auth_info_callbacks = { .pairing_complete = pairing_complete, - .pairing_failed = pairing_failed + .pairing_failed = pairing_failed, }; void BtConn_Init(void) { diff --git a/device/src/messenger.c b/device/src/messenger.c index f127eb3cf..1fe7f2d53 100644 --- a/device/src/messenger.c +++ b/device/src/messenger.c @@ -432,7 +432,7 @@ void Messenger_ProcessQueue() { EventVector_Unset(EventVector_NewMessage); messenger_queue_record_t rec = MessengerQueue_Take(); while (rec.data != NULL) { - Trace('<'); + Trace_Printc("<8"); receive(rec.data+rec.offset, rec.len); MessengerQueue_FreeMemory(rec.data); Trace('>'); diff --git a/right/src/bt_defs.h b/right/src/bt_defs.h index 31468b3cf..d2d62bd37 100644 --- a/right/src/bt_defs.h +++ b/right/src/bt_defs.h @@ -13,10 +13,10 @@ // Typedefs: typedef enum { - PairingMode_Oob, - PairingMode_PairHid, - PairingMode_Advertise, - PairingMode_Default, + PairingMode_Oob = 0, + PairingMode_PairHid = 1, + PairingMode_Advertise = 2, + PairingMode_Default = 3, PairingMode_Off = PairingMode_Default, } pairing_mode_t; diff --git a/right/src/slave_scheduler.c b/right/src/slave_scheduler.c index 5fcee870d..600f8d257 100644 --- a/right/src/slave_scheduler.c +++ b/right/src/slave_scheduler.c @@ -98,7 +98,7 @@ static void slaveSchedulerCallback(I2C_Type *base, i2c_master_handle_t *handle, bool isTransferScheduled = false; I2cSlaveScheduler_Counter++; - Trace_Printc("<"); + // Trace_Printc("<7"); do { uhk_slave_t *currentSlave = Slaves + currentSlaveId; @@ -145,7 +145,7 @@ static void slaveSchedulerCallback(I2C_Type *base, i2c_master_handle_t *handle, } while (!isTransferScheduled); - Trace_Printc(">"); + // Trace_Printc(">"); } void SlaveSchedulerCallback(status_t status) From 1e6eb64f0cf4f7c14967010202d9860b81c4d217 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 5 Jan 2026 13:54:07 +0100 Subject: [PATCH 22/46] Ble fixes: when detecting hid collision, don't collide with yourself. --- device/src/bt_advertise.c | 2 +- device/src/bt_conn.c | 8 +++++--- device/src/bt_conn.h | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/device/src/bt_advertise.c b/device/src/bt_advertise.c index 41688194f..e4a4b2dfd 100644 --- a/device/src/bt_advertise.c +++ b/device/src/bt_advertise.c @@ -248,7 +248,7 @@ adv_config_t BtAdvertise_Config() { return ADVERTISEMENT( 0 ); } } - else if (BtConn_ConnectedHidCount() > 0) { + else if (BtConn_ConnectedHidCount(NULL) > 0) { /** we can't handle multiple HID connections, so don't advertise it when one HID is already connected */ return ADVERTISEMENT(ADVERTISE_NUS); } else { diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 2672b47e6..6c54a5100 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -990,7 +990,7 @@ void BtConn_ReserveConnections() { uint8_t unusedConnectionCount = BtConn_UnusedPeripheralConnectionCount(); bool selectedConnectionIsBleHid = Connections_Type(SelectedHostConnectionId) == ConnectionType_BtHid; - if (selectedConnectionIsBleHid && BtConn_ConnectedHidCount() > 0) { + if (selectedConnectionIsBleHid && BtConn_ConnectedHidCount(NULL) > 0) { disconnectAllHids(); // Advertising will get started when the host actually gets disconnected } else if (unusedConnectionCount == 0) { @@ -1031,11 +1031,13 @@ void Bt_SetEnabled(bool enabled) { } } -uint8_t BtConn_ConnectedHidCount() { +uint8_t BtConn_ConnectedHidCount(const bt_addr_le_t* excludeAddr) { uint8_t connectedHids = 0; for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { if (Peers[peerId].conn && Connections_Type(Peers[peerId].connectionId) == ConnectionType_BtHid) { - connectedHids++; + if (excludeAddr == NULL || !BtAddrEq(excludeAddr, &Peers[peerId].addr)) { + connectedHids++; + } } } return connectedHids; diff --git a/device/src/bt_conn.h b/device/src/bt_conn.h index eac45c85b..90ef605c0 100644 --- a/device/src/bt_conn.h +++ b/device/src/bt_conn.h @@ -79,7 +79,7 @@ typedef enum { void Bt_SetEnabled(bool enabled); void BtConn_MakeSpaceForHid(); - uint8_t BtConn_ConnectedHidCount(); + uint8_t BtConn_ConnectedHidCount(const bt_addr_le_t* excludeAddr); static inline bool BtAddrEq(const bt_addr_le_t *a, const bt_addr_le_t *b) { return 0 == memcmp(a->a.val, b->a.val, sizeof(a->a.val)); From e502cb2ea5f706944ad6ce1d21b9f3cd594a16b1 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 5 Jan 2026 13:55:34 +0100 Subject: [PATCH 23/46] Ble fixes: on pairing complete, just set conn selected - dont reserve --- device/src/bt_conn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 6c54a5100..00e17b9e5 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -817,7 +817,7 @@ static void pairing_complete(struct bt_conn *conn, bool bonded) { Bt_NewPairedDevice = true; } - HostConnections_SelectByHostConnIndex(connectionId - ConnectionId_HostConnectionFirst); + HostConnection_SetSelectedConnection(connectionId); LOG_INF("Pairing complete, passing connection %d to authenticatedConnection handler. Selected conn is %d\n", connectionId, SelectedHostConnectionId); From 51097eb8a0efdb73c7261766c0b669a26f81b033 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 29 Dec 2025 18:19:55 +0100 Subject: [PATCH 24/46] Ble slots: pilot implementation Also, add `unpairHost` commands, and allow referencing hosts by slot numbers. --- device/src/bt_conn.c | 16 ++- device/src/bt_pair.c | 15 ++- device/src/bt_pair.h | 1 + device/src/connections.c | 10 +- device/src/connections.h | 3 +- .../oled/screens/notification_screen.c | 4 + .../oled/screens/notification_screen.h | 1 + .../src/keyboard/oled/widgets/widget_store.c | 41 ++++--- device/src/usb/usb_compatibility.cpp | 8 +- doc-dev/reference-manual.md | 4 +- right/src/config_parser/parse_config.c | 3 +- right/src/host_connection.c | 103 ++++++++++++++++-- right/src/host_connection.h | 8 ++ right/src/macros/commands.c | 41 ++++++- right/src/macros/display.c | 7 ++ right/src/macros/status_buffer.h | 1 + right/src/usb_report_updater.c | 51 ++++++++- 17 files changed, 273 insertions(+), 44 deletions(-) diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 00e17b9e5..a94f1b84a 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -35,6 +35,8 @@ #include "trace.h" #include "right/src/bt_defs.h" #include "bt_health.h" +#include "macros/status_buffer.h" + LOG_MODULE_REGISTER(Bt, LOG_LEVEL_INF); @@ -295,9 +297,11 @@ void BtConn_UpdateHostConnectionPeerAllocations() { if (conn) { connection_id_t currentId = Peers[peerId].connectionId; connection_id_t newId = Connections_GetConnectionIdByHostAddr(bt_conn_get_dst(conn)); - LOG_INF("Reallocating peer %s from connection %d -> %d\n", Peers[peerId].name, currentId, newId); if (newId != ConnectionId_Invalid && newId != currentId) { Connections_MoveConnection(peerId, currentId, newId); + } else if (newId == ConnectionId_Invalid) { + LOG_WRN("No host connection found for peer %s, disconnecting\n", Peers[peerId].name); + safeDisconnect(conn, BT_REASON_UNSPECIFIED); } } } @@ -811,10 +815,16 @@ static void pairing_complete(struct bt_conn *conn, bool bonded) { connection_type_t connectionType = Connections_Type(connectionId); if (connectionId == ConnectionId_Invalid) { - connectionId = Connections_GetNewBtHidConnectionId(); + connectionId = HostConnections_AllocateConnectionIdForUnregisteredHid(&addr); connectionType = ConnectionType_BtHid; - HostConnection(connectionId)->bleAddress = addr; Bt_NewPairedDevice = true; + + if (connectionId == ConnectionId_Invalid) { + LOG_WRN("No connection slot available for newly paired device %s\n", GetPeerStringByConn(conn)); + NotifyPrintf("No slot available!"); + safeDisconnect(conn, BT_REASON_UNSPECIFIED); + return; + } } HostConnection_SetSelectedConnection(connectionId); diff --git a/device/src/bt_pair.c b/device/src/bt_pair.c index 25ec07f1a..797ad0288 100644 --- a/device/src/bt_pair.c +++ b/device/src/bt_pair.c @@ -292,7 +292,7 @@ bool BtPair_IsDeviceBonded(const bt_addr_le_t *addr) return bonded; } -void deleteBondIfUnknown(const struct bt_bond_info *info, void *user_data) { +ATTR_UNUSED void deleteBondIfUnknown(const struct bt_bond_info *info, void *user_data) { if (HostConnections_IsKnownBleAddress(&info->addr) != HostKnown_Registered) { printk(" - Deleting an unknown bond!\n"); deleteBond(info); @@ -302,9 +302,20 @@ void deleteBondIfUnknown(const struct bt_bond_info *info, void *user_data) { }; -void BtPair_ClearUnknownBonds() { +ATTR_UNUSED void BtPair_ClearUnknownBonds() { BT_TRACE_AND_ASSERT("bp9"); printk("Clearing bonds\n"); bt_foreach_bond(BT_ID_DEFAULT, deleteBondIfUnknown, NULL); UsbCommand_UpdateNewPairingsFlag(); } + +void allocateBond(const struct bt_bond_info *info, void *user_data) { + HostConnections_AllocateConnectionIdForUnregisteredHid(&info->addr); +} + +void BtPair_AllocateUnregisteredBonds() { + BT_TRACE_AND_ASSERT("bp10"); + printk("Allocating unregistered bonds\n"); + bt_foreach_bond(BT_ID_DEFAULT, allocateBond, NULL); + UsbCommand_UpdateNewPairingsFlag(); +} diff --git a/device/src/bt_pair.h b/device/src/bt_pair.h index 0d4c841d8..2c0e95d6f 100644 --- a/device/src/bt_pair.h +++ b/device/src/bt_pair.h @@ -29,6 +29,7 @@ void BtManager_EnterMode(pairing_mode_t mode, bool toggle); void BtPair_ClearUnknownBonds(); void BtPair_UnpairAllNonLR(); + void BtPair_AllocateUnregisteredBonds(); // Variables diff --git a/device/src/connections.c b/device/src/connections.c index a4c334924..0ab7eba11 100644 --- a/device/src/connections.c +++ b/device/src/connections.c @@ -167,6 +167,8 @@ connection_type_t Connections_Type(connection_id_t connectionId) { host_connection_type_t hostConnectionType = HostConnection(connectionId)->type; switch (hostConnectionType) { case HostConnectionType_NewBtHid: + return ConnectionType_Empty; + case HostConnectionType_UnregisteredBtHid: return ConnectionType_BtHid; default: return (connection_type_t)hostConnectionType; @@ -205,10 +207,11 @@ connection_target_t Connections_Target(connection_id_t connectionId) { case HostConnectionType_UsbHidRight: return ConnectionTarget_Host; case HostConnectionType_BtHid: - case HostConnectionType_NewBtHid: + case HostConnectionType_UnregisteredBtHid: return ConnectionTarget_Host; case HostConnectionType_Dongle: return ConnectionTarget_Host; + case HostConnectionType_NewBtHid: case HostConnectionType_Empty: case HostConnectionType_Count: return ConnectionTarget_None; @@ -251,9 +254,9 @@ static connection_id_t getConnIdByAddr(const bt_addr_le_t *addr) { for (uint8_t connectionId = ConnectionId_HostConnectionFirst; connectionId <= ConnectionId_HostConnectionLast; connectionId++) { host_connection_t *hostConnection = HostConnection(connectionId); switch (hostConnection->type) { - case HostConnectionType_NewBtHid: case HostConnectionType_Dongle: case HostConnectionType_BtHid: + case HostConnectionType_UnregisteredBtHid: if (BtAddrEq(addr, &hostConnection->bleAddress)) { return connectionId; } @@ -261,6 +264,7 @@ static connection_id_t getConnIdByAddr(const bt_addr_le_t *addr) { case HostConnectionType_Empty: case HostConnectionType_UsbHidLeft: case HostConnectionType_UsbHidRight: + case HostConnectionType_NewBtHid: case HostConnectionType_Count: break; } @@ -329,7 +333,7 @@ void Connections_MoveConnection(uint8_t peerId, connection_id_t oldConnectionId, connection_state_t newState = Connections[newConnectionId].state; ASSERT(oldPeerId == peerId); - ASSERT(otherPeerId == PeerIdUnknown || newPeerId > peerId); + ASSERT(newPeerId == PeerIdUnknown || newPeerId > peerId); // Exchange connection data Connections[newConnectionId].peerId = oldPeerId; diff --git a/device/src/connections.h b/device/src/connections.h index cdaa0862c..da1dca64f 100644 --- a/device/src/connections.h +++ b/device/src/connections.h @@ -18,7 +18,8 @@ ConnectionType_UsbHidRight = HostConnectionType_UsbHidRight, ConnectionType_UsbHidLeft = HostConnectionType_UsbHidLeft, ConnectionType_BtHid = HostConnectionType_BtHid, - ConnectionType_NewBtHid = HostConnectionType_NewBtHid, + ConnectionType_UnusedNewBtHid = HostConnectionType_NewBtHid, + ConnectionType_UnusedUnregisteredHid = HostConnectionType_UnregisteredBtHid, ConnectionType_NusDongle = HostConnectionType_Dongle, ConnectionType_NusLeft = HostConnectionType_Count, ConnectionType_NusRight, diff --git a/device/src/keyboard/oled/screens/notification_screen.c b/device/src/keyboard/oled/screens/notification_screen.c index 434c2cc47..331728fad 100644 --- a/device/src/keyboard/oled/screens/notification_screen.c +++ b/device/src/keyboard/oled/screens/notification_screen.c @@ -30,3 +30,7 @@ void NotificationScreen_Notify(const char* message) { NotificationScreen_NotifyFor(message, SCREEN_NOTIFICATION_TIMEOUT); } +void NotificationScreen_Hide() { + EventScheduler_Reschedule(Timer_GetCurrentTime() + 10, EventSchedulerEvent_SwitchScreen, "notification screen - hide"); +} + diff --git a/device/src/keyboard/oled/screens/notification_screen.h b/device/src/keyboard/oled/screens/notification_screen.h index d3b715db1..aed0d8ee7 100644 --- a/device/src/keyboard/oled/screens/notification_screen.h +++ b/device/src/keyboard/oled/screens/notification_screen.h @@ -20,5 +20,6 @@ void NotificationScreen_Init(void); void NotificationScreen_Notify(const char* message); void NotificationScreen_NotifyFor(const char* message, uint16_t duration); + void NotificationScreen_Hide(); #endif diff --git a/device/src/keyboard/oled/widgets/widget_store.c b/device/src/keyboard/oled/widgets/widget_store.c index b4fdd666b..0a2849789 100644 --- a/device/src/keyboard/oled/widgets/widget_store.c +++ b/device/src/keyboard/oled/widgets/widget_store.c @@ -39,6 +39,7 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-truncation" + widget_t KeymapWidget; widget_t LayerWidget; widget_t KeymapLayerWidget; @@ -93,17 +94,31 @@ static string_segment_t getDebugLineText() { #undef BUFFER_LENGTH } -static string_segment_t getTargetText_() { - switch (ActiveHostConnectionId) { +static string_segment_t getTargetText_(uint8_t connId, bool shortVersion) { + switch (connId) { case ConnectionId_UsbHidRight: return (string_segment_t){ .start = "USB Cable", .end = NULL }; case ConnectionId_BtHid: return (string_segment_t){ .start = "Bluetooth", .end = NULL }; case ConnectionId_HostConnectionFirst ... ConnectionId_HostConnectionLast: { - host_connection_t* hostConnection = HostConnection(ActiveHostConnectionId); + host_connection_t* hostConnection = HostConnection(connId); + + if (hostConnection == NULL) { + LogU("Widget store get target called with invalid conn Id %d. This wasn't supposed to happen!\n", connId); + return (string_segment_t){ .start = NULL, .end = NULL }; + } if (SegmentLen(hostConnection->name) > 0) { - return hostConnection->name; + if (hostConnection->type == HostConnectionType_UnregisteredBtHid) { + #define UNREGISTERED_BLE_HID_TEXT_SHORT "Ble slot %d" + #define UNREGISTERED_BLE_HID_TEXT "Bluetooth device, slot %d" + static char buffer[] = UNREGISTERED_BLE_HID_TEXT; + const char* fmt = shortVersion ? UNREGISTERED_BLE_HID_TEXT_SHORT : UNREGISTERED_BLE_HID_TEXT; + snprintf(buffer, sizeof(buffer), fmt, connId - ConnectionId_HostConnectionFirst + 1); + return (string_segment_t){ .start = buffer, .end = NULL }; + } else { + return hostConnection->name; + } } switch(hostConnection->type) { @@ -134,22 +149,20 @@ static string_segment_t getTargetText() { if ( Macros_DisplayStringsBuffs.host[0] != 0) { return (string_segment_t){ .start = Macros_DisplayStringsBuffs.host, .end = NULL }; } else { - string_segment_t currentConnection = getTargetText_(); + bool shortNames = SelectedHostConnectionId != ConnectionId_Invalid; + size_t offset = 0; - string_segment_t selectedConnection = (string_segment_t){ .start = NULL, .end = NULL }; + string_segment_t currentConnection = getTargetText_(ActiveHostConnectionId, shortNames); + offset = snprintf(buffer, sizeof(buffer)-1, "%.*s", SegmentLen(currentConnection), currentConnection.start); if (SelectedHostConnectionId != ConnectionId_Invalid) { - host_connection_t* hostConnection = HostConnection(SelectedHostConnectionId); - if (hostConnection) { - selectedConnection = hostConnection->name; + string_segment_t selectedConnection = (string_segment_t){ .start = NULL, .end = NULL }; + selectedConnection = getTargetText_(SelectedHostConnectionId, true); + if (selectedConnection.start) { + snprintf(buffer+offset, sizeof(buffer)-1-offset, " -> %.*s", SegmentLen(selectedConnection), selectedConnection.start); } } - if (selectedConnection.start) { - snprintf(buffer, sizeof(buffer)-1, "%.*s -> %.*s", SegmentLen(currentConnection), currentConnection.start, SegmentLen(selectedConnection), selectedConnection.start); - } else { - snprintf(buffer, sizeof(buffer)-1, "%.*s", SegmentLen(currentConnection), currentConnection.start); - } return (string_segment_t){ .start = buffer, .end = NULL }; } }; diff --git a/device/src/usb/usb_compatibility.cpp b/device/src/usb/usb_compatibility.cpp index 700639a30..d6bc41811 100644 --- a/device/src/usb/usb_compatibility.cpp +++ b/device/src/usb/usb_compatibility.cpp @@ -63,7 +63,7 @@ static report_sink_t determineSink() { return ReportSink_Dongle; } default: - printk("Unhandled sink type. Is this connection really meant to be a report target?\n"); + printk("Unhandled sink type %d. Is this connection really meant to be a report target?\n", connectionType); return ReportSink_Usb; } } @@ -159,7 +159,6 @@ extern "C" void UsbCompatibility_UpdateKeyboardLedsState() UsbCompatibility_SetCurrentKeyboardLedsState(KeyboardLedsStateUsb); break; case ConnectionType_BtHid: - case ConnectionType_NewBtHid: UsbCompatibility_SetCurrentKeyboardLedsState(KeyboardLedsStateBle); break; case ConnectionType_NusDongle: @@ -209,9 +208,8 @@ extern "C" void UsbCompatibility_SetKeyboardLedsState(connection_id_t connection KeyboardLedsStateUsb = state; break; case ConnectionType_BtHid: - case ConnectionType_NewBtHid: KeyboardLedsStateBle = state; - break; + break; case ConnectionType_NusDongle: break; default: @@ -235,7 +233,6 @@ extern "C" float VerticalScrollMultiplier(void) return mouse_app::usb_handle().resolution_report().vertical_scroll_multiplier(); #if DEVICE_IS_UHK80_RIGHT case ConnectionType_BtHid: - case ConnectionType_NewBtHid: return mouse_app::ble_handle().resolution_report().vertical_scroll_multiplier(); #endif case ConnectionType_NusDongle: @@ -253,7 +250,6 @@ extern "C" float HorizontalScrollMultiplier(void) return mouse_app::usb_handle().resolution_report().horizontal_scroll_multiplier(); #if DEVICE_IS_UHK80_RIGHT case ConnectionType_BtHid: - case ConnectionType_NewBtHid: return mouse_app::ble_handle().resolution_report().horizontal_scroll_multiplier(); #endif case ConnectionType_NusDongle: diff --git a/doc-dev/reference-manual.md b/doc-dev/reference-manual.md index 585906d9f..d4eb176b1 100644 --- a/doc-dev/reference-manual.md +++ b/doc-dev/reference-manual.md @@ -126,7 +126,8 @@ COMMAND = tapKeySeq [persistent] [ SHORTCUT | KEY_SEQUENCE ]+ COMMAND = powerMode [toggle] { wake | lock | sleep } COMMAND = reboot COMMAND = bluetooth [toggle] { pair | advertise | noAdvertise } -COMMAND = switchHost { last | lastSelected | next | previous | | } +COMMAND = switchHost { last | lastSelected | next | previous | | | } +COMMAND = unpairHost { | } COMMAND = set module.MODULEID.navigationMode.LAYERID_BASIC NAVIGATION_MODE COMMAND = set module.MODULEID.baseSpeed COMMAND = set module.MODULEID.speed @@ -382,6 +383,7 @@ COMMAND = setEmergencyKey KEYID - `lastSelected` switches to the last manually selected connection. This is useful to undo an automatic switchover. - `` switches to the host connection with the given name. If the connection is not available, UHK will reserve a connection slot for this host. Therefore it is possible to connect to violet dongles too. - See the bluetooth section for more information. +- `unpairHost { | }` unpairs the given host connection. If the slot is in User Configuration, it only unpairs the host, but keeps the connection information. If the slot is not in User Configuration, it removes the entire connection information and makes the slot available to pairing another device. - `reconnect` disconnects current active host, waits 100ms and then attempts to connect to it again (i.e., similar to calling switchHost). ### Triggering keyboard actions (pressing keys, clicking, etc.): diff --git a/right/src/config_parser/parse_config.c b/right/src/config_parser/parse_config.c index 59737a0e8..1507f7735 100644 --- a/right/src/config_parser/parse_config.c +++ b/right/src/config_parser/parse_config.c @@ -36,6 +36,7 @@ #ifdef __ZEPHYR__ #include "usb_commands/usb_command_get_new_pairings.h" +#include "bt_pair.h" #include "state_sync.h" #else #include "segment_display.h" @@ -385,7 +386,7 @@ parser_error_t parseConfig(config_buffer_t *buffer) #ifdef __ZEPHYR__ StateSync_UpdateProperty(StateSyncPropertyId_BatteryStationaryMode, &Cfg.BatteryStationaryMode); - // BtPair_ClearUnknownBonds(); + BtPair_AllocateUnregisteredBonds(); BtConn_UpdateHostConnectionPeerAllocations(); UsbCommand_UpdateNewPairingsFlag(); #endif diff --git a/right/src/host_connection.c b/right/src/host_connection.c index 7e8c4ab3a..d670404d3 100644 --- a/right/src/host_connection.c +++ b/right/src/host_connection.c @@ -1,4 +1,5 @@ #include "host_connection.h" +#include "macros/status_buffer.h" #include "str_utils.h" #include "macros/string_reader.h" @@ -8,11 +9,13 @@ #include "logger.h" #include "keyboard/oled/widgets/widget_store.h" #include "stubs.h" +#include "debug.h" +#include "bt_pair.h" host_connection_t HostConnections[HOST_CONNECTION_COUNT_MAX] = { [HOST_CONNECTION_COUNT_MAX - 2] = { .type = HostConnectionType_NewBtHid, - .name = (string_segment_t){ .start = "New Bluetooth Device", .end = NULL }, + .name = (string_segment_t){ .start = "Unregistered Ble", .end = NULL }, .switchover = true, }, [HOST_CONNECTION_COUNT_MAX - 1] = { @@ -30,8 +33,9 @@ host_known_t HostConnections_IsKnownBleAddress(const bt_addr_le_t *address) { case HostConnectionType_UsbHidRight: case HostConnectionType_UsbHidLeft: case HostConnectionType_Count: - break; case HostConnectionType_NewBtHid: + break; + case HostConnectionType_UnregisteredBtHid: if (BtAddrEq(address, &HostConnections[i].bleAddress)) { return HostKnown_Unregistered; } @@ -122,13 +126,22 @@ void HostConnections_SelectLastSelectedConnection(void) { } } -void HostConnections_SelectByName(parser_context_t* ctx) { +uint8_t HostConnections_NameToConnId(parser_context_t* ctx) { for (uint8_t i = ConnectionId_HostConnectionFirst; i <= ConnectionId_HostConnectionLast; i++) { if (Macros_CompareStringToken(ctx, HostConnection(i)->name)) { - selectConnection(i); - return; + return i; } } + + return ConnectionId_Invalid; +} + +void HostConnections_SelectByName(parser_context_t* ctx) { + uint8_t connId = HostConnections_NameToConnId(ctx); + + if (connId != ConnectionId_Invalid) { + selectConnection(connId); + } } void HostConnections_SelectByHostConnIndex(uint8_t hostConnIndex) { @@ -137,7 +150,8 @@ void HostConnections_SelectByHostConnIndex(uint8_t hostConnIndex) { if (hostConnection && hostConnection->type != HostConnectionType_Empty) { selectConnection(connId); } else { - LogUS("Invalid host connection index: %d. Ignoring!\n", hostConnIndex); + LogU("Invalid host connection index: %d. Ignoring!\n", hostConnIndex); + NotifyPrintf("Unassigned slot: %d.", hostConnIndex + 1); } } @@ -147,11 +161,14 @@ void HostConnections_ListKnownBleConnections() { host_connection_type_t type = HostConnections[i].type; switch (type) { case HostConnectionType_Empty: + case HostConnectionType_Count: + break; case HostConnectionType_UsbHidRight: case HostConnectionType_UsbHidLeft: - case HostConnectionType_Count: + printk(" - %d '%.*s'\n", i, EXPAND_SEGMENT(HostConnections[i].name)); break; case HostConnectionType_NewBtHid: + case HostConnectionType_UnregisteredBtHid: case HostConnectionType_Dongle: case HostConnectionType_BtHid: printk(" - %d '%.*s': address: %s\n", i, EXPAND_SEGMENT(HostConnections[i].name), GetAddrString(&HostConnections[i].bleAddress)); @@ -167,6 +184,78 @@ void HostConnections_Reconnect() { HostConnections_SelectByHostConnIndex(connId - ConnectionId_HostConnectionFirst); } +static void allocateUnregisteredHidId(const bt_addr_le_t *addr, connection_id_t connectionId) { + host_connection_t* hostConnection = HostConnection(connectionId); + host_connection_t* newConnectionTemplate = HostConnection(Connections_GetNewBtHidConnectionId()); + + ASSERT(newConnectionTemplate); + if (!newConnectionTemplate) { + return; + } + + ASSERT(Connections[connectionId].peerId == PeerIdUnknown); + ASSERT(Connections[connectionId].state == ConnectionState_Disconnected); + + Bt_NewPairedDevice = true; + hostConnection->type = HostConnectionType_UnregisteredBtHid; + hostConnection->bleAddress = *addr; + hostConnection->switchover = newConnectionTemplate->switchover; + hostConnection->name = newConnectionTemplate->name; +} + +/* + * - Try to find existing allocation by Connections_GetConnectionIdByBtAddr(addr); + * - If not found, search for a host connection slot of type Empty. + * - If not found, return ConnectionId_Invalid. + */ +uint8_t HostConnections_AllocateConnectionIdForUnregisteredHid(const bt_addr_le_t *addr) { + connection_id_t existingConnId = Connections_GetConnectionIdByHostAddr(addr); + if (existingConnId != ConnectionId_Invalid) { + return existingConnId; + } + + for (uint8_t connectionId = ConnectionId_HostConnectionFirst; connectionId <= ConnectionId_HostConnectionLast; connectionId++) { + host_connection_t *hostConnection = HostConnection(connectionId); + if (hostConnection->type == HostConnectionType_Empty) { + allocateUnregisteredHidId(addr, connectionId); + return connectionId; + } + } + + // TODO: free connections that are not bonded anymore? + + return ConnectionId_Invalid; +} + + +void HostConnections_ClearConnectionByConnId(uint8_t connectionId) { + host_connection_t *hostConnection = HostConnection(connectionId); + + if (hostConnection->type == HostConnectionType_UnregisteredBtHid) { + BtPair_Unpair(hostConnection->bleAddress); + NotifyPrintf("Slot %d cleared.", connectionId - ConnectionId_HostConnectionFirst + 1); + + hostConnection->type = HostConnectionType_Empty; + memset(&hostConnection->bleAddress, 0, sizeof(bt_addr_le_t)); + hostConnection->name = (string_segment_t){ .start = NULL, .end = NULL }; + hostConnection->switchover = false; + } + else if (hostConnection->type == HostConnectionType_BtHid) { + BtPair_Unpair(hostConnection->bleAddress); + NotifyPrintf("Ble slot %d unpaired.", connectionId - ConnectionId_HostConnectionFirst + 1); + } + else if (hostConnection->type == HostConnectionType_Dongle) { + BtPair_Unpair(hostConnection->bleAddress); + NotifyPrintf("Dongle slot %d unpaired.", connectionId - ConnectionId_HostConnectionFirst + 1); + } + else if (hostConnection->type == HostConnectionType_Empty) { + NotifyPrintf("Slot %d is empty.", connectionId - ConnectionId_HostConnectionFirst + 1); + } + else { + NotifyPrintf("Unpairing %d not allowed.", connectionId - ConnectionId_HostConnectionFirst + 1); + } +} + #endif diff --git a/right/src/host_connection.h b/right/src/host_connection.h index a0cf65d12..31109807d 100644 --- a/right/src/host_connection.h +++ b/right/src/host_connection.h @@ -35,6 +35,7 @@ // Typedefs: + // these are serialized, don't change order! typedef enum { HostConnectionType_Empty = 0, HostConnectionType_UsbHidRight = 1, @@ -42,6 +43,7 @@ HostConnectionType_BtHid = 3, HostConnectionType_Dongle = 4, HostConnectionType_NewBtHid = 5, + HostConnectionType_UnregisteredBtHid = 6, HostConnectionType_Count, } host_connection_type_t; @@ -77,6 +79,12 @@ void HostConnections_SelectByName(parser_context_t* ctx); void HostConnection_SetSelectedConnection(uint8_t connectionId); + uint8_t HostConnections_NameToConnId(parser_context_t* ctx); + + void HostConnections_ClearConnectionByConnId(uint8_t connectionId); + void HostConnections_Reconnect(); + uint8_t HostConnections_AllocateConnectionIdForUnregisteredHid(const bt_addr_le_t *addr); + #endif diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index 66b7cb6a6..083b19839 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -43,6 +43,7 @@ #include "connections.h" #include "bt_pair.h" #include "shell.h" +#include "host_connection.h" #else #include "segment_display.h" #endif @@ -1869,12 +1870,47 @@ static macro_result_t processFreezeCommand() return MacroResult_Finished; } +static macro_result_t Macros_ProcessUnpairHostCommand(parser_context_t* ctx) +{ + ATTR_UNUSED uint8_t slotId = 0; + ATTR_UNUSED uint8_t connId = 0; + + if (Macros_IsNUM(ctx)) { + ATTR_UNUSED uint8_t arg = Macros_ConsumeInt(ctx); +#ifdef __ZEPHYR__ + slotId = arg; + connId = ConnectionId_HostConnectionFirst + slotId - 1; +#endif + } else { +#ifdef __ZEPHYR__ + connId = HostConnections_NameToConnId(ctx); + slotId = connId - ConnectionId_HostConnectionFirst + 1; +#endif + Macros_ConsumeStringToken(ctx); + } + + if (Macros_DryRun) { + return MacroResult_Finished; + } + +#ifdef __ZEPHYR__ + HostConnections_ClearConnectionByConnId(connId); +#endif + + return MacroResult_Finished; +} + static macro_result_t processSwitchHostCommand(parser_context_t* ctx) { #define DRY_RUN_FINISH() if (Macros_DryRun) { return MacroResult_Finished; } #ifdef __ZEPHYR__ - if (ConsumeToken(ctx, "next")) { + if (Macros_IsNUM(ctx)) { + uint8_t hostConnectionIndex = Macros_ConsumeInt(ctx) - 1; + DRY_RUN_FINISH(); + HostConnections_SelectByHostConnIndex(hostConnectionIndex); + } + else if (ConsumeToken(ctx, "next")) { DRY_RUN_FINISH(); HostConnections_SelectNextConnection(); } @@ -2579,6 +2615,9 @@ static macro_result_t processCommand(parser_context_t* ctx) else if (ConsumeToken(ctx, "untoggleLayer")) { return processUnToggleLayerCommand(); } + else if (ConsumeToken(ctx, "unpairHost")) { + return Macros_ProcessUnpairHostCommand(ctx); + } else { goto failed; } diff --git a/right/src/macros/display.c b/right/src/macros/display.c index ab420d7c5..6913227f3 100644 --- a/right/src/macros/display.c +++ b/right/src/macros/display.c @@ -255,5 +255,12 @@ void NotifyPrintf(const char *fmt, ...) #endif } +void UnNotify() +{ +#if DEVICE_HAS_OLED + NotificationScreen_Hide(); +#endif +} + diff --git a/right/src/macros/status_buffer.h b/right/src/macros/status_buffer.h index 62812d82a..9e589b2ca 100644 --- a/right/src/macros/status_buffer.h +++ b/right/src/macros/status_buffer.h @@ -59,5 +59,6 @@ void MacroStatusBuffer_SafePrint(); void NotifyPrintf(const char *fmt, ...); + void UnNotify(); #endif diff --git a/right/src/usb_report_updater.c b/right/src/usb_report_updater.c index f9e13a084..6267e34a9 100644 --- a/right/src/usb_report_updater.c +++ b/right/src/usb_report_updater.c @@ -344,7 +344,40 @@ static void applyKeystroke(key_state_t *keyState, key_action_cached_t *cachedAct } } -static void applyConnectionAction(connection_action_t command, uint8_t hostConnectionIdx) +static void applySwitchHostPress(key_state_t* keyState, uint8_t hostConnectionIdx) +{ +#ifdef __ZEPHYR__ + const uint32_t unpairTimeout = 3000; + static key_state_t* inProgress = NULL; + static uint32_t startTime = 0; + uint32_t currentTime = Timer_GetCurrentTime(); + + if (KeyState_ActivatedNow(keyState)) { + inProgress = keyState; + startTime = currentTime; + UnNotify(); + EventScheduler_Schedule(startTime + unpairTimeout, EventSchedulerEvent_NativeActions, "unpair host timeout"); + } + + if (KeyState_DeactivatedNow(keyState)) { + if (inProgress == keyState && currentTime - startTime < unpairTimeout) { + inProgress = NULL; + HostConnections_SelectByHostConnIndex(hostConnectionIdx); + } + } + + if (KeyState_Active(keyState) && inProgress == keyState) { + if (currentTime - startTime >= unpairTimeout) { + inProgress = NULL; + HostConnections_ClearConnectionByConnId(hostConnectionIdx + ConnectionId_HostConnectionFirst); + } else { + EventScheduler_Schedule(startTime + unpairTimeout, EventSchedulerEvent_NativeActions, "unpair host timeout"); + } + } +#endif +} + +static void applyConnectionActionPress(connection_action_t command, uint8_t hostConnectionIdx) { #ifdef __ZEPHYR__ switch(command) { @@ -361,7 +394,7 @@ static void applyConnectionAction(connection_action_t command, uint8_t hostConne HostConnections_SelectLastSelectedConnection(); break; case ConnectionAction_SwitchByHostConnectionId: - HostConnections_SelectByHostConnIndex(hostConnectionIdx); + //handled elsewhere break; case ConnectionAction_ToggleAdvertisement: BtManager_EnterMode(PairingMode_Advertise, true); @@ -372,6 +405,16 @@ static void applyConnectionAction(connection_action_t command, uint8_t hostConne } #endif } +static void applyConnectionAction(key_state_t* keyState, connection_action_t command, uint8_t hostConnectionIdx) { + if (command == ConnectionAction_SwitchByHostConnectionId) { + applySwitchHostPress(keyState, hostConnectionIdx); + } else { + if (KeyState_ActivatedNow(keyState)) { + applyConnectionActionPress(command, hostConnectionIdx); + } + } +} + static void applyOtherAction(other_action_t actionSubtype) { @@ -428,9 +471,7 @@ void ApplyKeyAction(key_state_t *keyState, key_action_cached_t *cachedAction, ke } break; case KeyActionType_Connections: - if (KeyState_ActivatedNow(keyState)) { - applyConnectionAction(action->connections.command, action->connections.hostConnectionId); - } + applyConnectionAction(keyState, action->connections.command, action->connections.hostConnectionId); break; case KeyActionType_Other: if (KeyState_ActivatedNow(keyState)) { From de34add0c6c4e62a9cdddfad228e4b717d5ab499 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Wed, 14 Jan 2026 14:52:13 +0100 Subject: [PATCH 25/46] Ble slots: Preserve ble slots when parsing UserConfig. --- .../src/config_parser/parse_host_connection.c | 90 +++++++++++++++---- 1 file changed, 75 insertions(+), 15 deletions(-) diff --git a/right/src/config_parser/parse_host_connection.c b/right/src/config_parser/parse_host_connection.c index dd4f3ace2..fb30597c9 100644 --- a/right/src/config_parser/parse_host_connection.c +++ b/right/src/config_parser/parse_host_connection.c @@ -7,42 +7,100 @@ #include "config_parser/basic_types.h" #include "host_connection.h" #include "parse_config.h" +#include "bt_conn.h" +#include static parser_error_t parseHostConnection(config_buffer_t* buffer, host_connection_t* hostConnection) { - hostConnection->type = ReadUInt8(buffer); + host_connection_type_t hostType = ReadUInt8(buffer); // check validity of the type - if (hostConnection->type >= HostConnectionType_Count) { - hostConnection->type = HostConnectionType_Empty; - ConfigParser_Error(buffer, "Invalid host type: %d\n", hostConnection->type); + if (hostType >= HostConnectionType_Count) { + hostType = HostConnectionType_Empty; + ConfigParser_Error(buffer, "Invalid host type: %d\n", hostType); return ParserError_InvalidHostType; } - if (hostConnection->type == HostConnectionType_BtHid || hostConnection->type == HostConnectionType_Dongle) { - for (uint8_t i = 0; i < BLE_ADDRESS_LENGTH; i++) { - hostConnection->bleAddress.a.val[i] = ReadUInt8(buffer); + // Handle overwrite logic - don't overwrite unregistered slots by empty slots + if (hostType == HostConnectionType_Empty) { + if (hostConnection->type == HostConnectionType_UnregisteredBtHid) { + return ParserError_Success; + } else { + hostConnection->type = HostConnectionType_Empty; + return ParserError_Success; } - hostConnection->bleAddress.type = hostConnection->bleAddress.a.val[0] & 0x01; } - if (hostConnection->type != HostConnectionType_Empty) { - if (VERSION_AT_LEAST(DataModelVersion, 8, 3, 0)) { - hostConnection->switchover = ReadUInt8(buffer); + // Parse the connection + { + // Set type + hostConnection->type = (host_connection_type_t)hostType; + + // Set address + if (hostConnection->type == HostConnectionType_BtHid || hostConnection->type == HostConnectionType_Dongle) { + for (uint8_t i = 0; i < BLE_ADDRESS_LENGTH; i++) { + hostConnection->bleAddress.a.val[i] = ReadUInt8(buffer); + } + hostConnection->bleAddress.type = hostConnection->bleAddress.a.val[0] & 0x01; } - uint16_t len; - hostConnection->name.start = ReadString(buffer, &len); - hostConnection->name.end = hostConnection->name.start + len; + // Set name and switchover + if (hostConnection->type != HostConnectionType_Empty) { + if (VERSION_AT_LEAST(DataModelVersion, 8, 3, 0)) { + hostConnection->switchover = ReadUInt8(buffer); + } + + uint16_t len; + hostConnection->name.start = ReadString(buffer, &len); + hostConnection->name.end = hostConnection->name.start + len; + } } return ParserError_Success; } +static void deduplicateUnregisteredConnections(void) { +#ifdef __ZEPHYR__ + if (ParserRunDry) { + return; + } + + for (uint8_t i = 0; i < SERIALIZED_HOST_CONNECTION_COUNT_MAX; i++) { + host_connection_t* conn = &HostConnections[i]; + + if (conn->type != HostConnectionType_UnregisteredBtHid) { + continue; + } + + for (uint8_t j = 0; j < SERIALIZED_HOST_CONNECTION_COUNT_MAX; j++) { + if (i == j) { + continue; + } + + host_connection_t* other = &HostConnections[j]; + + if (other->type != HostConnectionType_BtHid && + other->type != HostConnectionType_Dongle && + other->type != HostConnectionType_UnregisteredBtHid) { + continue; + } + + if (BtAddrEq(&conn->bleAddress, &other->bleAddress)) { + conn->type = HostConnectionType_Empty; + memset(&conn->bleAddress, 0, sizeof(bt_addr_le_t)); + conn->name = (string_segment_t){ .start = NULL, .end = NULL }; + conn->switchover = false; + break; + } + } + } +#endif +} + parser_error_t ParseHostConnections(config_buffer_t *buffer) { int errorCode; for (uint8_t hostConnectionId = 0; hostConnectionId < SERIALIZED_HOST_CONNECTION_COUNT_MAX; hostConnectionId++) { - host_connection_t dummy; + host_connection_t dummy = { .type = HostConnectionType_Empty }; host_connection_t* hostConnection = &dummy; @@ -55,5 +113,7 @@ parser_error_t ParseHostConnections(config_buffer_t *buffer) { ); } + deduplicateUnregisteredConnections(); + return ParserError_Success; } From 81459f211f5d00a0cf89cb00c85daa112e3f16ef Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Tue, 30 Dec 2025 14:05:14 +0100 Subject: [PATCH 26/46] Ble: reason codes. --- device/src/bt_conn.c | 73 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index a94f1b84a..2548f07ff 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -47,8 +47,41 @@ struct bt_conn *auth_conn; #define BLE_KEY_LEN 16 #define BLE_ADDR_LEN 6 -#define BT_REASON_INTENTIONAL_DISCONNECT BT_HCI_ERR_REMOTE_USER_TERM_CONN -#define BT_REASON_UNSPECIFIED BT_HCI_ERR_UNSPECIFIED +/* + * Reason codes are named from the perspective of the party that receives them. + * + * BT_HCI_ERR_REMOTE_LOW_RESOURCES + * - it is our resources that are low + * + * BT_HCI_ERR_LOCALHOST_TERM_CONN + * - this is their decision + * - we never send this. + * - don't trust llms on it. + * - if we actually use it, the connection will not actually disconnect + * + * BT_HCI_ERR_REMOTE_USER_TERM_CONN + * - this is probably the right thing to send + * - bond should remain valid + * + * BT_HCI_ERR_AUTH_FAIL + * - permanent failure + * - bond should be removed + * + * LLM says that according to the core spec, following disconnect reasons are valid: + * + * Code: 0x08 Zephyr Constant: BT_HCI_ERR_AUTH_FAIL Meaning: Authentication failure - signals permanent bond problem + * Code: 0x13 Zephyr Constant: BT_HCI_ERR_REMOTE_USER_TERM_CONN Meaning: Standard disconnect - "I'm disconnecting you" + * Code: 0x14 Zephyr Constant: BT_HCI_ERR_REMOTE_LOW_RESOURCES Meaning: Low resources - signals temporary resource issue + * Code: 0x15 Zephyr Constant: BT_HCI_ERR_REMOTE_POWER_OFF Meaning: Power off - device is shutting down + * Code: 0x1A Zephyr Constant: BT_HCI_ERR_UNSUPP_REMOTE_FEATURE Meaning: Unsupported feature + * Code: 0x29 Zephyr Constant: BT_HCI_ERR_PAIRING_NOT_SUPPORTED Meaning: Pairing not supported + * Code: 0x3B Zephyr Constant: BT_HCI_ERR_UNACCEPT_CONN_PARAM + * */ + +#define BT_REASON_HID_GIVE_US_BREAK BT_HCI_ERR_REMOTE_USER_TERM_CONN +#define BT_REASON_NOT_SELECTED BT_HCI_ERR_REMOTE_USER_TERM_CONN +#define BT_REASON_PERMANENT BT_HCI_ERR_AUTH_FAIL +#define BT_REASON_UNSPECIFIED BT_HCI_ERR_REMOTE_USER_TERM_CONN static void disconnectAllHids(); @@ -268,14 +301,15 @@ static void configureLatency(struct bt_conn *conn, latency_mode_t latencyMode) { static void youAreNotWanted(struct bt_conn *conn) { static uint32_t lastAttemptTime = 0; + static bt_addr_le_t lastAddr = {0}; uint32_t currentTime = k_uptime_get_32(); - if (currentTime - lastAttemptTime < 2000) { + if (currentTime - lastAttemptTime < 1000 && BtAddrEq(bt_conn_get_dst(conn), &lastAddr)) { LOG_WRN("Refusing connenction %s (this is not a selected connection)(this is repeated attempt!)\n", GetPeerStringByConn(conn)); - safeDisconnect(conn, BT_REASON_INTENTIONAL_DISCONNECT); + safeDisconnect(conn, BT_REASON_HID_GIVE_US_BREAK); } else { - LOG_WRN("Refusing connenction %s (this is not a selected connection)\n", GetPeerStringByConn(conn)); - safeDisconnect(conn, BT_REASON_INTENTIONAL_DISCONNECT); + LOG_WRN("Refusing connenction %s (this is not a selected connection (%d))\n", GetPeerStringByConn(conn), SelectedHostConnectionId); + safeDisconnect(conn, BT_REASON_TEMPORARY); } LOG_INF(" Free peripheral slots: %d, Peripheral conn count: %d, bt pari mode: %d", BtConn_UnusedPeripheralConnectionCount(), @@ -284,6 +318,7 @@ static void youAreNotWanted(struct bt_conn *conn) { ); lastAttemptTime = currentTime; + lastAddr = *bt_conn_get_dst(conn); } ATTR_UNUSED static void youAreNotAuthenticated(struct bt_conn *conn) { @@ -386,7 +421,15 @@ static bool isWantedInHidPairingMode(struct bt_conn *conn, bool alreadyAuthentic bool isLeftConnection = connectionType == ConnectionType_NusLeft; bool isBonded = BtPair_IsDeviceBonded(bt_conn_get_dst(conn)); - return isLeftConnection || !isBonded || !alreadyAuthenticated; + bool result = isLeftConnection || !isBonded || !alreadyAuthenticated; + + if (!result) { + LOG_INF(" Not wanted in HID pairing mode: isLeft: %d, isBonded: %d, alreadyAuth: %d", isLeftConnection, isBonded, alreadyAuthenticated); + } else { + LOG_INF(" Is wanted in HID pairing mode: isLeft: %d, isBonded: %d, alreadyAuth: %d", isLeftConnection, isBonded, alreadyAuthenticated); + } + + return result; } static bool isWantedInNormalMode(struct bt_conn *conn, connection_id_t connectionId, connection_type_t connectionType) { @@ -502,7 +545,7 @@ ATTR_UNUSED static uint8_t discover_func(struct bt_conn *conn, const struct bt_g if (!isPairedConnection && DEVICE_IS_UHK80_RIGHT) { LOG_INF("Unknown NUS trying to connect. Refusing!\n"); - safeDisconnect(conn, BT_REASON_INTENTIONAL_DISCONNECT); + safeDisconnect(conn, BT_REASON_PERMANENT); } return BT_GATT_ITER_STOP; } @@ -658,7 +701,7 @@ static void connectAuthenticatedConnection(struct bt_conn *conn, connection_id_t break; case ConnectionType_Unknown: default: - LOG_WRN("Authenticated connection is not known. Disconnecting %s", GetPeerStringByConn(conn)); + LOG_ERR("Authenticated connection is not known. Disconnecting %s", GetPeerStringByConn(conn)); safeDisconnect(conn, BT_HCI_ERR_AUTH_FAIL); BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in authenticatedConnection - is unknown"); break; @@ -675,7 +718,7 @@ static void securityChanged(struct bt_conn *conn, bt_security_t level, enum bt_s int err = bt_conn_get_info(conn, &info); if (err == 0 && info.state == BT_CONN_STATE_CONNECTED) { bt_conn_auth_cancel(conn); - // bt_conn_disconnect(conn, BT_REASON_INTENTIONAL_DISCONNECT); + // bt_conn_disconnect(conn, BT_REASON_PERMANENT); } else { // Sometimes securityChanged gets called twice, resulting in a race and a crash, so check for it \efp. LOG_WRN("The connection (%s) isn't even connected! Ignoring.", GetPeerStringByConn(conn)); @@ -845,7 +888,7 @@ static void pairing_complete(struct bt_conn *conn, bool bonded) { } static void bt_foreach_conn_cb(struct bt_conn *conn, void *user_data) { - safeDisconnect(conn, BT_REASON_INTENTIONAL_DISCONNECT); + safeDisconnect(conn, BT_REASON_NOT_SELECTED); // gpt says you should unref here. Don't believe it! } @@ -863,14 +906,14 @@ void BtConn_DisconnectOne(connection_id_t connectionId) { if (!conn) { return; } - safeDisconnect(conn, BT_REASON_INTENTIONAL_DISCONNECT); + safeDisconnect(conn, BT_REASON_NOT_SELECTED); } static void bt_foreach_conn_cb_disconnect_unidentified(struct bt_conn *conn, void *user_data) { peer_t* peer = getPeerByConn(conn); if (!peer) { LOG_INF(" disconnecting unassigned connection %s\n", GetPeerStringByConn(conn)); - safeDisconnect(conn, BT_REASON_INTENTIONAL_DISCONNECT); + safeDisconnect(conn, BT_REASON_NOT_SELECTED); } } @@ -972,14 +1015,14 @@ ATTR_UNUSED static void disconnectOldestHost() { if (oldestPeerId != PeerIdUnknown) { LOG_INF("Disconnecting oldest host %d\n", oldestPeerId); - safeDisconnect(Peers[oldestPeerId].conn, BT_REASON_INTENTIONAL_DISCONNECT); + safeDisconnect(Peers[oldestPeerId].conn, BT_REASON_NOT_SELECTED); } } ATTR_UNUSED static void disconnectAllHids() { for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { if (Peers[peerId].conn && Connections_Type(Peers[peerId].connectionId) == ConnectionType_BtHid) { - safeDisconnect(Peers[peerId].conn, BT_REASON_INTENTIONAL_DISCONNECT); + safeDisconnect(Peers[peerId].conn, BT_REASON_TEMPORARY); } } } From f96133156eed371237e432f3bda227721cb7b13c Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Thu, 8 Jan 2026 16:58:59 +0100 Subject: [PATCH 27/46] Ble: restore advertising state after cancelled switch. Cancel timeout. --- device/src/bt_conn.c | 5 +++-- right/src/event_scheduler.c | 3 +++ right/src/event_scheduler.h | 1 + right/src/host_connection.c | 17 ++++++++++++++++- right/src/host_connection.h | 2 ++ right/src/stubs.c | 1 + right/src/stubs.h | 1 + 7 files changed, 27 insertions(+), 3 deletions(-) diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 2548f07ff..a561c7bfc 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -80,6 +80,7 @@ struct bt_conn *auth_conn; #define BT_REASON_HID_GIVE_US_BREAK BT_HCI_ERR_REMOTE_USER_TERM_CONN #define BT_REASON_NOT_SELECTED BT_HCI_ERR_REMOTE_USER_TERM_CONN +#define BT_REASON_TEMPORARY BT_HCI_ERR_REMOTE_USER_TERM_CONN #define BT_REASON_PERMANENT BT_HCI_ERR_AUTH_FAIL #define BT_REASON_UNSPECIFIED BT_HCI_ERR_REMOTE_USER_TERM_CONN @@ -850,7 +851,7 @@ static void pairing_complete(struct bt_conn *conn, bool bonded) { BtPair_EndPairing(true, "Successfuly bonded!"); // Disconnect it so that the connection is established only after it is identified as a host connection - bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + bt_conn_disconnect(conn, BT_REASON_TEMPORARY); } else { BtPair_EndPairing(true, "Successfuly bonded!"); @@ -1022,7 +1023,7 @@ ATTR_UNUSED static void disconnectOldestHost() { ATTR_UNUSED static void disconnectAllHids() { for (uint8_t peerId = PeerIdFirstHost; peerId <= PeerIdLastHost; peerId++) { if (Peers[peerId].conn && Connections_Type(Peers[peerId].connectionId) == ConnectionType_BtHid) { - safeDisconnect(Peers[peerId].conn, BT_REASON_TEMPORARY); + safeDisconnect(Peers[peerId].conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); } } } diff --git a/right/src/event_scheduler.c b/right/src/event_scheduler.c index c01c49446..8b1690c80 100644 --- a/right/src/event_scheduler.c +++ b/right/src/event_scheduler.c @@ -150,6 +150,9 @@ static void processEvt(event_scheduler_event_t evt) case EventSchedulerEvent_BlinkStatusIcons: WIDGET_REFRESH(&StatusWidget); break; + case EventSchedulerEvent_UnselectHostConnection: + HostConnection_Unselect(); + break; default: return; } diff --git a/right/src/event_scheduler.h b/right/src/event_scheduler.h index 4fd62e7c2..1ffb48f85 100644 --- a/right/src/event_scheduler.h +++ b/right/src/event_scheduler.h @@ -46,6 +46,7 @@ EventSchedulerEvent_CheckDongleProtocolVersion, EventSchedulerEvent_PutBackToShutDown, EventSchedulerEvent_BlinkStatusIcons, + EventSchedulerEvent_UnselectHostConnection, EventSchedulerEvent_Count } event_scheduler_event_t; diff --git a/right/src/host_connection.c b/right/src/host_connection.c index d670404d3..80bd4b378 100644 --- a/right/src/host_connection.c +++ b/right/src/host_connection.c @@ -1,7 +1,9 @@ #include "host_connection.h" +#include "event_scheduler.h" #include "macros/status_buffer.h" #include "str_utils.h" #include "macros/string_reader.h" +#include "timer.h" #ifdef __ZEPHYR__ #include "bt_conn.h" @@ -11,6 +13,10 @@ #include "stubs.h" #include "debug.h" #include "bt_pair.h" +#include "bt_manager.h" + + +#define HOST_CONNECTION_SELECT_TIMEOUT 30000 host_connection_t HostConnections[HOST_CONNECTION_COUNT_MAX] = { [HOST_CONNECTION_COUNT_MAX - 2] = { @@ -75,13 +81,22 @@ void HostConnection_SetSelectedConnection(uint8_t connectionId) { } } +void HostConnection_Unselect() { + HostConnection_SetSelectedConnection(ConnectionId_Invalid); + BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in HostConnection_Unselect"); +} + static void selectConnection(uint8_t connectionId) { if (Connections_IsReady(connectionId)) { + printk("Selecting ready host connection %d\n", connectionId); Connections_HandleSwitchover(connectionId, true); - HostConnection_SetSelectedConnection(ConnectionId_Invalid); + HostConnection_Unselect(); } else { + printk("Selecting not ready host connection %d\n", connectionId); HostConnection_SetSelectedConnection(connectionId); BtConn_ReserveConnections(); + EventScheduler_Reschedule(Timer_GetCurrentTime() + HOST_CONNECTION_SELECT_TIMEOUT, EventSchedulerEvent_UnselectHostConnection, "Unselect host connection timeout"); + } Connections_ReportState(connectionId); LastSelectedHostConnectionId = connectionId; diff --git a/right/src/host_connection.h b/right/src/host_connection.h index 31109807d..f1c60d936 100644 --- a/right/src/host_connection.h +++ b/right/src/host_connection.h @@ -79,6 +79,8 @@ void HostConnections_SelectByName(parser_context_t* ctx); void HostConnection_SetSelectedConnection(uint8_t connectionId); + void HostConnection_Unselect(); + uint8_t HostConnections_NameToConnId(parser_context_t* ctx); void HostConnections_ClearConnectionByConnId(uint8_t connectionId); diff --git a/right/src/stubs.c b/right/src/stubs.c index 0d38cb339..0140fdfeb 100644 --- a/right/src/stubs.c +++ b/right/src/stubs.c @@ -39,3 +39,4 @@ ATTRS void PowerMode_PutBackToSleepMaybe(void) {}; ATTRS void BtAdvertise_DisableAdvertisingIcon(void) {}; ATTRS void NotificationScreen_NotifyFor(const char* message, uint16_t duration) {}; + ATTRS void HostConnection_Unselect() {}; diff --git a/right/src/stubs.h b/right/src/stubs.h index a7c5ae513..e1f54b048 100644 --- a/right/src/stubs.h +++ b/right/src/stubs.h @@ -59,6 +59,7 @@ extern void PowerMode_PutBackToSleepMaybe(void); extern void BtAdvertise_DisableAdvertisingIcon(void); extern void NotificationScreen_NotifyFor(const char* message, uint16_t duration); + extern void HostConnection_Unselect(); #if DEVICE_HAS_OLED #define WIDGET_REFRESH(W) Widget_Refresh(W) From 9ea3546ba24b39842391de78f123ff1028f1ee98 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Thu, 8 Jan 2026 17:00:11 +0100 Subject: [PATCH 28/46] Ble: bluetooth pairing notifications. --- device/src/bt_conn.c | 13 ++++++++-- .../keyboard/oled/screens/pairing_screen.c | 24 +++++++++---------- .../keyboard/oled/screens/pairing_screen.h | 3 +-- .../keyboard/oled/screens/screen_manager.c | 8 ------- .../keyboard/oled/screens/screen_manager.h | 2 -- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index a561c7bfc..7580c1b83 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -21,6 +21,7 @@ #include "nus_server.h" #include "device.h" #include "keyboard/oled/screens/pairing_screen.h" +#include "keyboard/oled/screens/screen_manager.h" #include "usb/usb.h" #include "keyboard/oled/widgets/widgets.h" #include @@ -37,7 +38,6 @@ #include "bt_health.h" #include "macros/status_buffer.h" - LOG_MODULE_REGISTER(Bt, LOG_LEVEL_INF); bool Bt_NewPairedDevice = false; @@ -85,6 +85,7 @@ struct bt_conn *auth_conn; #define BT_REASON_UNSPECIFIED BT_HCI_ERR_REMOTE_USER_TERM_CONN static void disconnectAllHids(); +static void auth_cancel(struct bt_conn *conn); peer_t Peers[PeerCount] = { { @@ -172,6 +173,12 @@ static void safeDisconnect(struct bt_conn *conn, int reason) { if (conn == auth_conn) { LOG_INF("Unauthenticating %s\n", GetPeerStringByConn(conn)); unsetAuthConn(true); + + #if DEVICE_HAS_OLED + if (ActiveScreen == ScreenId_Pairing) { + PairingScreen_Cancel(); + } + #endif } else { struct bt_conn_info info; int err = bt_conn_get_info(conn, &info); @@ -936,6 +943,8 @@ static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason) { PairingScreen_Feedback(false); } + safeDisconnect(conn, BT_REASON_PERMANENT); + LOG_WRN("Pairing failed: %s, reason %d\n", GetPeerStringByConn(conn), reason); } @@ -972,7 +981,7 @@ void num_comp_reply(int passkey) { struct bt_conn *conn; #if DEVICE_HAS_OLED - ScreenManager_SwitchScreenEvent(); + NotificationScreen_NotifyFor("Pairing...", 10000); #endif if (!auth_conn) { diff --git a/device/src/keyboard/oled/screens/pairing_screen.c b/device/src/keyboard/oled/screens/pairing_screen.c index daca6838a..c5c637d41 100644 --- a/device/src/keyboard/oled/screens/pairing_screen.c +++ b/device/src/keyboard/oled/screens/pairing_screen.c @@ -8,17 +8,13 @@ #include "bt_conn.h" #include "screen_manager.h" #include "key_action.h" +#include "macros/status_buffer.h" static widget_t splitterWidget; static widget_t questionLine; static widget_t answerLine; -static widget_t pairingFailed; -static widget_t pairingSucceeded; - widget_t* PairingScreen; -widget_t* PairingSucceededScreen; -widget_t* PairingFailedScreen; static uint8_t passwordCharCount = 0; static uint8_t password[PASSWORD_LENGTH]; @@ -62,7 +58,17 @@ static void registerPasswordDigit(uint8_t digit) void PairingScreen_Feedback(bool success) { - ScreenManager_ActivateScreen(success ? ScreenId_PairingSucceeded : ScreenId_PairingFailed); + if (success) { + NotifyPrintf("Pairing succeeded!"); + } else { + NotifyPrintf("Pairing failed!"); + } +} + +void PairingScreen_Cancel(void) +{ + passwordCharCount = 0; + ScreenManager_SwitchScreenEvent(); } const rgb_t* PairingScreen_ActionColor(key_action_t* action) { @@ -135,10 +141,4 @@ void PairingScreen_Init(void) answerLine = TextWidget_Build(&JetBrainsMono16, passwordTextBuffer); splitterWidget = SplitterWidget_BuildVertical(&questionLine, &answerLine, (DISPLAY_HEIGHT-DISPLAY_SHIFTING_MARGIN)/2, false); PairingScreen = &splitterWidget; - - pairingFailed = TextWidget_Build(&JetBrainsMono16, "Pairing failed!"); - PairingFailedScreen = &pairingFailed; - - pairingSucceeded = TextWidget_Build(&JetBrainsMono16, "Pairing succeeded!"); - PairingSucceededScreen = &pairingSucceeded; } diff --git a/device/src/keyboard/oled/screens/pairing_screen.h b/device/src/keyboard/oled/screens/pairing_screen.h index e0d4a2e8a..f3c84deda 100644 --- a/device/src/keyboard/oled/screens/pairing_screen.h +++ b/device/src/keyboard/oled/screens/pairing_screen.h @@ -17,8 +17,6 @@ // Variables: extern widget_t* PairingScreen; - extern widget_t* PairingSucceededScreen; - extern widget_t* PairingFailedScreen; // Functions: @@ -26,6 +24,7 @@ void PairingScreen_RegisterScancode(uint8_t scancode); void PairingScreen_AskForPassword(void); void PairingScreen_Feedback(bool success); + void PairingScreen_Cancel(void); const rgb_t* PairingScreen_ActionColor(key_action_t* action); #endif diff --git a/device/src/keyboard/oled/screens/screen_manager.c b/device/src/keyboard/oled/screens/screen_manager.c index 4560d1113..3638f2d9d 100644 --- a/device/src/keyboard/oled/screens/screen_manager.c +++ b/device/src/keyboard/oled/screens/screen_manager.c @@ -47,14 +47,6 @@ void ScreenManager_ActivateScreen(screen_id_t screen) case ScreenId_Main: screenPtr = MainScreen; break; - case ScreenId_PairingSucceeded: - screenPtr = PairingSucceededScreen; - EventScheduler_Schedule(Timer_GetCurrentTime() + SCREEN_NOTIFICATION_TIMEOUT, EventSchedulerEvent_SwitchScreen, "ScreenManager - switch to main screen"); - break; - case ScreenId_PairingFailed: - screenPtr = PairingFailedScreen; - EventScheduler_Schedule(Timer_GetCurrentTime() + SCREEN_NOTIFICATION_TIMEOUT, EventSchedulerEvent_SwitchScreen, "ScreenManager - switch to main screen"); - break; case ScreenId_Canvas: EventScheduler_Reschedule(Timer_GetCurrentTime() + CANVAS_TIMEOUT, EventSchedulerEvent_SwitchScreen, "ScreenManager - switch to main screen"); screenPtr = CanvasScreen; diff --git a/device/src/keyboard/oled/screens/screen_manager.h b/device/src/keyboard/oled/screens/screen_manager.h index b0f85ca49..80d4e09fd 100644 --- a/device/src/keyboard/oled/screens/screen_manager.h +++ b/device/src/keyboard/oled/screens/screen_manager.h @@ -17,8 +17,6 @@ ScreenId_Main, ScreenId_Debug, ScreenId_Pairing, - ScreenId_PairingFailed, - ScreenId_PairingSucceeded, ScreenId_Notification, ScreenId_Canvas, } screen_id_t; From 0f638741b374e806b05887c99bf8fcd435280898 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Thu, 8 Jan 2026 17:56:38 +0100 Subject: [PATCH 29/46] Implement exponential backoff when aggresive devices turn up. --- device/src/bt_conn.c | 16 ++++++++-------- device/src/bt_manager.c | 25 +++++++++++++++++++++---- device/src/bt_manager.h | 2 +- device/src/bt_pair.c | 4 ++-- device/src/device_state.c | 2 +- right/src/host_connection.c | 2 +- right/src/macros/set_command.c | 4 ++-- 7 files changed, 36 insertions(+), 19 deletions(-) diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 7580c1b83..c7f96a34e 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -526,7 +526,7 @@ static void connectHid(struct bt_conn *conn, connection_id_t connectionId, conne // Assume that HOGP is ready LOG_INF("Established HID connection with %s\n", GetPeerStringByConn(conn)); Connections_SetState(connectionId, ConnectionState_Ready); - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in connectHid"); + BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in connectHid"); } #define BT_UUID_NUS_VAL BT_UUID_128_ENCODE(0x6e400001, 0xb5a3, 0xf393, 0xe0a9, 0xe50e24dcca9e) @@ -610,7 +610,7 @@ static void connected(struct bt_conn *conn, uint8_t err) { if (connectionId == ConnectionId_Invalid) { connectUnknown(conn); - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in connected - invalid connection"); + BtManager_StartScanningAndAdvertisingAsync(true, "StartScanningAndAdvertisingAsync in connected - invalid connection"); } else { if (isWanted(conn, false, connectionId, connectionType)) { @@ -618,7 +618,7 @@ static void connected(struct bt_conn *conn, uint8_t err) { // advertising/scanning needs to be started only after peers are assigned :-/ } else { youAreNotWanted(conn); - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in connected - they are not wanted"); + BtManager_StartScanningAndAdvertisingAsync(true, "StartScanningAndAdvertisingAsync in connected - they are not wanted"); } } @@ -662,7 +662,7 @@ static void disconnected(struct bt_conn *conn, uint8_t reason) { } if (!BtManager_Restarting) { - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in disconnected"); + BtManager_StartScanningAndAdvertisingAsync(true, "StartScanningAndAdvertisingAsync in disconnected"); } if (conn == auth_conn) { @@ -675,7 +675,7 @@ void Bt_SetConnectionConfigured(struct bt_conn* conn) { uint8_t peerId = GetPeerIdByConn(conn); if (Connections[Peers[peerId].connectionId].state != ConnectionState_Ready) { Connections_SetState(Peers[peerId].connectionId, ConnectionState_Ready); - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in SetConnectionConfigured"); + BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in SetConnectionConfigured"); } } @@ -711,7 +711,7 @@ static void connectAuthenticatedConnection(struct bt_conn *conn, connection_id_t default: LOG_ERR("Authenticated connection is not known. Disconnecting %s", GetPeerStringByConn(conn)); safeDisconnect(conn, BT_HCI_ERR_AUTH_FAIL); - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in authenticatedConnection - is unknown"); + BtManager_StartScanningAndAdvertisingAsync(true, "StartScanningAndAdvertisingAsync in authenticatedConnection - is unknown"); break; } } @@ -892,7 +892,7 @@ static void pairing_complete(struct bt_conn *conn, bool bonded) { PairingScreen_Feedback(true); } - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in pairing_complete"); + BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in pairing_complete"); } static void bt_foreach_conn_cb(struct bt_conn *conn, void *user_data) { @@ -1060,7 +1060,7 @@ void BtConn_ReserveConnections() { disconnectOldestHost(); // Advertising will get started when the host actually gets disconnected } else { - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in ReserveConnections"); + BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in ReserveConnections"); } WIDGET_REFRESH(&TargetWidget); } else { diff --git a/device/src/bt_manager.c b/device/src/bt_manager.c index 391a1690e..9531f3a55 100644 --- a/device/src/bt_manager.c +++ b/device/src/bt_manager.c @@ -88,11 +88,28 @@ void BtManager_StopBt() { } -void BtManager_StartScanningAndAdvertisingAsync(const char* eventLabel) { +void BtManager_StartScanningAndAdvertisingAsync(bool wasAggresive, const char* eventLabel) { BT_TRACE_AND_ASSERT("bm4"); - uint32_t delay = 50; - LOG_INF("btManager: BtManager_StartScanningAndAdvertisingAsync because %s\n", eventLabel); - EventScheduler_Reschedule(Timer_GetCurrentTime() + delay, EventSchedulerEvent_BtStartScanningAndAdvertising, eventLabel); + uint32_t maxDelay = 5000; + uint32_t minDelay = 100; + uint32_t expDelay; + static int8_t aggressiveTries = 0; + + bool weArePairing = BtPair_PairingMode == PairingMode_Oob || BtPair_PairingMode == PairingMode_PairHid; + bool weAreSwitching = SelectedHostConnectionId != ConnectionId_Invalid; + + if (weArePairing || weAreSwitching) { + expDelay = minDelay; + } else { + aggressiveTries = wasAggresive ? aggressiveTries + 1 : 0; + aggressiveTries = MAX(0, aggressiveTries); + aggressiveTries = MIN(aggressiveTries, 16); + + expDelay = MIN(maxDelay, minDelay << aggressiveTries); + } + + LOG_INF("btManager: BtManager_StartScanningAndAdvertisingAsync because %s, delay %d\n", eventLabel, expDelay); + EventScheduler_Reschedule(Timer_GetCurrentTime() + expDelay, EventSchedulerEvent_BtStartScanningAndAdvertising, eventLabel); } /* diff --git a/device/src/bt_manager.h b/device/src/bt_manager.h index 7b8325f34..46d0c9de9 100644 --- a/device/src/bt_manager.h +++ b/device/src/bt_manager.h @@ -24,7 +24,7 @@ void BtManager_StopBt(); void BtManager_RestartBt(); void BtManager_StartScanningAndAdvertising(); - void BtManager_StartScanningAndAdvertisingAsync(const char* eventLabel); + void BtManager_StartScanningAndAdvertisingAsync(bool wasAggresive, const char* eventLabel); void BtManager_EnterMode(pairing_mode_t mode, bool toggle); #endif // __BT_MANAGER_H__ diff --git a/device/src/bt_pair.c b/device/src/bt_pair.c index 797ad0288..003edfd0d 100644 --- a/device/src/bt_pair.c +++ b/device/src/bt_pair.c @@ -87,7 +87,7 @@ void BtManager_EnterMode(pairing_mode_t mode, bool toggle) { if (mode == PairingMode_PairHid) { BtConn_MakeSpaceForHid(); } - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in BtManager_EnterMode - start advertising"); + BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in BtManager_EnterMode - start advertising"); if (mode != defaultMode) { EventScheduler_Reschedule(k_uptime_get_32() + USER_PAIRING_TIMEOUT, EventSchedulerEvent_EndBtPairing, "User pairing mode timeout."); } @@ -156,7 +156,7 @@ void BtPair_EndPairing(bool success, const char* msg) { BtAdvertise_Stop(); #endif - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in BtPair_EndPairing"); + BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in BtPair_EndPairing"); } struct delete_args_t { diff --git a/device/src/device_state.c b/device/src/device_state.c index f37b27eba..cba3fc885 100644 --- a/device/src/device_state.c +++ b/device/src/device_state.c @@ -54,7 +54,7 @@ void handleStateTransition(connection_target_t remote, connection_id_t connectio EventVector_WakeMain(); } - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in handleStateTransition - left was disconnected"); + BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in handleStateTransition - left was disconnected"); } break; case ConnectionTarget_Host: diff --git a/right/src/host_connection.c b/right/src/host_connection.c index 80bd4b378..cea04fff7 100644 --- a/right/src/host_connection.c +++ b/right/src/host_connection.c @@ -83,7 +83,7 @@ void HostConnection_SetSelectedConnection(uint8_t connectionId) { void HostConnection_Unselect() { HostConnection_SetSelectedConnection(ConnectionId_Invalid); - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in HostConnection_Unselect"); + BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in HostConnection_Unselect"); } static void selectConnection(uint8_t connectionId) { diff --git a/right/src/macros/set_command.c b/right/src/macros/set_command.c index 315d039a8..0b1719bcd 100644 --- a/right/src/macros/set_command.c +++ b/right/src/macros/set_command.c @@ -369,12 +369,12 @@ static macro_variable_t bluetooth(parser_context_t* ctx, set_command_action_t ac ASSIGN_BOOL(Cfg.Bt_AlwaysAdvertiseHid); #if DEVICE_IS_UHK80_RIGHT BtManager_EnterMode(PairingMode_Default, false); - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in set_command - alwaysAdvertiseHid changed"); + BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in set_command - alwaysAdvertiseHid changed"); #endif } else if (ConsumeToken(ctx, "directedAdvertisingAllowed")) { ASSIGN_BOOL(Cfg.Bt_DirectedAdvertisingAllowed); #ifdef __ZEPHYR__ - BtManager_StartScanningAndAdvertisingAsync("StartScanningAndAdvertisingAsync in set_command - directedAdvertisingAllowed changed"); + BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in set_command - directedAdvertisingAllowed changed"); #endif } else { Macros_ReportErrorTok(ctx, "Parameter not recognized:"); From cd8333fcd76abe87621f5a1e272a93c4cd5fc87c Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 12 Jan 2026 15:35:27 +0100 Subject: [PATCH 30/46] DOCS: testing scenarios, notes --- doc-dev/other/testing-scenarios.md | 36 ++++++++++++++++++++++++++++++ doc-dev/other/testing/bluetooth.md | 8 +++++++ doc-dev/other/zephyr_logging.md | 24 ++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 doc-dev/other/testing-scenarios.md create mode 100644 doc-dev/other/testing/bluetooth.md create mode 100644 doc-dev/other/zephyr_logging.md diff --git a/doc-dev/other/testing-scenarios.md b/doc-dev/other/testing-scenarios.md new file mode 100644 index 000000000..b82aac755 --- /dev/null +++ b/doc-dev/other/testing-scenarios.md @@ -0,0 +1,36 @@ +Following is a list testing suggestions. Not all have to be tested each time. + +## Bluetooth: + +- Pair ble hid. Unpair. Pair again. Save it into connections. Make sure the Agent doesn't offer saving the same device multiple times. +- When two ble hid devices are paired, test that we can `switchHost` between them. +- When a device is paired, check that we can disconnect it and connect it again - i.e., that the bonding information didn't get corrupted. (Optionally, throw in uhk restart, bluetooth restart, test this with multiple devices.) +- Test that we can switch between two dongles and that their leds light up accordingly. +- Issue `switchHost device` against a device that is not present. `switchHost` to current usb. See that uhk now advertises hid (pairing icon) and that a ble hid host can connect. +- Test that we can add a paired device into the host connections. Try pair and add multiple devices within one Agent session. +- Unpair+pair dongle. (Optionally, let a bluetooth hid active while doing so.) +- Pair two ble hids using `bluetooth pair` while all devices are active. + +## Usb suspend: + +In all scenarios make sure that uhk works afterwards. + +Test with a pc and with a mac separately. + +- suspend and wake the pc when connected via usb, via pc's button(s). +- suspend and wake the pc when connected via usb, by tapping any key. +- suspend and wake the pc when connected via usb, by tapping the wake key. +- suspend and wake the pc when connected via ble, via pc's button(s). + +## Troubleshooting: + +- Use the `panic` macro to check that crash logs are generated as expected. (`set devMode true` is needed on uhk80) + +## Macros: + +These are obscure scenarios that are not worth to test often, but may be good to test from time to time. + +- test `oneShot` modifiers with multiple oneShot keys. When keystrokeDelay = 250ms and oneshotTimeout = 5000, you have to see modifiers release only after the oneShot key is pressed. +- test `overlayLayer` - test that overlaid keys are working as expected, are in the right places and that their neighbours didn't get affected. + +- diff --git a/doc-dev/other/testing/bluetooth.md b/doc-dev/other/testing/bluetooth.md new file mode 100644 index 000000000..4be32b1d1 --- /dev/null +++ b/doc-dev/other/testing/bluetooth.md @@ -0,0 +1,8 @@ +Things to test: + +- Pair ble hid. Unpair. Pair again. +- When two ble hid devices are paired, test that we can `switchHost` between them. +- When a device is paired, check that we can disconnect it and connect it again - i.e., that the bonding information didn't get corrupted. (Optionally, throw in uhk restart, bluetooth restart, test this with multiple devices.) +- Test that we can switch between two dongles and that their leds light up accordingly. +- Issue `switchHost device` against a device that is not present. `switchHost` to current usb. See that uhk now advertises hid (pairing icon) and that a ble hid host can connect. +- Test that we can add a paired device into the host connections. Try pair and add multiple devices within one Agent session. diff --git a/doc-dev/other/zephyr_logging.md b/doc-dev/other/zephyr_logging.md new file mode 100644 index 000000000..c9ad46da9 --- /dev/null +++ b/doc-dev/other/zephyr_logging.md @@ -0,0 +1,24 @@ +# Crash logs - users + +## Uhk80 + +Crash logs have to be enabled via `set devMode true` in `$onInit` + +## Uhk60 + +Crash logs are enabled by default. However, `set devMode true` is suggested as it enables logging in cases of ESD events. + +(We don't log esd reboots because they are often and these crash logs are significantly more disturbing than a quick reboot.) + +# Developer notes + +In order to fit all our uart applications onto two controllers, we use the async uart driver. Unfortunatelly, the async serial backend is significantly less reliable than the default interrupt backend. Especially it is not available at all during early boot phases. When these log are needed, the interrupt driver has to be used: + +- Comment out pin wiring stuff in device/src/main.c. + +- Comment out this in uhk-80-right.conf: + +``` +# CONFIG_SHELL_BACKEND_SERIAL_API_ASYNC=y +``` + From 8497742e385b936eb9a058c883550d5586fbc381 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Sat, 17 Jan 2026 15:19:22 +0100 Subject: [PATCH 31/46] Inline tracing. --- right/src/trace.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/right/src/trace.c b/right/src/trace.c index 04b1da13a..c194ecbff 100644 --- a/right/src/trace.c +++ b/right/src/trace.c @@ -39,6 +39,16 @@ void Trace(char a) { } } +void Trace_Printc(const char* s) { + if (enabled) { + StateWormhole.traceBuffer.eventVector = EventScheduler_Vector; + for (uint16_t i = 0; s[i] != '\0' && s[i] < 127; i++) { + TraceBuffer[TraceBufferPosition] = a; + TraceBufferPosition = (TraceBufferPosition + 1) % TRACE_BUFFER_SIZE; + } + } +} + void Trace_Printf(const char *fmt, ...) { if (enabled) { EXPAND_STRING(buffer, TRACE_BUFFER_SIZE); @@ -56,21 +66,6 @@ void Trace_Printf(const char *fmt, ...) { } } -void Trace_Printc(const char* s) { - if (enabled) { - for (uint16_t i = 0; s[i] != '\0'; i++) { - if (s[i] == '\0' || s[i] > 126) { - break; - } - if (s[i] == '\n') { - Trace(' '); - continue; - } - Trace(s[i]); - } - } -} - void Trace_Init(void) { for (uint16_t i = 0; i < TRACE_BUFFER_SIZE; i++) { if (TraceBuffer[i] < 32 || TraceBuffer[i] > 126) { From 2e8d9f0cb0b36d027543842e027926a978b5ccdf Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Sat, 17 Jan 2026 15:44:52 +0100 Subject: [PATCH 32/46] Optimization: inline trace calls. --- right/src/stubs.h | 3 --- right/src/trace.c | 57 +++------------------------------------- right/src/trace.h | 59 ++++++++++++++++++++++++++++++------------ right/src/trace_defs.h | 21 +++++++++++++++ right/src/wormhole.h | 2 +- 5 files changed, 67 insertions(+), 75 deletions(-) create mode 100644 right/src/trace_defs.h diff --git a/right/src/stubs.h b/right/src/stubs.h index e1f54b048..17ea4c5d3 100644 --- a/right/src/stubs.h +++ b/right/src/stubs.h @@ -53,9 +53,6 @@ extern void PairingScreen_Feedback(bool success); extern void StateSync_CheckFirmwareVersions(); extern void StateSync_CheckDongleProtocolVersion(); - extern void Trace(char a); - extern void Trace_Printc(const char *s); - extern void Trace_Printf(const char *fmt, ...); extern void PowerMode_PutBackToSleepMaybe(void); extern void BtAdvertise_DisableAdvertisingIcon(void); extern void NotificationScreen_NotifyFor(const char* message, uint16_t duration); diff --git a/right/src/trace.c b/right/src/trace.c index c194ecbff..6b5d8af1b 100644 --- a/right/src/trace.c +++ b/right/src/trace.c @@ -1,11 +1,7 @@ #include "trace.h" -#include -#include #include #include "macros/status_buffer.h" #include "trace_reasons.h" -#include "wormhole.h" -#include "event_scheduler.h" #include "logger.h" #include "versioning.h" @@ -17,54 +13,7 @@ #define ProxyLog_IsInPanicMode false #endif -static bool enabled = true; - -#define EXPAND_STRING(BUFFER, MAX_LOG_LENGTH) \ -char BUFFER[MAX_LOG_LENGTH]; \ -{ \ - va_list myargs; \ - va_start(myargs, fmt); \ - BUFFER[MAX_LOG_LENGTH-1] = '\0'; \ - vsnprintf(BUFFER, MAX_LOG_LENGTH-1, fmt, myargs); \ -} - -#define TraceBuffer StateWormhole.traceBuffer.data -#define TraceBufferPosition StateWormhole.traceBuffer.position - -void Trace(char a) { - if (enabled) { - StateWormhole.traceBuffer.eventVector = EventScheduler_Vector; - TraceBuffer[TraceBufferPosition] = a; - TraceBufferPosition = (TraceBufferPosition + 1) % TRACE_BUFFER_SIZE; - } -} - -void Trace_Printc(const char* s) { - if (enabled) { - StateWormhole.traceBuffer.eventVector = EventScheduler_Vector; - for (uint16_t i = 0; s[i] != '\0' && s[i] < 127; i++) { - TraceBuffer[TraceBufferPosition] = a; - TraceBufferPosition = (TraceBufferPosition + 1) % TRACE_BUFFER_SIZE; - } - } -} - -void Trace_Printf(const char *fmt, ...) { - if (enabled) { - EXPAND_STRING(buffer, TRACE_BUFFER_SIZE); - - for (uint16_t i = 0; i < TRACE_BUFFER_SIZE; i++) { - if (buffer[i] == '\0' || buffer[i] > 126) { - break; - } - if (buffer[i] == '\n') { - Trace(' '); - continue; - } - Trace(buffer[i]); - } - } -} +bool Trace_Enabled = true; void Trace_Init(void) { for (uint16_t i = 0; i < TRACE_BUFFER_SIZE; i++) { @@ -80,7 +29,7 @@ void Trace_Init(void) { void Trace_Print(log_target_t additionalLogTargets, const char* reason) { uint16_t iter; - enabled = false; + Trace_Enabled = false; device_id_t targetDeviceId = DEVICE_ID; log_target_t targetInterface = 0; @@ -119,7 +68,7 @@ void Trace_Print(log_target_t additionalLogTargets, const char* reason) { } } - enabled = true; + Trace_Enabled = true; #undef LINE_LENGTH } diff --git a/right/src/trace.h b/right/src/trace.h index a474ef75d..ccdc74f70 100644 --- a/right/src/trace.h +++ b/right/src/trace.h @@ -3,33 +3,58 @@ // Includes: - #include #include - #include "debug.h" + #include + #include + #include "trace_defs.h" #include "logger.h" + #include "wormhole.h" + #include "event_scheduler.h" -// Macros: +// Variables: -#define TRACE_BUFFER_SIZE 256 + extern bool Trace_Enabled; -// Typedefs: +// Macros: - typedef struct { - char data[TRACE_BUFFER_SIZE]; - uint32_t eventVector; - uint16_t position; - } ATTR_PACKED trace_buffer_t; - -// Variables: + #define TraceBuffer StateWormhole.traceBuffer.data + #define TraceBufferPosition StateWormhole.traceBuffer.position + +// Inline functions: + + static inline void Trace(char a) { + if (Trace_Enabled) { + StateWormhole.traceBuffer.eventVector = EventScheduler_Vector; + TraceBuffer[TraceBufferPosition] = a; + TraceBufferPosition = (TraceBufferPosition + 1) % TRACE_BUFFER_SIZE; + } + } + + static inline void Trace_Printc(const char* s) { + if (Trace_Enabled) { + StateWormhole.traceBuffer.eventVector = EventScheduler_Vector; + for (uint16_t i = 0; s[i] != '\0' && s[i] < 127; i++) { + TraceBuffer[TraceBufferPosition] = s[i]; + TraceBufferPosition = (TraceBufferPosition + 1) % TRACE_BUFFER_SIZE; + } + } + } + + static inline void Trace_Printf(const char *fmt, ...) { + if (Trace_Enabled) { + char buffer[TRACE_BUFFER_SIZE]; + va_list args; + va_start(args, fmt); + buffer[TRACE_BUFFER_SIZE-1] = '\0'; + vsnprintf(buffer, TRACE_BUFFER_SIZE-1, fmt, args); + va_end(args); + Trace_Printc(buffer); + } + } // Functions: void Trace_Init(void); - - void Trace(char a); - void Trace_Printf(const char *fmt, ...); - void Trace_Printc(const char* s); - void Trace_Print(log_target_t additionalLogTargets, const char* reason); #endif diff --git a/right/src/trace_defs.h b/right/src/trace_defs.h new file mode 100644 index 000000000..66cac37a5 --- /dev/null +++ b/right/src/trace_defs.h @@ -0,0 +1,21 @@ +#ifndef __TRACE_DEFS_H__ +#define __TRACE_DEFS_H__ + +// Includes: + + #include + #include "debug.h" + +// Macros: + + #define TRACE_BUFFER_SIZE 256 + +// Typedefs: + + typedef struct { + char data[TRACE_BUFFER_SIZE]; + uint32_t eventVector; + uint16_t position; + } ATTR_PACKED trace_buffer_t; + +#endif diff --git a/right/src/wormhole.h b/right/src/wormhole.h index 0462ca904..1f2f81da4 100644 --- a/right/src/wormhole.h +++ b/right/src/wormhole.h @@ -7,7 +7,7 @@ #include #include "power_mode.h" #include "macros/status_buffer.h" - #include "trace.h" + #include "trace_defs.h" // Macros: From bf073a75de9f0884a2dcaab3623787dc4096116a Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Sat, 17 Jan 2026 17:48:34 +0100 Subject: [PATCH 33/46] Ble fixes: unauth on pairing_failed? --- device/src/bt_conn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 75f84f780..529ecbe80 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -939,12 +939,12 @@ static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason) { if (auth_conn == conn) { Trace_Printc("bu7"); LOG_WRN("Pairing of auth conn failed because of %d\n", reason); - unsetAuthConn(false); + unsetAuthConn(true); PairingScreen_Feedback(false); } // TODO: should we here? - safeDisconnect(conn, BT_REASON_PERMANENT); + //safeDisconnect(conn, BT_REASON_PERMANENT); LOG_WRN("Pairing failed: %s, reason %d\n", GetPeerStringByConn(conn), reason); } From d0af27e7a95db76713ffa635d9aec8636e1202e1 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Sat, 17 Jan 2026 17:49:27 +0100 Subject: [PATCH 34/46] Ble fixes: reorder bluetooth shutdown sequence to not produce hci crashes. --- device/src/bt_manager.c | 21 ++++++++++++++++++--- device/src/bt_pair.c | 3 +-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/device/src/bt_manager.c b/device/src/bt_manager.c index 9531f3a55..e9cbc234e 100644 --- a/device/src/bt_manager.c +++ b/device/src/bt_manager.c @@ -72,19 +72,34 @@ void BtManager_StartBt() { void BtManager_StopBt() { BT_TRACE_AND_ASSERT("bm3"); - if (DEVICE_IS_UHK80_RIGHT) { - HOGP_Disable(); - } + + k_sleep(K_MSEC(10)); + + EventScheduler_Unschedule(EventSchedulerEvent_BtStartScanningAndAdvertising); if (DEVICE_IS_UHK80_LEFT || DEVICE_IS_UHK80_RIGHT) { BtAdvertise_Stop(); } + k_sleep(K_MSEC(10)); + if (DEVICE_IS_UHK80_RIGHT || DEVICE_IS_UHK_DONGLE) { BtScan_Stop(); } + EventScheduler_Unschedule(EventSchedulerEvent_BtStartScanningAndAdvertising); + + k_sleep(K_MSEC(10)); + + BtConn_DisconnectAll(); + + k_sleep(K_MSEC(10)); + BtAdvertise_DisableAdvertisingIcon(); + + if (DEVICE_IS_UHK80_RIGHT) { + HOGP_Disable(); + } } diff --git a/device/src/bt_pair.c b/device/src/bt_pair.c index e26368331..6f15cbad9 100644 --- a/device/src/bt_pair.c +++ b/device/src/bt_pair.c @@ -36,10 +36,9 @@ static pairing_mode_t defaultPairingMode() { } static void enterOobPairingMode() { - printk("--- Entering pairing mode. Going to stop BT and disconnect all connections. ---\n"); + printk("------ Entering oob pairing mode. Going to stop BT and disconnect all connections. ------\n"); BtPair_PairingMode = PairingMode_Oob; BtManager_StopBt(); - BtConn_DisconnectAll(); } struct bt_le_oob* BtPair_GetLocalOob() { From 3561b08cccaa542acf6808c16f57b1f5e42ef913 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Sat, 17 Jan 2026 17:50:13 +0100 Subject: [PATCH 35/46] Ble fixes: prevent automatic advertising start if oob is in progress. --- device/src/bt_manager.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/device/src/bt_manager.c b/device/src/bt_manager.c index e9cbc234e..89338c60c 100644 --- a/device/src/bt_manager.c +++ b/device/src/bt_manager.c @@ -110,7 +110,11 @@ void BtManager_StartScanningAndAdvertisingAsync(bool wasAggresive, const char* e uint32_t expDelay; static int8_t aggressiveTries = 0; - bool weArePairing = BtPair_PairingMode == PairingMode_Oob || BtPair_PairingMode == PairingMode_PairHid; + if (BtPair_PairingMode == PairingMode_Oob) { + return; + } + + bool weArePairing = BtPair_PairingMode == PairingMode_PairHid; bool weAreSwitching = SelectedHostConnectionId != ConnectionId_Invalid; if (weArePairing || weAreSwitching) { From 17f752ed71cb2db8b1837af638661d151b7040f9 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Sat, 17 Jan 2026 17:50:28 +0100 Subject: [PATCH 36/46] Increase i2c stack size - we have encountered an overflow. --- device/src/keyboard/i2c.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/device/src/keyboard/i2c.c b/device/src/keyboard/i2c.c index 435215023..1578a4ce2 100644 --- a/device/src/keyboard/i2c.c +++ b/device/src/keyboard/i2c.c @@ -13,7 +13,8 @@ // Thread definitions -#define THREAD_STACK_SIZE 1000 +// 1000 did overflow +#define THREAD_STACK_SIZE 2000 #define THREAD_PRIORITY -1 static K_THREAD_STACK_DEFINE(stack_area, THREAD_STACK_SIZE); From 536760f783affa60dd0f48d1923d7ab6b243df56 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Sat, 17 Jan 2026 18:07:02 +0100 Subject: [PATCH 37/46] Fix compilation. --- right/src/config_parser/parse_host_connection.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/right/src/config_parser/parse_host_connection.c b/right/src/config_parser/parse_host_connection.c index fb30597c9..dda73b9b9 100644 --- a/right/src/config_parser/parse_host_connection.c +++ b/right/src/config_parser/parse_host_connection.c @@ -7,9 +7,12 @@ #include "config_parser/basic_types.h" #include "host_connection.h" #include "parse_config.h" -#include "bt_conn.h" #include +#ifdef __ZEPHYR__ +#include "bt_conn.h" +#endif + static parser_error_t parseHostConnection(config_buffer_t* buffer, host_connection_t* hostConnection) { host_connection_type_t hostType = ReadUInt8(buffer); From abecad7c22f2abdc6079421d04ea3216cff9cd7e Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 19 Jan 2026 13:23:54 +0100 Subject: [PATCH 38/46] DOCS: rename testing_scenarios.md --- doc-dev/other/{testing-scenarios.md => testing_scenarios.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc-dev/other/{testing-scenarios.md => testing_scenarios.md} (100%) diff --git a/doc-dev/other/testing-scenarios.md b/doc-dev/other/testing_scenarios.md similarity index 100% rename from doc-dev/other/testing-scenarios.md rename to doc-dev/other/testing_scenarios.md From 5d9425cc08c7fb2f061ec4903641da681ae39d65 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 19 Jan 2026 13:46:04 +0100 Subject: [PATCH 39/46] DOCS: add logging instructions. --- doc-dev/other/crash_logs.md | 24 +++++++++++++++++++++++ doc-dev/other/testing_scenarios.md | 3 +++ doc-dev/other/zephyr_logging.md | 31 ++++++++++++++++++------------ 3 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 doc-dev/other/crash_logs.md diff --git a/doc-dev/other/crash_logs.md b/doc-dev/other/crash_logs.md new file mode 100644 index 000000000..c9ad46da9 --- /dev/null +++ b/doc-dev/other/crash_logs.md @@ -0,0 +1,24 @@ +# Crash logs - users + +## Uhk80 + +Crash logs have to be enabled via `set devMode true` in `$onInit` + +## Uhk60 + +Crash logs are enabled by default. However, `set devMode true` is suggested as it enables logging in cases of ESD events. + +(We don't log esd reboots because they are often and these crash logs are significantly more disturbing than a quick reboot.) + +# Developer notes + +In order to fit all our uart applications onto two controllers, we use the async uart driver. Unfortunatelly, the async serial backend is significantly less reliable than the default interrupt backend. Especially it is not available at all during early boot phases. When these log are needed, the interrupt driver has to be used: + +- Comment out pin wiring stuff in device/src/main.c. + +- Comment out this in uhk-80-right.conf: + +``` +# CONFIG_SHELL_BACKEND_SERIAL_API_ASYNC=y +``` + diff --git a/doc-dev/other/testing_scenarios.md b/doc-dev/other/testing_scenarios.md index b82aac755..4bb30f1c7 100644 --- a/doc-dev/other/testing_scenarios.md +++ b/doc-dev/other/testing_scenarios.md @@ -11,6 +11,9 @@ Following is a list testing suggestions. Not all have to be tested each time. - Unpair+pair dongle. (Optionally, let a bluetooth hid active while doing so.) - Pair two ble hids using `bluetooth pair` while all devices are active. +Known issues: +- Oob pairing (of the dongle) fails when another device is connected, including a dongle that is *not* connected over usb. + ## Usb suspend: In all scenarios make sure that uhk works afterwards. diff --git a/doc-dev/other/zephyr_logging.md b/doc-dev/other/zephyr_logging.md index c9ad46da9..3eb11d776 100644 --- a/doc-dev/other/zephyr_logging.md +++ b/doc-dev/other/zephyr_logging.md @@ -1,24 +1,31 @@ -# Crash logs - users - -## Uhk80 - -Crash logs have to be enabled via `set devMode true` in `$onInit` +# Uhk logs ## Uhk60 -Crash logs are enabled by default. However, `set devMode true` is suggested as it enables logging in cases of ESD events. +Uhk 60 has no extensive logging in place. See [crash logging](./crash_logs.md) for crash logging configuration. -(We don't log esd reboots because they are often and these crash logs are significantly more disturbing than a quick reboot.) +## Uhk80 -# Developer notes +- Launch Agent. +- Go to the About page. +- Click the Agent logo seven times. +- Go to the newly visible "Advanced settings" menu. +- Click on the "Zephyr logging" button. +- Check the "Right half" checkbox. If the left half and/or dongle is affected and connected via USB, check them, too. -In order to fit all our uart applications onto two controllers, we use the async uart driver. Unfortunatelly, the async serial backend is significantly less reliable than the default interrupt backend. Especially it is not available at all during early boot phases. When these log are needed, the interrupt driver has to be used: +# Agent logs -- Comment out pin wiring stuff in device/src/main.c. +You can find Agent logs in the following path: -- Comment out this in uhk-80-right.conf: +- on Linux: `~/.config/uhk-agent/uhk-agent.log` +- on macOS: `~/Library/Logs/uhk-agent/uhk-agent.log` +- on Windows: `%USERPROFILE%\AppData\Roaming\uhk-agent/uhk-agent.log` + +Depending on the issue, you may need to enable additional logging by passing additional commandline arguments. For communication related issues, it is `--log=usb`, valid values are `config | misc | usb | usbOps | all`. Furthermore, you may need to prefix them with multiple groups of `--`, exact count depending on your system and exact Agent build. Resulting command may look as follows: ``` -# CONFIG_SHELL_BACKEND_SERIAL_API_ASYNC=y +./UHK.Agent-9.0.0-linux-x86_64.AppImage -- -- --log=misc,config,usb ``` + + From 62040baa6adfcc7ba9ea3666aa3833503e617e13 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 19 Jan 2026 13:51:01 +0100 Subject: [PATCH 40/46] DOCS: testing scenarios. --- doc-dev/other/testing_scenarios.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc-dev/other/testing_scenarios.md b/doc-dev/other/testing_scenarios.md index 4bb30f1c7..684e9654b 100644 --- a/doc-dev/other/testing_scenarios.md +++ b/doc-dev/other/testing_scenarios.md @@ -10,6 +10,10 @@ Following is a list testing suggestions. Not all have to be tested each time. - Test that we can add a paired device into the host connections. Try pair and add multiple devices within one Agent session. - Unpair+pair dongle. (Optionally, let a bluetooth hid active while doing so.) - Pair two ble hids using `bluetooth pair` while all devices are active. +- Unregistered slot connections: try to add a ble hid without registering it in Agent. Change registered connection count. Check that the ble hid remaind in the same slot (unless another host connection took that slot - in that case, it should be in the first availalbe slot). + +Todos: +- deduplicate and reorder these scenarios. Known issues: - Oob pairing (of the dongle) fails when another device is connected, including a dongle that is *not* connected over usb. From 9b265b28f65bdee5ee289b03c4bcd713566e9ffe Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 19 Jan 2026 14:22:03 +0100 Subject: [PATCH 41/46] Bt: refactor some logging --- device/src/bt_manager.c | 7 ++++++- device/src/bt_pair.c | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/device/src/bt_manager.c b/device/src/bt_manager.c index 89338c60c..ce6dcdc19 100644 --- a/device/src/bt_manager.c +++ b/device/src/bt_manager.c @@ -75,6 +75,8 @@ void BtManager_StopBt() { k_sleep(K_MSEC(10)); + printk("OOB: Stopping bluetooth services.\n"); + EventScheduler_Unschedule(EventSchedulerEvent_BtStartScanningAndAdvertising); if (DEVICE_IS_UHK80_LEFT || DEVICE_IS_UHK80_RIGHT) { @@ -95,11 +97,14 @@ void BtManager_StopBt() { k_sleep(K_MSEC(10)); - BtAdvertise_DisableAdvertisingIcon(); if (DEVICE_IS_UHK80_RIGHT) { HOGP_Disable(); } + + BtAdvertise_DisableAdvertisingIcon(); + + printk("OOB: Bluetooth stopped\n"); } diff --git a/device/src/bt_pair.c b/device/src/bt_pair.c index 6f15cbad9..43ac347d1 100644 --- a/device/src/bt_pair.c +++ b/device/src/bt_pair.c @@ -111,8 +111,9 @@ void BtPair_PairCentral() { BtPair_PairingAsCentral = true; Settings_Reload(); bt_le_oob_set_sc_flag(true); + printk ("OOB: Scanning preparing...\n"); BtScan_Start(); - printk ("Scanning for pairable device\n"); + printk ("OOB: Scanning for pairable device\n"); EventScheduler_Reschedule(k_uptime_get_32() + PAIRING_TIMEOUT, EventSchedulerEvent_EndBtPairing, "Oob pairing timeout."); } #endif @@ -123,8 +124,9 @@ void BtPair_PairPeripheral() { BtPair_PairingAsCentral = false; Settings_Reload(); bt_le_oob_set_sc_flag(true); + printk ("OOB: Advertisement preparing...\n"); BtAdvertise_Start(BtAdvertise_Config()); - printk ("Waiting for central to pair to me.\n"); + printk ("OOB: Waiting for central to pair to me.\n"); EventScheduler_Reschedule(k_uptime_get_32() + PAIRING_TIMEOUT, EventSchedulerEvent_EndBtPairing, "Oob pairing timeout."); } #endif From 513945dab4042ab74437ee00eca94c89778da483 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 19 Jan 2026 14:25:19 +0100 Subject: [PATCH 42/46] DOCS: add some testing notes. --- doc-dev/other/testing_scenarios.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc-dev/other/testing_scenarios.md b/doc-dev/other/testing_scenarios.md index 684e9654b..c35845506 100644 --- a/doc-dev/other/testing_scenarios.md +++ b/doc-dev/other/testing_scenarios.md @@ -10,13 +10,19 @@ Following is a list testing suggestions. Not all have to be tested each time. - Test that we can add a paired device into the host connections. Try pair and add multiple devices within one Agent session. - Unpair+pair dongle. (Optionally, let a bluetooth hid active while doing so.) - Pair two ble hids using `bluetooth pair` while all devices are active. -- Unregistered slot connections: try to add a ble hid without registering it in Agent. Change registered connection count. Check that the ble hid remaind in the same slot (unless another host connection took that slot - in that case, it should be in the first availalbe slot). +- Unregistered slot connections: try to add a ble hid without registering it in Agent. Change registered connection count and save the userconfig. Check that the ble hid remaind in the same slot (unless another host connection took that slot - in that case, it should be in the first availalbe slot). +- If moving currently connected device's host connection to another position, check that saving the UserConfig doesn't disconnect it - it should be remapped to the new slot by itself. Todos: - deduplicate and reorder these scenarios. Known issues: - Oob pairing (of the dongle) fails when another device is connected, including a dongle that is *not* connected over usb. +- IRK fails are produced by settings loads (maybe this is the cause of the direct advertising issue though?): +``` +[00:00:32.821,655] bt_hci_core: opcode 0x2027 status 0x12 +[00:00:32.821,655] bt_id: Failed to add IRK to controller +``` ## Usb suspend: From 9a727943794f95754a8e134d7641813a6dd21b61 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 19 Jan 2026 14:31:06 +0100 Subject: [PATCH 43/46] Bt: prune some logs. --- device/src/bt_manager.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/device/src/bt_manager.c b/device/src/bt_manager.c index ce6dcdc19..1c6343ed3 100644 --- a/device/src/bt_manager.c +++ b/device/src/bt_manager.c @@ -132,7 +132,7 @@ void BtManager_StartScanningAndAdvertisingAsync(bool wasAggresive, const char* e expDelay = MIN(maxDelay, minDelay << aggressiveTries); } - LOG_INF("btManager: BtManager_StartScanningAndAdvertisingAsync because %s, delay %d\n", eventLabel, expDelay); + LOG_INF("BtManager: Scheduling scan/adv in %dms. (%s)\n", expDelay, eventLabel); EventScheduler_Reschedule(Timer_GetCurrentTime() + expDelay, EventSchedulerEvent_BtStartScanningAndAdvertising, eventLabel); } @@ -174,8 +174,6 @@ void BtManager_StartScanningAndAdvertising() { bool dongleShouldScanForOob = DEVICE_IS_UHK_DONGLE && BtPair_PairingMode == PairingMode_Oob && BtPair_PairingAsCentral; bool shouldScan = rightShouldScanForPeer || rightShouldScanForOob || dongleShouldScanForPeer || dongleShouldScanForOob; - LOG_INF("btManager: should scanAndAdvertise %d %d\n", shouldScan, shouldAdvertise); - if (shouldAdvertise || shouldScan) { const char* label = ""; if (shouldAdvertise && shouldScan) { @@ -185,7 +183,7 @@ void BtManager_StartScanningAndAdvertising() { } else if (shouldScan) { label = "scanning"; } - LOG_INF("Starting %s, try %d!\n", label, try); + LOG_INF("BtManager: Start '%s' (%d).\n", label, try); } #ifdef CONFIG_BT_PERIPHERAL From 38787f6c798d3484e2fd95e3cb103acaedc65b5d Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 19 Jan 2026 14:54:33 +0100 Subject: [PATCH 44/46] Humanify logs. --- device/src/bt_advertise.c | 32 +++-- device/src/bt_conn.c | 114 +++++++++--------- device/src/bt_manager.c | 26 ++-- device/src/bt_pair.c | 4 +- device/src/connections.c | 21 ++-- device/src/device_state.c | 2 +- device/src/keyboard/charger.c | 20 +-- .../oled/screens/notification_screen.c | 5 +- device/src/nus_client.c | 34 +++--- device/src/nus_server.c | 12 +- device/src/state_sync.c | 21 ++-- right/src/host_connection.c | 2 +- right/src/macros/set_command.c | 4 +- 13 files changed, 152 insertions(+), 145 deletions(-) diff --git a/device/src/bt_advertise.c b/device/src/bt_advertise.c index e4a4b2dfd..83d7b3b5e 100644 --- a/device/src/bt_advertise.c +++ b/device/src/bt_advertise.c @@ -81,7 +81,7 @@ ATTR_UNUSED static void setFilters(adv_config_t advConfig) { bt_le_filter_accept_list_clear(); if (advConfig.advType & (ADVERTISE_HID | ADVERTISE_NUS)) { - printk("Bt: filling adv allow filter\n"); + LOG_DBG("Bt: filling adv allow filter"); for (uint8_t connId = ConnectionId_HostConnectionFirst; connId <= ConnectionId_HostConnectionLast; connId++) { host_connection_t* hostConnection = HostConnection(connId); @@ -99,7 +99,7 @@ ATTR_UNUSED static void setFilters(adv_config_t advConfig) { } if (advConfig.advType & ADVERTISE_DIRECTED_NUS) { - printk("Bt: filling adv allow filter for \"directed\" advertising.\n"); + LOG_DBG("Bt: filling adv allow filter for \"directed\" advertising."); bt_le_filter_accept_list_add(advConfig.addr); } } @@ -136,14 +136,12 @@ uint8_t BtAdvertise_Start(adv_config_t advConfig) updateAdvertisingIcon(advConfig.advType & ADVERTISE_HID); - printk("Bt: start advertising\n"); - // Start advertising static struct bt_le_adv_param advParam; switch (advConfig.advType) { case ADVERTISE_HID: case ADVERTISE_NUS | ADVERTISE_HID: - LOG_INF("Adv: advertise nus+hid.\n"); + LOG_DBG("Adv: advertise nus+hid."); /* our devices don't check service uuids, so hid advertisement effectively advertises nus too */ advParam = *BT_LE_ADV_CONN_ONE_TIME; err = BT_LE_ADV_START(&advParam, adHid, sdHid); @@ -151,7 +149,7 @@ uint8_t BtAdvertise_Start(adv_config_t advConfig) break; case ADVERTISE_NUS: if (Cfg.Bt_DirectedAdvertisingAllowed) { - LOG_INF("Adv: advertise nus, with allow list.\n"); + LOG_DBG("Adv: advertise nus, with allow list."); setFilters(advConfig); advParam = *BT_LE_ADV_CONN_ONE_TIME; @@ -159,14 +157,14 @@ uint8_t BtAdvertise_Start(adv_config_t advConfig) err = BT_LE_ADV_START(&advParam, BY_SIDE(adNusLeft, adNusRight), sdNus); } else { - LOG_INF("Adv: advertise nus, without allow list.\n"); + LOG_DBG("Adv: advertise nus, without allow list."); advParam = *BT_LE_ADV_CONN_ONE_TIME; err = BT_LE_ADV_START(&advParam, BY_SIDE(adNusLeft, adNusRight), sdNus); } break; case ADVERTISE_DIRECTED_NUS: if (Cfg.Bt_DirectedAdvertisingAllowed) { - LOG_INF("Adv: direct advertise nus, with allow list.\n"); + LOG_DBG("Adv: direct advertise nus, with allow list."); setFilters(advConfig); advParam = *BT_LE_ADV_CONN_ONE_TIME; @@ -174,31 +172,31 @@ uint8_t BtAdvertise_Start(adv_config_t advConfig) err = BT_LE_ADV_START(&advParam, BY_SIDE(adNusLeft, adNusRight), sdNus); } else { - LOG_INF("Adv: direct advertise nus, without allow list.\n"); + LOG_DBG("Adv: direct advertise nus, without allow list."); advParam = *BT_LE_ADV_CONN_ONE_TIME; err = BT_LE_ADV_START(&advParam, BY_SIDE(adNusLeft, adNusRight), sdNus); } //// TODO: fix and reenable this? - // printk("Advertising against %s\n", GetAddrString(advConfig.addr)); + // printk("Advertising against %s", GetAddrString(advConfig.addr)); // advParam = *BT_LE_ADV_CONN_DIR_LOW_DUTY(advConfig.addr); // advParam.options |= BT_LE_ADV_OPT_DIR_ADDR_RPA; // err = BT_LE_ADV_START(&advParam, BY_SIDE(adNusLeft, adNusRight), sdNus); break; default: - LOG_INF("Adv: Attempted to start advertising without any type! Ignoring.\n"); + LOG_INF("Adv: Attempted to start advertising without any type! Ignoring."); return 0; } // Log it if (err == 0) { - LOG_INF("Adv: %s advertising successfully started\n", advTypeString); + LOG_INF("Adv: '%s' started", advTypeString); return 0; } else if (err == -EALREADY) { - LOG_INF("Adv: %s advertising continued\n", advTypeString); + LOG_INF("Adv: '%s' continued", advTypeString); return 0; } else { - LOG_INF("Adv: %s advertising failed to start (err %d), free connections: %d\n", advTypeString, err, BtConn_UnusedPeripheralConnectionCount()); + LOG_INF("Adv: '%s' failed to start (err %d), free connections: %d", advTypeString, err, BtConn_UnusedPeripheralConnectionCount()); return err; } } @@ -207,7 +205,7 @@ void BtAdvertise_Stop(void) { BT_TRACE_AND_ASSERT("ba2"); int err = bt_le_adv_stop(); if (err) { - LOG_WRN("Adv: Advertising failed to stop (err %d)\n", err); + LOG_WRN("Adv: Advertising failed to stop (err %d)", err); Bt_HandleError("BtAdvertise_Stop", err); } } @@ -262,7 +260,7 @@ adv_config_t BtAdvertise_Config() { } else { /** advertising needs a peripheral slot. When it is not free and we try to advertise, it will fail, and our code will try to * disconnect other devices in order to restore proper function. */ - LOG_INF("Adv: Current slot count is zero, not advertising!\n"); + LOG_INF("Adv: Current slot count is zero, not advertising!"); // BtConn_ListCurrentConnections(); return ADVERTISEMENT( 0 ); } @@ -270,7 +268,7 @@ adv_config_t BtAdvertise_Config() { case DeviceId_Uhk_Dongle: return ADVERTISEMENT( 0 ); default: - LOG_WRN("unknown device!\n"); + LOG_WRN("unknown device!"); return ADVERTISEMENT( 0 ); } } diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 529ecbe80..c56c7ab18 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -171,7 +171,7 @@ static struct bt_conn* unsetAuthConn(bool cancel_auth) { static void safeDisconnect(struct bt_conn *conn, int reason) { if (conn == auth_conn) { - LOG_INF("Unauthenticating %s\n", GetPeerStringByConn(conn)); + LOG_INF("Unauthenticating %s", GetPeerStringByConn(conn)); unsetAuthConn(true); #if DEVICE_HAS_OLED @@ -183,7 +183,7 @@ static void safeDisconnect(struct bt_conn *conn, int reason) { struct bt_conn_info info; int err = bt_conn_get_info(conn, &info); if (err == 0 && info.state == BT_CONN_STATE_CONNECTED) { - LOG_INF("Disconnecting %s\n", GetPeerStringByConn(conn)); + LOG_INF("Disconnecting %s", GetPeerStringByConn(conn)); bt_conn_disconnect(conn, reason); } } @@ -273,7 +273,7 @@ static void enableDataLengthExtension(struct bt_conn *conn) { static void setLatency(struct bt_conn* conn, const struct bt_le_conn_param* params) { int err = bt_conn_le_param_update(conn, params); if (err) { - LOG_WRN("LE latencies update failed: %d\n", err); + LOG_WRN("LE latencies update failed: %d", err); Bt_HandleError("bt_conn_le_param_update", err); } } @@ -313,10 +313,10 @@ static void youAreNotWanted(struct bt_conn *conn) { uint32_t currentTime = k_uptime_get_32(); if (currentTime - lastAttemptTime < 1000 && BtAddrEq(bt_conn_get_dst(conn), &lastAddr)) { - LOG_WRN("Refusing connenction %s (this is not a selected connection)(this is repeated attempt!)\n", GetPeerStringByConn(conn)); + LOG_WRN("Refusing connenction %s (this is not a selected connection)(this is repeated attempt!)", GetPeerStringByConn(conn)); safeDisconnect(conn, BT_REASON_HID_GIVE_US_BREAK); } else { - LOG_WRN("Refusing connenction %s (this is not a selected connection (%d))\n", GetPeerStringByConn(conn), SelectedHostConnectionId); + LOG_WRN("Refusing connenction %s (this is not a selected connection (%d))", GetPeerStringByConn(conn), SelectedHostConnectionId); safeDisconnect(conn, BT_REASON_TEMPORARY); } LOG_INF(" Free peripheral slots: %d, Peripheral conn count: %d, bt pari mode: %d", @@ -330,7 +330,7 @@ static void youAreNotWanted(struct bt_conn *conn) { } ATTR_UNUSED static void youAreNotAuthenticated(struct bt_conn *conn) { - LOG_WRN("Implement this!\n"); + LOG_WRN("Implement this!"); } void BtConn_UpdateHostConnectionPeerAllocations() { @@ -343,7 +343,7 @@ void BtConn_UpdateHostConnectionPeerAllocations() { if (newId != ConnectionId_Invalid && newId != currentId) { Connections_MoveConnection(peerId, currentId, newId); } else if (newId == ConnectionId_Invalid) { - LOG_WRN("No host connection found for peer %s, disconnecting\n", Peers[peerId].name); + LOG_WRN("No host connection found for peer %s, disconnecting", Peers[peerId].name); safeDisconnect(conn, BT_REASON_UNSPECIFIED); } } @@ -384,7 +384,7 @@ static uint8_t assignPeer(struct bt_conn* conn, uint8_t connectionId, uint8_t co uint8_t peerId = allocateHostPeer(connectionType); if (peerId == PeerIdUnknown) { - LOG_WRN("No peer slot available for connection %d\n", connectionId); + LOG_WRN("No peer slot available for connection %d", connectionId); safeDisconnect(conn, BT_REASON_UNSPECIFIED); return PeerIdUnknown; } @@ -402,25 +402,25 @@ void bt_foreach_list_current_connections(struct bt_conn *conn, void *data) { int8_t peerId = GetPeerIdByConn(conn); if (peerId == PeerIdUnknown) { - LogU(" - %s\n", GetPeerStringByConn(conn)); + LogU(" - %s", GetPeerStringByConn(conn)); } else { - LogU(" - peer %d(%s), connection %d\n", peerId, GetPeerStringByConn(conn), Peers[peerId].connectionId); + LogU(" - peer %d(%s), connection %d", peerId, GetPeerStringByConn(conn), Peers[peerId].connectionId); } } void BtConn_ListCurrentConnections() { - LogU("Current connections:\n"); + LogU("Current connections:"); bt_conn_foreach(BT_CONN_TYPE_LE, bt_foreach_list_current_connections, NULL); } static void bt_foreach_print_bond(const struct bt_bond_info *info, void *user_data) { - LogU(" - %s\n", GetAddrString(&info->addr)); + LogU(" - %s", GetAddrString(&info->addr)); } void BtConn_ListAllBonds() { - LogU("All bonds:\n"); + LogU("All bonds:"); bt_foreach_bond(BT_ID_DEFAULT, bt_foreach_print_bond, NULL); } @@ -475,7 +475,7 @@ static bool isWantedInNormalMode(struct bt_conn *conn, connection_id_t connectio */ // We can get here during pairing where the connection collides with itself. - LOG_INF(" Not wanted: this is HID collision\n"); + LOG_INF(" Not wanted: this is HID collision"); return false; } else if (selectedConnectionIsBleHid) { if (!isSelectedConnection) { @@ -505,7 +505,7 @@ static bool isWanted(struct bt_conn *conn, bool alreadyAuthenticated, connection static void connectNus(struct bt_conn *conn, connection_id_t connectionId, connection_type_t connectionType) { uint8_t peerId = assignPeer(conn, connectionId, connectionType); - LOG_INF("connected to %s\n", GetPeerStringByConn(conn)); + LOG_DBG("connected to %s", GetPeerStringByConn(conn)); configureLatency(conn, LatencyMode_NUS); enableDataLengthExtension(conn); @@ -513,7 +513,7 @@ static void connectNus(struct bt_conn *conn, connection_id_t connectionId, conne bool isRightClient = DEVICE_IS_UHK80_RIGHT && peerId == PeerIdLeft; bool isDongleClient = DEVICE_IS_UHK_DONGLE && peerId == PeerIdRight; if ( isRightClient || isDongleClient ) { - LOG_INF("Initiating NUS connection with %s\n", GetPeerStringByConn(conn)); + LOG_INF("Initiating NUS connection with %s", GetPeerStringByConn(conn)); NusClient_Connect(conn); } } @@ -524,9 +524,9 @@ static void connectHid(struct bt_conn *conn, connection_id_t connectionId, conne configureLatency(conn, LatencyMode_NUS); // Assume that HOGP is ready - LOG_INF("Established HID connection with %s\n", GetPeerStringByConn(conn)); + LOG_INF("Established HID connection with %s", GetPeerStringByConn(conn)); Connections_SetState(connectionId, ConnectionState_Ready); - BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in connectHid"); + BtManager_StartScanningAndAdvertisingAsync(false, "connectHid"); } #define BT_UUID_NUS_VAL BT_UUID_128_ENCODE(0x6e400001, 0xb5a3, 0xf393, 0xe0a9, 0xe50e24dcca9e) @@ -535,7 +535,7 @@ static void connectHid(struct bt_conn *conn, connection_id_t connectionId, conne ATTR_UNUSED static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) { if (!attr) { - LOG_INF("Service discovery completed, connection wasn't matched.\n"); + LOG_DBG("Service discovery completed, connection wasn't matched."); // TODO: consider setting a timer to disconnect the connection if neither auth nor security is Established return BT_GATT_ITER_STOP; } @@ -552,7 +552,7 @@ ATTR_UNUSED static uint8_t discover_func(struct bt_conn *conn, const struct bt_g } if (!isPairedConnection && DEVICE_IS_UHK80_RIGHT) { - LOG_INF("Unknown NUS trying to connect. Refusing!\n"); + LOG_INF("Unknown NUS trying to connect. Refusing!"); safeDisconnect(conn, BT_REASON_PERMANENT); } return BT_GATT_ITER_STOP; @@ -570,7 +570,7 @@ static void connectUnknown(struct bt_conn *conn) { #if DEVICE_IS_UHK80_RIGHT || DEVICE_IS_UHK_DONGLE int err; - LOG_INF("Bt connected to unknown. Starting discovery.\n"); + LOG_DBG("Bt connected to unknown. Starting discovery."); static struct bt_gatt_discover_params discover_params; discover_params.uuid = NULL; // Will discover all services discover_params.start_handle = 0x0001; @@ -580,7 +580,7 @@ static void connectUnknown(struct bt_conn *conn) { err = bt_gatt_discover(conn, &discover_params); if (err) { - LOG_WRN("Service discovery failed (err %u)\n", err); + LOG_WRN("Service discovery failed (err %u)", err); Bt_HandleError("bt_gatt_discover", err); return; } @@ -590,14 +590,14 @@ static void connectUnknown(struct bt_conn *conn) { static void connected(struct bt_conn *conn, uint8_t err) { BT_TRACE_AND_ASSERT("bc1"); - LOG_INF("Connected cb\n"); + LOG_DBG("Connected cb"); // Without this, linux pairing fails, because tiny 27 byte packets // exhaust acl buffers easily enableDataLengthExtension(conn); if (err) { - LOG_WRN("Failed to connect to %s, err %u\n", GetPeerStringByConn(conn), err); + LOG_WRN("Failed to connect to %s, err %u", GetPeerStringByConn(conn), err); BtManager_StartScanningAndAdvertising(); return; } @@ -606,11 +606,11 @@ static void connected(struct bt_conn *conn, uint8_t err) { connection_id_t connectionId = Connections_GetConnectionIdByHostAddr(addr); connection_type_t connectionType = Connections_Type(connectionId); - LOG_INF("connected %s, %d %d\n", GetPeerStringByConn(conn), connectionId, connectionType); + LOG_INF("Connected %s, %d %d", GetPeerStringByConn(conn), connectionId, connectionType); if (connectionId == ConnectionId_Invalid) { connectUnknown(conn); - BtManager_StartScanningAndAdvertisingAsync(true, "StartScanningAndAdvertisingAsync in connected - invalid connection"); + BtManager_StartScanningAndAdvertisingAsync(true, "connected - invalid connection"); } else { if (isWanted(conn, false, connectionId, connectionType)) { @@ -618,7 +618,7 @@ static void connected(struct bt_conn *conn, uint8_t err) { // advertising/scanning needs to be started only after peers are assigned :-/ } else { youAreNotWanted(conn); - BtManager_StartScanningAndAdvertisingAsync(true, "StartScanningAndAdvertisingAsync in connected - they are not wanted"); + BtManager_StartScanningAndAdvertisingAsync(true, "connected - they are not wanted"); } } @@ -634,7 +634,7 @@ static void disconnected(struct bt_conn *conn, uint8_t reason) { ARG_UNUSED(peerId); - LOG_INF("Bt disconnected from %s, reason %u\n", GetPeerStringByConn(conn), reason); + LOG_INF("Disconnected from %s, reason %u", GetPeerStringByConn(conn), reason); if (DEVICE_IS_UHK80_LEFT && peerId == PeerIdRight) { NusServer_Disconnected(); @@ -662,7 +662,7 @@ static void disconnected(struct bt_conn *conn, uint8_t reason) { } if (!BtManager_Restarting) { - BtManager_StartScanningAndAdvertisingAsync(true, "StartScanningAndAdvertisingAsync in disconnected"); + BtManager_StartScanningAndAdvertisingAsync(true, "disconnected"); } if (conn == auth_conn) { @@ -675,7 +675,7 @@ void Bt_SetConnectionConfigured(struct bt_conn* conn) { uint8_t peerId = GetPeerIdByConn(conn); if (Connections[Peers[peerId].connectionId].state != ConnectionState_Ready) { Connections_SetState(Peers[peerId].connectionId, ConnectionState_Ready); - BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in SetConnectionConfigured"); + BtManager_StartScanningAndAdvertisingAsync(false, "SetConnectionConfigured"); } } @@ -693,7 +693,7 @@ static bool isUhkDeviceConnection(connection_type_t connectionType) { static void connectAuthenticatedConnection(struct bt_conn *conn, connection_id_t connectionId, connection_type_t connectionType) { // in case we don't have free connection slots and this is not the selected connection, then refuse if (!isWanted(conn, true, connectionId, connectionType)) { - LOG_WRN("Refusing authenticated connenction %d (this is not a selected connection(%d))\n", connectionId, SelectedHostConnectionId); + LOG_WRN("Refusing authenticated connenction %d (this is not a selected connection(%d))", connectionId, SelectedHostConnectionId); youAreNotWanted(conn); return; } @@ -711,7 +711,7 @@ static void connectAuthenticatedConnection(struct bt_conn *conn, connection_id_t default: LOG_ERR("Authenticated connection is not known. Disconnecting %s", GetPeerStringByConn(conn)); safeDisconnect(conn, BT_HCI_ERR_AUTH_FAIL); - BtManager_StartScanningAndAdvertisingAsync(true, "StartScanningAndAdvertisingAsync in authenticatedConnection - is unknown"); + BtManager_StartScanningAndAdvertisingAsync(true, "authenticatedConnection - is unknown"); break; } } @@ -720,7 +720,7 @@ static void securityChanged(struct bt_conn *conn, bt_security_t level, enum bt_s BT_TRACE_AND_ASSERT("bc3"); // In case of failure, disconnect if (err || (level < BT_SECURITY_L4 && !Cfg.Bt_AllowUnsecuredConnections)) { - LOG_WRN("Bt security failed: %s, level %u, err %d, disconnecting\n", GetPeerStringByConn(conn), level, err); + LOG_WRN("Bt security failed: %s, level %u, err %d, disconnecting", GetPeerStringByConn(conn), level, err); struct bt_conn_info info; int err = bt_conn_get_info(conn, &info); @@ -738,7 +738,7 @@ static void securityChanged(struct bt_conn *conn, bt_security_t level, enum bt_s // Ignore connection that is being paired. At this point, the central is // probably talking to us via an anonymous address, and it will yet change. if (conn == auth_conn) { - LOG_INF("Bt connection secured: %s, level %u. It is auth_conn, so ignoring.\n", GetPeerStringByConn(conn), level); + LOG_DBG("Bt connection secured: %s, level %u. It is auth_conn, so ignoring.", GetPeerStringByConn(conn), level); return; } @@ -750,7 +750,7 @@ static void securityChanged(struct bt_conn *conn, bt_security_t level, enum bt_s __attribute__((unused)) static void infoLatencyParamsUpdated(struct bt_conn* conn, uint16_t interval, uint16_t latency, uint16_t timeout) { - LOG_INF("%s conn params: interval=%u ms, latency=%u, timeout=%u ms\n", GetPeerStringByConn(conn), interval * 5 / 4, latency, timeout * 10); + LOG_DBG("%s conn params: interval=%u ms, latency=%u, timeout=%u ms", GetPeerStringByConn(conn), interval * 5 / 4, latency, timeout * 10); bool isUhkPeer = isUhkDeviceConnection(Connections_Type(Peers[GetPeerIdByConn(conn)].connectionId)); @@ -777,12 +777,12 @@ static void auth_passkey_entry(struct bt_conn *conn) { Trace_Printc("br1"); setAuthConn(conn); - LOG_INF("Received passkey pairing inquiry.\n"); + LOG_INF("Received passkey pairing inquiry."); enableDataLengthExtension(conn); if (!auth_conn) { - LOG_INF("Returning: no auth conn\n"); + LOG_INF("Returning: no auth conn"); return; } @@ -794,7 +794,7 @@ static void auth_passkey_entry(struct bt_conn *conn) { connection_type_t connectionType = Connections_Type(Peers[peerId].connectionId); bool isUhkPeer = isUhkDeviceConnection(connectionType); if (isUhkPeer || isUhkPeerByAddr || BtPair_PairingMode == PairingMode_Oob) { - LOG_INF("refusing passkey authentification for %s\n", GetPeerStringByConn(conn)); + LOG_INF("refusing passkey authentification for %s", GetPeerStringByConn(conn)); bt_conn_auth_cancel(conn); return; } @@ -803,11 +803,11 @@ static void auth_passkey_entry(struct bt_conn *conn) { PairingScreen_AskForPassword(); #endif - LOG_INF("Type `uhk passkey xxxxxx` to pair, or `uhk passkey -1` to reject\n"); + LOG_INF("Type `uhk passkey xxxxxx` to pair, or `uhk passkey -1` to reject"); } static void auth_cancel(struct bt_conn *conn) { - LOG_INF("Pairing cancelled: peer %s\n", GetPeerStringByConn(conn)); + LOG_INF("Pairing cancelled: peer %s", GetPeerStringByConn(conn)); if (auth_conn) { Trace_Printc("bu5"); @@ -831,12 +831,12 @@ static void auth_oob_data_request(struct bt_conn *conn, struct bt_conn_oob_info struct bt_le_oob* oobRemote = BtPair_GetRemoteOob(); if (memcmp(info.le.remote->a.val, oobRemote->addr.a.val, sizeof(info.le.remote->a.val))) { - LOG_WRN("Addresses not matching! Cancelling authentication\n"); + LOG_WRN("Addresses not matching! Cancelling authentication"); bt_conn_auth_cancel(conn); return; } - LOG_INF("Pairing OOB data requested!\n"); + LOG_INF("Pairing OOB data requested!"); bt_le_oob_set_sc_data(conn, &oobLocal->le_sc_data, &oobRemote->le_sc_data); } @@ -850,7 +850,7 @@ static struct bt_conn_auth_cb conn_auth_callbacks = { // Auth info callbacks static void pairing_complete(struct bt_conn *conn, bool bonded) { - LOG_WRN("Pairing completed: %s, bonded %d\n", GetPeerStringByConn(conn), bonded); + LOG_WRN("Pairing completed: %s, bonded %d", GetPeerStringByConn(conn), bonded); bt_addr_le_t addr = *bt_conn_get_dst(conn); @@ -871,7 +871,7 @@ static void pairing_complete(struct bt_conn *conn, bool bonded) { Bt_NewPairedDevice = true; if (connectionId == ConnectionId_Invalid) { - LOG_WRN("No connection slot available for newly paired device %s\n", GetPeerStringByConn(conn)); + LOG_WRN("No connection slot available for newly paired device %s", GetPeerStringByConn(conn)); NotifyPrintf("No slot available!"); safeDisconnect(conn, BT_REASON_UNSPECIFIED); return; @@ -880,7 +880,7 @@ static void pairing_complete(struct bt_conn *conn, bool bonded) { HostConnection_SetSelectedConnection(connectionId); - LOG_INF("Pairing complete, passing connection %d to authenticatedConnection handler. Selected conn is %d\n", connectionId, SelectedHostConnectionId); + LOG_INF("Pairing complete, passing connection %d to authenticatedConnection handler. Selected conn is %d", connectionId, SelectedHostConnectionId); // we have to connect from here, because central changes its address *after* setting security connectAuthenticatedConnection(conn, connectionId, connectionType); @@ -892,7 +892,7 @@ static void pairing_complete(struct bt_conn *conn, bool bonded) { PairingScreen_Feedback(true); } - BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in pairing_complete"); + BtManager_StartScanningAndAdvertisingAsync(false, "pairing_complete"); } static void bt_foreach_conn_cb(struct bt_conn *conn, void *user_data) { @@ -920,7 +920,7 @@ void BtConn_DisconnectOne(connection_id_t connectionId) { static void bt_foreach_conn_cb_disconnect_unidentified(struct bt_conn *conn, void *user_data) { peer_t* peer = getPeerByConn(conn); if (!peer) { - LOG_INF(" disconnecting unassigned connection %s\n", GetPeerStringByConn(conn)); + LOG_INF(" disconnecting unassigned connection %s", GetPeerStringByConn(conn)); safeDisconnect(conn, BT_REASON_NOT_SELECTED); } } @@ -938,7 +938,7 @@ static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason) { if (auth_conn == conn) { Trace_Printc("bu7"); - LOG_WRN("Pairing of auth conn failed because of %d\n", reason); + LOG_WRN("Pairing of auth conn failed because of %d", reason); unsetAuthConn(true); PairingScreen_Feedback(false); } @@ -946,7 +946,7 @@ static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason) { // TODO: should we here? //safeDisconnect(conn, BT_REASON_PERMANENT); - LOG_WRN("Pairing failed: %s, reason %d\n", GetPeerStringByConn(conn), reason); + LOG_WRN("Pairing failed: %s, reason %d", GetPeerStringByConn(conn), reason); } @@ -969,12 +969,12 @@ void BtConn_Init(void) { err = bt_conn_auth_cb_register(&conn_auth_callbacks); if (err) { - LOG_WRN("Failed to register authorization callbacks.\n"); + LOG_WRN("Failed to register authorization callbacks."); } err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks); if (err) { - LOG_WRN("Failed to register authorization info callbacks.\n"); + LOG_WRN("Failed to register authorization info callbacks."); } } @@ -993,7 +993,7 @@ void num_comp_reply(int passkey) { if (passkey >= 0) { bt_conn_auth_passkey_entry(conn, passkey); - LOG_INF("Sending passkey to conn %s\n", GetPeerStringByConn(conn)); + LOG_INF("Sending passkey to conn %s", GetPeerStringByConn(conn)); } else { conn = unsetAuthConn(true); } @@ -1025,7 +1025,7 @@ ATTR_UNUSED static void disconnectOldestHost() { } if (oldestPeerId != PeerIdUnknown) { - LOG_INF("Disconnecting oldest host %d\n", oldestPeerId); + LOG_INF("Disconnecting oldest host %d", oldestPeerId); safeDisconnect(Peers[oldestPeerId].conn, BT_REASON_NOT_SELECTED); } } @@ -1061,7 +1061,7 @@ void BtConn_ReserveConnections() { disconnectOldestHost(); // Advertising will get started when the host actually gets disconnected } else { - BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in ReserveConnections"); + BtManager_StartScanningAndAdvertisingAsync(false, "ReserveConnections"); } WIDGET_REFRESH(&TargetWidget); } else { @@ -1069,7 +1069,7 @@ void BtConn_ReserveConnections() { // Is this even a legal code path? // Just log for now. if (hostState == ConnectionState_Connected) { - LOG_WRN("Selected host is connected but not yet ready in ReserveConnections. Waiting...\n"); + LOG_WRN("Selected host is connected but not yet ready in ReserveConnections. Waiting..."); } } #endif @@ -1086,10 +1086,10 @@ void Bt_SetEnabled(bool enabled) { Cfg.Bt_Enabled = enabled; if (enabled) { - LOG_WRN("Starting bluetooth on request.\n"); + LOG_WRN("Starting bluetooth on request."); BtManager_StartScanningAndAdvertising(); } else { - LOG_WRN("Shutting down bluetooth on request!\n"); + LOG_WRN("Shutting down bluetooth on request!"); BtManager_StopBt(); BtConn_DisconnectAll(); } diff --git a/device/src/bt_manager.c b/device/src/bt_manager.c index 1c6343ed3..894c0d06e 100644 --- a/device/src/bt_manager.c +++ b/device/src/bt_manager.c @@ -30,10 +30,10 @@ bool BtManager_Restarting = false; static void bt_ready(int err) { if (err) { - LOG_WRN("Bluetooth init failed (err %d)\n", err); + LOG_WRN("Bluetooth init failed (err %d)", err); Bt_HandleError("bt_ready", err); } else { - LOG_INF("Bluetooth initialized successfully\n"); + LOG_INF("Bluetooth initialized successfully"); } } @@ -44,7 +44,7 @@ void BtManager_InitBt() { if (DEVICE_IS_UHK80_LEFT || DEVICE_IS_UHK80_RIGHT) { int err = NusServer_Init(); if (err) { - LOG_WRN("NusServer_Init failed with error %d\n", err); + LOG_WRN("NusServer_Init failed with error %d", err); Bt_HandleError("NusServer_Init", err); } } @@ -57,7 +57,7 @@ void BtManager_InitBt() { void BtManager_StartBt() { BT_TRACE_AND_ASSERT("bm2"); - LOG_INF("Starting bluetooth services.\n"); + LOG_INF("Starting bluetooth services."); if (!Cfg.Bt_Enabled) { return; @@ -75,7 +75,7 @@ void BtManager_StopBt() { k_sleep(K_MSEC(10)); - printk("OOB: Stopping bluetooth services.\n"); + LOG_INF("OOB: Stopping bluetooth services."); EventScheduler_Unschedule(EventSchedulerEvent_BtStartScanningAndAdvertising); @@ -104,7 +104,7 @@ void BtManager_StopBt() { BtAdvertise_DisableAdvertisingIcon(); - printk("OOB: Bluetooth stopped\n"); + LOG_INF("OOB: Bluetooth stopped"); } @@ -132,7 +132,7 @@ void BtManager_StartScanningAndAdvertisingAsync(bool wasAggresive, const char* e expDelay = MIN(maxDelay, minDelay << aggressiveTries); } - LOG_INF("BtManager: Scheduling scan/adv in %dms. (%s)\n", expDelay, eventLabel); + LOG_INF("BtManager: Scheduling scan/adv in %dms. (%s)", expDelay, eventLabel); EventScheduler_Reschedule(Timer_GetCurrentTime() + expDelay, EventSchedulerEvent_BtStartScanningAndAdvertising, eventLabel); } @@ -183,7 +183,7 @@ void BtManager_StartScanningAndAdvertising() { } else if (shouldScan) { label = "scanning"; } - LOG_INF("BtManager: Start '%s' (%d).\n", label, try); + LOG_INF("BtManager: Start '%s' (%d).", label, try); } #ifdef CONFIG_BT_PERIPHERAL @@ -217,7 +217,7 @@ void BtManager_StartScanningAndAdvertising() { void BtManager_RestartBt() { BT_TRACE_AND_ASSERT("bm6"); - LOG_INF("Going to reset bluetooth stack\n"); + LOG_INF("Going to reset bluetooth stack"); BtManager_Restarting = true; int err; @@ -228,7 +228,7 @@ void BtManager_RestartBt() { err = bt_disable(); if (err) { - LOG_WRN("Bluetooth disable failed (err %d)\n", err); + LOG_WRN("Bluetooth disable failed (err %d)", err); Bt_HandleError("bt_disable", err); return; } @@ -238,13 +238,13 @@ void BtManager_RestartBt() { err = bt_hci_cmd_send(BT_HCI_OP_RESET, NULL); if (err) { - LOG_WRN("HCI Reset failed (err %d)\n", err); + LOG_WRN("HCI Reset failed (err %d)", err); Bt_HandleError("bt_hci_cmd_send", err); } err = bt_enable(bt_ready); if (err) { - LOG_WRN("Bluetooth init failed (err %d)\n", err); + LOG_WRN("Bluetooth init failed (err %d)", err); Bt_HandleError("bt_enable", err); } @@ -254,7 +254,7 @@ void BtManager_RestartBt() { BtManager_Restarting = false; - LOG_INF("Bluetooth subsystem restart finished\n"); + LOG_INF("Bluetooth subsystem restart finished"); } void BtManager_RestartBtAsync() { diff --git a/device/src/bt_pair.c b/device/src/bt_pair.c index 43ac347d1..44dd33373 100644 --- a/device/src/bt_pair.c +++ b/device/src/bt_pair.c @@ -86,7 +86,7 @@ void BtManager_EnterMode(pairing_mode_t mode, bool toggle) { if (mode == PairingMode_PairHid) { BtConn_MakeSpaceForHid(); } - BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in BtManager_EnterMode - start advertising"); + BtManager_StartScanningAndAdvertisingAsync(false, "BtManager_EnterMode - start advertising"); if (mode != defaultMode) { EventScheduler_Reschedule(k_uptime_get_32() + USER_PAIRING_TIMEOUT, EventSchedulerEvent_EndBtPairing, "User pairing mode timeout."); } @@ -157,7 +157,7 @@ void BtPair_EndPairing(bool success, const char* msg) { BtAdvertise_Stop(); #endif - BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in BtPair_EndPairing"); + BtManager_StartScanningAndAdvertisingAsync(false, "BtPair_EndPairing"); } struct delete_args_t { diff --git a/device/src/connections.c b/device/src/connections.c index 0ab7eba11..a6cdc8f57 100644 --- a/device/src/connections.c +++ b/device/src/connections.c @@ -16,6 +16,9 @@ #include "config_manager.h" #include "bt_pair.h" #include "usb_commands/usb_command_get_new_pairings.h" +#include + +LOG_MODULE_REGISTER(Conn, LOG_LEVEL_INF); connection_t Connections[ConnectionId_Count] = { [ConnectionId_UsbHidRight] = { .isAlias = true }, @@ -88,7 +91,7 @@ const char* Connections_GetStaticName(connection_id_t connectionId) { return getStaticName(connectionId); } -static void reportConnectionState(connection_id_t connectionId, const char* message) { +static void reportConnectionState(connection_id_t connectionId) { connectionId = resolveAliases(connectionId); bool isHostConnection = false; @@ -117,9 +120,9 @@ static void reportConnectionState(connection_id_t connectionId, const char* mess if (isHostConnection) { host_connection_t* hc = HostConnection(connectionId); - LogU("%s: %s%d(%.*s, %s)%s%s%s%s\n", message, name, connectionId - ConnectionId_HostConnectionFirst, hc->name.end - hc->name.start, hc->name.start, getStateString(Connections[connectionId].state), peerLabel, peerString, activeLabel, selectedLabel); + LOG_INF("%s: %d(%.*s, %s)%s%s%s%s", name, connectionId - ConnectionId_HostConnectionFirst, hc->name.end - hc->name.start, hc->name.start, getStateString(Connections[connectionId].state), peerLabel, peerString, activeLabel, selectedLabel); } else { - LogU("%s: %s(%s)%s%s%s%s\n", message, name, getStateString(Connections[connectionId].state), peerLabel, peerString, activeLabel, selectedLabel); + LOG_INF("%s: (%s)%s%s%s%s", name, getStateString(Connections[connectionId].state), peerLabel, peerString, activeLabel, selectedLabel); } } @@ -131,7 +134,7 @@ void Connections_ResetWatermarks(connection_id_t connectionId) { } void Connections_ReportState(connection_id_t connectionId) { - reportConnectionState(connectionId, "Conn state"); + reportConnectionState(connectionId); } connection_state_t Connections_GetState(connection_id_t connectionId) { @@ -144,7 +147,7 @@ void Connections_SetState(connection_id_t connectionId, connection_state_t state if ( Connections[connectionId].state != state ) { Connections[connectionId].state = state; - reportConnectionState(connectionId, "Conn state"); + reportConnectionState(connectionId); Connections_ResetWatermarks(connectionId); @@ -195,7 +198,7 @@ connection_type_t Connections_Type(connection_id_t connectionId) { return ConnectionType_Unknown; break; } - printk("Unhandled connectionId %d\n", connectionId); + LOG_ERR("Unhandled connectionId %d", connectionId); return ConnectionType_Unknown; } @@ -233,7 +236,7 @@ connection_target_t Connections_Target(connection_id_t connectionId) { case ConnectionId_Invalid: return ConnectionTarget_None; } - printk("Unhandled connectionId %d\n", connectionId); + LOG_ERR("Unhandled connectionId %d", connectionId); return ConnectionTarget_None; } @@ -432,7 +435,7 @@ void Connections_HandleSwitchover(connection_id_t connectionId, bool forceSwitch setDongleToStandby(ActiveHostConnectionId); } switchOver(connectionId); - reportConnectionState(connectionId, "Conn state"); + reportConnectionState(connectionId); } } @@ -442,7 +445,7 @@ void Connections_HandleSwitchover(connection_id_t connectionId, bool forceSwitch for (uint8_t i = ConnectionId_HostConnectionFirst; i <= ConnectionId_HostConnectionLast; i++) { if (Connections[i].state == ConnectionState_Ready) { switchOver(i); - reportConnectionState(i, "Conn state"); + reportConnectionState(i); break; } } diff --git a/device/src/device_state.c b/device/src/device_state.c index 442d208d1..4a89d2b4a 100644 --- a/device/src/device_state.c +++ b/device/src/device_state.c @@ -54,7 +54,7 @@ void handleStateTransition(connection_target_t remote, connection_id_t connectio EventVector_WakeMain(); } - BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in handleStateTransition - left was disconnected"); + BtManager_StartScanningAndAdvertisingAsync(false, "handleStateTransition - left was disconnected"); } break; case ConnectionTarget_Host: diff --git a/device/src/keyboard/charger.c b/device/src/keyboard/charger.c index 603c3ffd8..73baa1934 100644 --- a/device/src/keyboard/charger.c +++ b/device/src/keyboard/charger.c @@ -23,7 +23,7 @@ #include "battery_unloaded_calculator.h" #include -LOG_MODULE_REGISTER(Battery, LOG_LEVEL_INF); +LOG_MODULE_REGISTER(Battery, LOG_LEVEL_WRN); /** * chargerStatDt == 1 => (actually) not charging (e.g., fully charged, or no power provided) @@ -134,7 +134,7 @@ static uint16_t getVoltage() { } static void printState(battery_state_t* state) { - printk("Battery is present: %i, charging: %i, charger enabled: %i, powered: %d, at %imV (%i%%); automaton state %d\n", state->batteryPresent, state->batteryCharging, Charger_ChargingEnabled, state->powered, state->batteryVoltage, state->batteryPercentage, currentChargingAutomatonState); + LOG_INF("Battery is present: %i, charging: %i, charger enabled: %i, powered: %d, at %imV (%i%%); automaton state %d\n", state->batteryPresent, state->batteryCharging, Charger_ChargingEnabled, state->powered, state->batteryVoltage, state->batteryPercentage, currentChargingAutomatonState); } void Charger_PrintState() { @@ -158,7 +158,7 @@ static bool handleStateTransition(battery_manager_automaton_state_t newState) { currentChargingAutomatonState = newState; switch (newState) { case BatteryManagerAutomatonState_TurnOff: - // printk("Going to shut down. Measured voltage %d, computed voltage %d, powered %d\n", rawVoltage, batteryState->batteryVoltage, batteryState->powered); + // LOG_INF("Going to shut down. Measured voltage %d, computed voltage %d, powered %d\n", rawVoltage, batteryState->batteryVoltage, batteryState->powered); // PowerMode_ActivateMode(PowerMode_AutoShutDown, false, false); // break; case BatteryManagerAutomatonState_Powersaving: @@ -191,12 +191,12 @@ static bool updateChargerEnabled(battery_state_t *batteryState, battery_manager_ if (voltage > minThreshold) { BatteryManager_SetMaxCharge(newMaxVoltage); oldState = BatteryManagerAutomatonState_Charged; - printk("Charger stopped at %dmV. Setting as new 100%%.\n", voltage); + LOG_INF("Charger stopped at %dmV. Setting as new 100%%.\n", voltage); Charger_PrintState(); } else { BatteryManager_SetMaxCharge(BatteryManager_StandardUse.maxVoltage - 10); oldState = BatteryManagerAutomatonState_Charged; - printk("Charger stopped bellow %dmV. This is suspicious!\n", minThreshold); + LOG_INF("Charger stopped bellow %dmV. This is suspicious!\n", minThreshold); Charger_PrintState(); } } else if (newMaxVoltage > config->maxVoltage) { @@ -324,7 +324,7 @@ void Charger_UpdateBatteryState() { bool actuallyCharging = !gpio_pin_get_dt(&chargerStatDt); stateChanged |= setActuallyCharging(actuallyCharging && Charger_ChargingEnabled); - // printk("Going to measure voltage; charger formallyEnabled = %d, actuallyEnabled = %d, actuallyCharging = %d\n", Charger_ChargingEnabled, actuallyEnabled, actuallyCharging); + // LOG_INF("Going to measure voltage; charger formallyEnabled = %d, actuallyEnabled = %d, actuallyCharging = %d\n", Charger_ChargingEnabled, actuallyEnabled, actuallyCharging); setChargerEnPin(false); @@ -395,7 +395,7 @@ void chargerStatCallback(const struct device *port, struct gpio_callback *cb, gp StateSync_UpdateProperty(StateSyncPropertyId_Battery, &batteryState); } if (Shell.statLog) { - printk("STAT changed to %i\n", stat ? 1 : 0); + LOG_INF("STAT changed to %i\n", stat ? 1 : 0); } EventScheduler_Reschedule(Timer_GetCurrentTime() + CHARGER_STAT_PERIOD, EventSchedulerEvent_UpdateBattery, "charger - stat callback"); } @@ -404,10 +404,10 @@ bool Charger_ShouldRemainInDepletedMode(bool checkVoltage) { updatePowered(); if (checkVoltage) { uint16_t voltage = getVoltage(); - printk("Charger_ShouldRemainInDepletedMode called; powered = %d && raw voltage = %d\n", batteryState.powered, voltage); + LOG_INF("Charger_ShouldRemainInDepletedMode called; powered = %d && raw voltage = %d\n", batteryState.powered, voltage); return !batteryState.powered && voltage > 1000 && voltage < BatteryManager_GetCurrentBatteryConfig()->minWakeupVoltage; } else { - printk("Charger_ShouldRemainInDepletedMode called; powered = %d\n", batteryState.powered); + LOG_INF("Charger_ShouldRemainInDepletedMode called; powered = %d\n", batteryState.powered); return !batteryState.powered; } } @@ -416,7 +416,7 @@ bool Charger_ShouldEnterDepletedMode() { updatePowered(); uint16_t voltage = getVoltage(); - printk("Charger_ShouldEnterDepletedMode called; powered = %d && raw voltage = %d\n", batteryState.powered, voltage); + LOG_INF("Charger_ShouldEnterDepletedMode called; powered = %d && raw voltage = %d\n", batteryState.powered, voltage); return !batteryState.powered && voltage < BatteryManager_GetCurrentBatteryConfig()->minVoltage; } diff --git a/device/src/keyboard/oled/screens/notification_screen.c b/device/src/keyboard/oled/screens/notification_screen.c index 331728fad..e94c18a17 100644 --- a/device/src/keyboard/oled/screens/notification_screen.c +++ b/device/src/keyboard/oled/screens/notification_screen.c @@ -4,6 +4,9 @@ #include "screen_manager.h" #include #include "event_scheduler.h" +#include + +LOG_MODULE_REGISTER(Notify, LOG_LEVEL_INF); static widget_t notificationWidget; @@ -17,7 +20,7 @@ void NotificationScreen_Init(void) { } void NotificationScreen_NotifyFor(const char* message, uint16_t duration) { - printk("Notification: %s\n", message); + LOG_INF("Notification: %s", message); TextWidget_SetText(¬ificationWidget, message); ScreenManager_ActivateScreen(ScreenId_Notification); diff --git a/device/src/nus_client.c b/device/src/nus_client.c index 6d02b0f0d..18b4e0884 100644 --- a/device/src/nus_client.c +++ b/device/src/nus_client.c @@ -25,7 +25,7 @@ #include "trace.h" #include -LOG_MODULE_DECLARE(Bt); +LOG_MODULE_REGISTER(NusClient, LOG_LEVEL_INF); static struct bt_nus_client nus_client; @@ -35,7 +35,7 @@ static K_SEM_DEFINE(nusBusy, NUS_SLOTS, NUS_SLOTS); static void ble_data_sent(struct bt_nus_client *nus, uint8_t err, const uint8_t *const data, uint16_t len) { k_sem_give(&nusBusy); if (err) { - LOG_WRN("Bt: ATT error code: 0x%02X\n", err); + LOG_WRN("Bt: ATT error code: 0x%02X", err); } } @@ -66,25 +66,25 @@ static void discovery_complete(struct bt_gatt_dm *dm, void *context) { err = bt_nus_handles_assign(dm, nus); if (err) { - LOG_WRN("Could not assign NUS handles (err %d)\n", err); + LOG_WRN("Could not assign NUS handles (err %d)", err); return; } err = bt_nus_subscribe_receive(nus); if (err) { - LOG_WRN("Could not subscribe to NUS notifications (err %d)\n", err); + LOG_WRN("Could not subscribe to NUS notifications (err %d)", err); return; } err = bt_gatt_dm_data_release(dm); if (err) { - LOG_WRN("Could not release the discovery data (err %d)\n", err); + LOG_WRN("Could not release the discovery data (err %d)", err); return; } - LOG_INF("NUS connection with %s successfully established\n", GetPeerStringByConn(nus->conn)); + LOG_DBG("NUS connection with %s successfully established", GetPeerStringByConn(nus->conn)); if (DEVICE_ID == DeviceId_Uhk80_Right) { Bt_SetConnectionConfigured(Peers[PeerIdLeft].conn); @@ -95,11 +95,11 @@ static void discovery_complete(struct bt_gatt_dm *dm, void *context) { } static void discovery_service_not_found(struct bt_conn *conn, void *context) { - LOG_WRN("Service not found\n"); + LOG_WRN("Service not found"); } static void discovery_error(struct bt_conn *conn, int err, void *context) { - LOG_WRN("Error while discovering GATT database: (%d)\n", err); + LOG_WRN("Error while discovering GATT database: (%d)", err); } struct bt_gatt_dm_cb discovery_cb = { @@ -111,15 +111,15 @@ struct bt_gatt_dm_cb discovery_cb = { static void gatt_discover(struct bt_conn *conn) { int err = bt_gatt_dm_start(conn, BT_UUID_NUS_SERVICE, &discovery_cb, &nus_client); if (err) { - LOG_WRN("could not start the discovery procedure, error code: %d\n", err); + LOG_WRN("could not start the discovery procedure, error code: %d", err); } } static void exchange_func(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params) { if (err) { - LOG_WRN("MTU exchange failed with %s (err %u)\n", GetPeerStringByConn(conn), err); + LOG_WRN("MTU exchange failed with %s (err %u)", GetPeerStringByConn(conn), err); } else { - LOG_INF("MTU exchange done with %s\n", GetPeerStringByConn(conn)); + LOG_DBG("MTU exchange done with %s", GetPeerStringByConn(conn)); } } @@ -129,14 +129,14 @@ void NusClient_Connect(struct bt_conn *conn) { exchange_params.func = exchange_func; int err = bt_gatt_exchange_mtu(conn, &exchange_params); if (err) { - LOG_WRN("MTU exchange failed with %s, err %d\n", GetPeerStringByConn(conn), err); + LOG_WRN("MTU exchange failed with %s, err %d", GetPeerStringByConn(conn), err); } gatt_discover(conn); err = bt_scan_stop(); if (err && (err != -EALREADY)) { - LOG_WRN("Stop LE scan failed (err %d)\n", err); + LOG_WRN("Stop LE scan failed (err %d)", err); } } @@ -160,11 +160,11 @@ void NusClient_Init(void) { err = bt_nus_client_init(&nus_client, &init); if (err) { - LOG_WRN("NUS Client initialization failed (err %d)\n", err); + LOG_WRN("NUS Client initialization failed (err %d)", err); return; } - LOG_INF("NUS Client module initialized\n"); + LOG_INF("NUS Client module initialized"); } bool NusClient_Availability(messenger_availability_op_t operation) { @@ -186,7 +186,7 @@ static void send_raw_buffer(const uint8_t *data, uint16_t len) { int err = bt_nus_client_send(&nus_client, data, len); if (err) { k_sem_give(&nusBusy); - LOG_WRN("Client failed to send data over BLE connection (err %d)\n", err); + LOG_WRN("Client failed to send data over BLE connection (err %d)", err); } } @@ -209,7 +209,7 @@ void NusClient_SendMessage(message_t* msg) { } if (bufferIdx + msg->len > MAX_LINK_PACKET_LENGTH) { - LOG_WRN("Message is too long for NUS packets! [%i, %i, ...]\n", buffer[0], buffer[1]); + LOG_WRN("Message is too long for NUS packets! [%i, %i, ...]", buffer[0], buffer[1]); Trace_Printc("E1"); Trace_Printc("r2"); return; diff --git a/device/src/nus_server.c b/device/src/nus_server.c index 669c14be1..d67544a4d 100644 --- a/device/src/nus_server.c +++ b/device/src/nus_server.c @@ -13,7 +13,7 @@ #include #include "bt_manager.h" -LOG_MODULE_DECLARE(Bt); +LOG_MODULE_REGISTER(NusServer, LOG_LEVEL_INF); #define NUS_SLOTS 2 @@ -51,7 +51,7 @@ static void send_enabled(enum bt_nus_send_status status) { if (status == BT_NUS_SEND_STATUS_ENABLED) { // in theory, NUS is ready. In practice, it is once we receive a message from the client. - LOG_INF("NUS peripheral connection is ready.\n"); + LOG_DBG("NUS peripheral connection is ready."); } } @@ -64,11 +64,11 @@ static struct bt_nus_cb nus_cb = { int NusServer_Init(void) { int err = bt_nus_init(&nus_cb); if (err) { - LOG_WRN("Failed to initialize UART service (err: %d)\n", err); + LOG_WRN("Failed to initialize UART service (err: %d)", err); return err; } - LOG_INF("NUS Server module initialized.\n"); + LOG_INF("NUS Server module initialized."); return 0; } @@ -85,7 +85,7 @@ static void send_raw_buffer(const uint8_t *data, uint16_t len, struct bt_conn* c int err = bt_nus_send(conn, data, len); if (err) { k_sem_give(&nusBusy); - LOG_WRN("Failed to send data over BLE connection (err: %d)\n", err); + LOG_WRN("Failed to send data over BLE connection (err: %d)", err); } } @@ -122,7 +122,7 @@ void NusServer_SendMessageTo(message_t* msg, struct bt_conn* conn) { } if (bufferIdx + msg->len > MAX_LINK_PACKET_LENGTH) { - LOG_WRN("Message is too long for NUS packets! [%i, %i, ...]\n", buffer[0], buffer[1]); + LOG_WRN("Message is too long for NUS packets! [%i, %i, ...]", buffer[0], buffer[1]); return; } diff --git a/device/src/state_sync.c b/device/src/state_sync.c index 02c4a12bf..6b3bd0094 100644 --- a/device/src/state_sync.c +++ b/device/src/state_sync.c @@ -36,6 +36,9 @@ #include "versioning.h" #include "event_scheduler.h" #include "macro_events.h" +#include + +LOG_MODULE_REGISTER(StateSync, LOG_LEVEL_INF); #define WAKE(TID) if (TID != 0) { k_wakeup(TID); } @@ -72,10 +75,10 @@ static void wake(k_tid_t tid) { if (tid != 0) { k_wakeup(tid); // if (DEBUG_MODE) { - // LogU("StateSync woke up %p\n", tid); + // LogU("StateSync woke up %p", tid); // } } else if (k_uptime_get_32() > 5000) { - printk("Skipping wake up, tid is 0"); + LOG_INF("Skipping wake up, tid is 0"); } } @@ -425,7 +428,7 @@ static void receiveProperty(device_id_t src, state_sync_prop_id_t propId, const case StateSyncPropertyId_Battery: WIDGET_REFRESH(&StatusWidget); { - printk("Batteries: %d%% %d%% (%d %d)\n" + LOG_INF("Batteries: %d%% %d%% (%d %d)" , SyncLeftHalfState.battery.batteryPercentage, SyncRightHalfState.battery.batteryPercentage , SyncLeftHalfState.battery.batteryVoltage, SyncRightHalfState.battery.batteryVoltage ); @@ -517,11 +520,11 @@ static void receiveProperty(device_id_t src, state_sync_prop_id_t propId, const } break; case StateSyncPropertyId_ZeroDummy: - printk("Received an invalid state sync property message: %d %d %d | %d %d | %d %d %d %d %d\n", data[-5], data[-4], data[-3], data[-2], data[-1], data[0], data[1], data[2], data[3], data[4]); + LOG_INF("Received an invalid state sync property message: %d %d %d | %d %d | %d %d %d %d %d", data[-5], data[-4], data[-3], data[-2], data[-1], data[0], data[1], data[2], data[3], data[4]); break; case StateSyncPropertyId_BatteryStationaryMode: //for both local and remote - printk("Setting battery mode to %d\n", Cfg.BatteryStationaryMode); + LOG_INF("Setting battery mode to %d", Cfg.BatteryStationaryMode); EventScheduler_Schedule(Timer_GetCurrentTime() + 1000, EventSchedulerEvent_UpdateBattery, "state sync"); break; case StateSyncPropertyId_PowerMode: @@ -530,8 +533,8 @@ static void receiveProperty(device_id_t src, state_sync_prop_id_t propId, const } break; default: - printk("Property %i ('%s') has no receive handler. If this is correct, please add a " - "separate empty case...\n", + LOG_ERR("Property %i ('%s') has no receive handler. If this is correct, please add a " + "separate empty case...", propId, prop->name); break; } @@ -926,7 +929,7 @@ void StateSync_Init() { } void StateSync_ResetRightLeftLink(bool bidirectional) { - printk("Resetting left right link! %s\n", bidirectional ? "Bidirectional" : "Unidirectional"); + LOG_INF("Resetting left right link! %s", bidirectional ? "Bidirectional" : "Unidirectional"); StateSync_LeftResetCounter++; if (bidirectional) { invalidateProperty(StateSyncPropertyId_ResetRightLeftLink); @@ -957,7 +960,7 @@ void StateSync_ResetRightLeftLink(bool bidirectional) { void StateSync_ResetRightDongleLink(bool bidirectional) { StateSync_DongleResetCounter++; - // printk("Resetting dongle right link! %s\n", bidirectional ? "Bidirectional" : "Unidirectional"); + // LOG_INF("Resetting dongle right link! %s", bidirectional ? "Bidirectional" : "Unidirectional"); if (bidirectional) { invalidateProperty(StateSyncPropertyId_ResetRightDongleLink); } diff --git a/right/src/host_connection.c b/right/src/host_connection.c index cea04fff7..023dc14e2 100644 --- a/right/src/host_connection.c +++ b/right/src/host_connection.c @@ -83,7 +83,7 @@ void HostConnection_SetSelectedConnection(uint8_t connectionId) { void HostConnection_Unselect() { HostConnection_SetSelectedConnection(ConnectionId_Invalid); - BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in HostConnection_Unselect"); + BtManager_StartScanningAndAdvertisingAsync(false, "HostConnection_Unselect"); } static void selectConnection(uint8_t connectionId) { diff --git a/right/src/macros/set_command.c b/right/src/macros/set_command.c index e6411590f..2bd21ee9b 100644 --- a/right/src/macros/set_command.c +++ b/right/src/macros/set_command.c @@ -382,12 +382,12 @@ static macro_variable_t bluetooth(parser_context_t* ctx, set_command_action_t ac ASSIGN_BOOL(Cfg.Bt_AlwaysAdvertiseHid); #if DEVICE_IS_UHK80_RIGHT BtManager_EnterMode(PairingMode_Default, false); - BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in set_command - alwaysAdvertiseHid changed"); + BtManager_StartScanningAndAdvertisingAsync(false, "set_command - alwaysAdvertiseHid changed"); #endif } else if (ConsumeToken(ctx, "directedAdvertisingAllowed")) { ASSIGN_BOOL(Cfg.Bt_DirectedAdvertisingAllowed); #ifdef __ZEPHYR__ - BtManager_StartScanningAndAdvertisingAsync(false, "StartScanningAndAdvertisingAsync in set_command - directedAdvertisingAllowed changed"); + BtManager_StartScanningAndAdvertisingAsync(false, "set_command - directedAdvertisingAllowed changed"); #endif } else { Macros_ReportErrorTok(ctx, "Parameter not recognized:"); From d359f2fa8c975f40fec35570d90310c51b04de02 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 19 Jan 2026 15:09:16 +0100 Subject: [PATCH 45/46] Bt: advertisement exponentail backoff - adjustments. --- device/src/bt_manager.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/device/src/bt_manager.c b/device/src/bt_manager.c index 894c0d06e..cff0ceb04 100644 --- a/device/src/bt_manager.c +++ b/device/src/bt_manager.c @@ -125,11 +125,14 @@ void BtManager_StartScanningAndAdvertisingAsync(bool wasAggresive, const char* e if (weArePairing || weAreSwitching) { expDelay = minDelay; } else { - aggressiveTries = wasAggresive ? aggressiveTries + 1 : 0; + if (wasAggresive) { + aggressiveTries = aggressiveTries + 1; + } else { + aggressiveTries = 0; + } aggressiveTries = MAX(0, aggressiveTries); - aggressiveTries = MIN(aggressiveTries, 16); - - expDelay = MIN(maxDelay, minDelay << aggressiveTries); + aggressiveTries = MIN(aggressiveTries, 64); + expDelay = MIN(maxDelay, minDelay << (aggressiveTries/2)); } LOG_INF("BtManager: Scheduling scan/adv in %dms. (%s)", expDelay, eventLabel); From a8f13ed1c8a082afefbe7fb77132c72abdb6833a Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Mon, 19 Jan 2026 15:15:34 +0100 Subject: [PATCH 46/46] connection quotas: Add comment. --- device/prj.conf.overlays/nrf_shared.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/device/prj.conf.overlays/nrf_shared.conf b/device/prj.conf.overlays/nrf_shared.conf index f2f2c41a1..1ffb6afb6 100644 --- a/device/prj.conf.overlays/nrf_shared.conf +++ b/device/prj.conf.overlays/nrf_shared.conf @@ -24,6 +24,7 @@ CONFIG_BT_SMP=y CONFIG_BT_FILTER_ACCEPT_LIST=y # increase these to make multiple connections more reliable +# this is a generic ai advice. CONFIG_BT_ATT_TX_COUNT=10 CONFIG_BT_CONN_TX_MAX=6 CONFIG_BT_L2CAP_TX_BUF_COUNT=12