diff --git a/device/src/bt_conn.c b/device/src/bt_conn.c index 636c57ffa..8d7a3e7e2 100644 --- a/device/src/bt_conn.c +++ b/device/src/bt_conn.c @@ -498,17 +498,18 @@ static bool isWanted(struct bt_conn *conn, connection_id_t connectionId, connect bool isSelectedConnection = BtAddrEq(addr, &HostConnection(CurrentHostConnectionId)->bleAddress); bool isSelectedSlotEmpty = Connections_Type(CurrentHostConnectionId) == ConnectionType_Empty; bool isPeerConnection = connectionType == ConnectionType_NusLeft || connectionType == ConnectionType_NusRight; + bool isOobPairingPeer = BtPair_OobPairingInProgress && BtAddrEq(addr, &BtPair_GetRemoteOob()->addr); bool weHaveSlotToSpare = BtConn_UnusedPeripheralConnectionCount() > 1 || !shouldReserveForCurrentConnection(); bool result = false; if (Cfg.Bt_AlwaysAdvertise) { - result = isPeerConnection || isSelectedConnection || isSelectedSlotEmpty || weHaveSlotToSpare; + result = isPeerConnection || isSelectedConnection || isSelectedSlotEmpty || isOobPairingPeer || weHaveSlotToSpare; } else { - result = isPeerConnection || isSelectedConnection || isSelectedSlotEmpty; + result = isPeerConnection || isSelectedConnection || isSelectedSlotEmpty || isOobPairingPeer; } if (!result) { - LOG_INF(" Not wanted: haveSlot: %d, isSelected: %d (selected %d, this %d (%d)), isPeer: %d, isEmptySlot: %d", weHaveSlotToSpare, isSelectedConnection, CurrentHostConnectionId, connectionId, connectionType, isPeerConnection, isSelectedSlotEmpty); + LOG_INF(" Not wanted: haveSlot: %d, isSelected: %d (selected %d, this %d (%d)), isPeer: %d, isEmptySlot: %d, isOobPeer: %d", weHaveSlotToSpare, isSelectedConnection, CurrentHostConnectionId, connectionId, connectionType, isPeerConnection, isSelectedSlotEmpty, isOobPairingPeer); } return result; } @@ -1154,7 +1155,6 @@ void BtConn_ReserveConnections() { BtConn_DisconnectAllUnidentified(); uint8_t unusedConnectionCount = BtConn_UnusedPeripheralConnectionCount(); - bool selectedConnectionIsBleHid = Connections_Type(CurrentHostConnectionId) == ConnectionType_BtHid; if (unusedConnectionCount == 0) { disconnectOldestHost(); diff --git a/device/src/connections.c b/device/src/connections.c index 056d912d5..4df72d396 100644 --- a/device/src/connections.c +++ b/device/src/connections.c @@ -454,6 +454,18 @@ static void setDongleToStandby(connection_id_t connectionId) { } } +static void disconnectOldHost(connection_id_t oldConnectionId) { + switch (Connections_Type(oldConnectionId)) { + case ConnectionType_BtHid: + case ConnectionType_NusDongle: + LOG_INF("Switchover: disconnecting the old host %d", oldConnectionId); + BtConn_DisconnectOne(oldConnectionId); + break; + default: + break; + } +} + static void updateLastConnection(connection_id_t lastConnId, connection_id_t newConnId) { if ( LastHostConnectionId != lastConnId @@ -477,7 +489,12 @@ static connection_id_t findReadySwitchoverHost(void) { static void switchOver(connection_id_t connectionId, bool explicitlySelected) { if (connectionId != CurrentHostConnectionId) { - setDongleToStandby(CurrentHostConnectionId); + if (Cfg.Bt_KeepConnectionsAlive || Cfg.Bt_AlwaysAdvertise) { + setDongleToStandby(CurrentHostConnectionId); + } else { + // The old host occupies a peripheral slot that the new host may need. + disconnectOldHost(CurrentHostConnectionId); + } } updateLastConnection(CurrentHostConnectionId, connectionId); diff --git a/device/src/messenger.c b/device/src/messenger.c index fd05da98c..8bad0d283 100644 --- a/device/src/messenger.c +++ b/device/src/messenger.c @@ -227,14 +227,6 @@ static void processSyncablePropertyDongle(device_id_t src, const uint8_t* data, return; } -#if DEVICE_IS_UHK_DONGLE - if (!Connections_IsCurrentHostAwake()) { - // We received a report to relay but our USB host is suspended - ask it - // to wake up instead of just failing to deliver. - USB_RemoteWakeup(); - } -#endif - errno_t ATTR_UNUSED ret = sendDongleReport(propertyId, message); #if DEVICE_IS_UHK_DONGLE diff --git a/doc-dev/reference-manual.md b/doc-dev/reference-manual.md index fb1372e54..f604a1189 100644 --- a/doc-dev/reference-manual.md +++ b/doc-dev/reference-manual.md @@ -192,6 +192,8 @@ COMMAND = set leds.fadeTimeout COMMAND = set leds.{keyBacklightFadeTimeout|keyBacklightFadeBatteryTimeout|displayFadeTimeout|displayFadeBatteryTimeout} COMMAND = set battery.chargeLimit { full | optimizeHealth } COMMAND = set bluetooth.enabled BOOL +COMMAND = set bluetooth.alwaysAdvertise BOOL +COMMAND = set bluetooth.keepConnectionsAlive BOOL COMMAND = set modifierLayerTriggers.{shift|alt|super|ctrl} {left|right|both} COMMAND = ¯oArg. CONDITION = @@ -319,7 +321,6 @@ COMMAND = set leds.alwaysOn BOOL COMMAND = set bluetooth.peripheralConnectionCount INT COMMAND = set bluetooth.minAdvertisingDelay INT COMMAND = set bluetooth.directedAdvertisingAllowed BOOL -COMMAND = set bluetooth.alwaysAdvertise BOOL COMMAND = set devMode BOOL COMMAND = set log.sink.usb BOOL COMMAND = set log.sink.oled BOOL @@ -766,6 +767,10 @@ Key actions can be parametrized with macro arguments. These arguments can be exp - `leds.brightness <0-1 multiple of default (FLOAT)>` allows scaling default brightness. E.g., `0.5` will dim the entire keyboard to half of the default values that are configured in Agent - `leds.fadeTimeout ` will make uhk turn off all leds after the configured interval. (This is an alias that sets all of `{keyBacklightFadeTimeout|keyBacklightFadeBatteryTimeout|displayFadeTimeout|displayFadeBatteryTimeout}`) +- bluetooth: + - `set bluetooth.alwaysAdvertise BOOL` makes uhk keep advertising even when the current host is already connected, as long as there is a free peripheral connection slot. This lets other configured hosts (dongles, ble hids) connect in the background, so that switching to them later is instantaneous. Default is off. Implies `bluetooth.keepConnectionsAlive`. + - `set bluetooth.keepConnectionsAlive BOOL` keeps the old host connected when you switch away from it. By default, switching hosts disconnects the previous ble host or dongle in order to free its peripheral connection slot for the new host. Default is off. + - modifier layer triggers: - `set modifierLayerTriggers.{shift|alt|super|ctrl} {left|right|both}` controls whether modifier layers are triggered by left or right or either of the modifiers. diff --git a/right/src/config_manager.c b/right/src/config_manager.c index 867c3c04f..04875631c 100644 --- a/right/src/config_manager.c +++ b/right/src/config_manager.c @@ -279,6 +279,7 @@ const config_t DefaultCfg = (config_t){ #endif .Bt_Enabled = true, .Bt_AlwaysAdvertise = false, + .Bt_KeepConnectionsAlive = false, .Bt_MaxPeripheralConnections = 3, .Bt_MinAdvertisingDelay = 250, .Bt_DirectedAdvertisingAllowed = false, diff --git a/right/src/config_manager.h b/right/src/config_manager.h index 993fc1d29..5e00d34c4 100644 --- a/right/src/config_manager.h +++ b/right/src/config_manager.h @@ -97,6 +97,7 @@ // bluetooth bool Bt_AlwaysAdvertise; + bool Bt_KeepConnectionsAlive; bool Bt_DirectedAdvertisingAllowed; bool Bt_Enabled; diff --git a/right/src/hid/transport.cpp b/right/src/hid/transport.cpp index a9699b58d..aca67e249 100644 --- a/right/src/hid/transport.cpp +++ b/right/src/hid/transport.cpp @@ -127,7 +127,11 @@ static report_sink_t determineSink() LOG_WRN("Can't send report - selected connection is not ready!\n"); Connections_HandleSwitchover(ConnectionId_Invalid, false); if (!Connections_IsReady(CurrentHostConnectionId)) { - return ReportSink_Usb; + if (connectionType == ConnectionType_UsbHidRight) { + return ReportSink_Usb; + } else { + return ReportSink_BlackHole; + } } } @@ -252,6 +256,9 @@ extern "C" errno_t Hid_SendKeyboardReport(const hid_keyboard_report_t *report) TestHooks_CaptureReport(report); Hid_KeyboardReportSentCallback(ReportSink_Usb); break; + case ReportSink_BlackHole: + err = -EHOSTUNREACH; + break; default: LOG_WRN("Unhandled and unexpected switch state!\n"); err = -EHOSTUNREACH; @@ -324,6 +331,9 @@ extern "C" errno_t Hid_SendMouseReport(const hid_mouse_report_t *report) } break; #endif + case ReportSink_BlackHole: + err = -EHOSTUNREACH; + break; default: LOG_WRN("Unhandled and unexpected switch state!\n"); err = -EHOSTUNREACH; @@ -390,6 +400,9 @@ extern "C" errno_t Hid_SendControlsReport(const hid_controls_report_t *report) } break; #endif + case ReportSink_BlackHole: + err = -EHOSTUNREACH; + break; default: LOG_WRN("Unhandled and unexpected switch state!\n"); err = -EHOSTUNREACH; diff --git a/right/src/hid/transport.h b/right/src/hid/transport.h index fc588add8..cac786037 100644 --- a/right/src/hid/transport.h +++ b/right/src/hid/transport.h @@ -20,6 +20,7 @@ typedef enum { ReportSink_BleHid, ReportSink_Dongle, ReportSink_TestSuite, + ReportSink_BlackHole, } report_sink_t; typedef enum diff --git a/right/src/macros/set_command.c b/right/src/macros/set_command.c index e52c5855c..91a5ca91a 100644 --- a/right/src/macros/set_command.c +++ b/right/src/macros/set_command.c @@ -383,6 +383,8 @@ static macro_variable_t bluetooth(parser_context_t* ctx, set_command_action_t ac #ifdef __ZEPHYR__ BtManager_StartScanningAndAdvertisingAsync(false, "set_command - alwaysAdvertise changed"); #endif + } else if (ConsumeToken(ctx, "keepConnectionsAlive")) { + ASSIGN_BOOL(Cfg.Bt_KeepConnectionsAlive); } else if (ConsumeToken(ctx, "directedAdvertisingAllowed")) { ASSIGN_BOOL(Cfg.Bt_DirectedAdvertisingAllowed); #ifdef __ZEPHYR__ diff --git a/right/src/usb_state.c b/right/src/usb_state.c index 348a54b78..8ee666cb3 100644 --- a/right/src/usb_state.c +++ b/right/src/usb_state.c @@ -32,7 +32,7 @@ static void recalculateConnectionState(void) { #if DEVICE_IS_UHK_DONGLE StateSync_UpdateProperty(StateSyncPropertyId_DongleHostAwake, &UsbState_Awake); #elif defined(__ZEPHYR__) - Connections_SetStateAsync(ConnectionId_UsbHidRight, UsbState_Awake ? ConnectionState_Ready : ConnectionState_Disconnected); + Connections_SetStateAsync(ConnectionId_UsbHidRight, UsbState_TransportUp ? ConnectionState_Ready : ConnectionState_Disconnected); EventScheduler_Schedule(Timer_GetCurrentTime(), EventSchedulerEvent_PowerModeUpdate, "no host short wakeup"); WIDGET_REFRESH(&TargetWidget); #else