From 51c17b7e107860e8ddfc42bc3297261a2ac67168 Mon Sep 17 00:00:00 2001 From: Ilia Ki Date: Thu, 16 Jul 2026 03:29:54 +0700 Subject: [PATCH] Fix dongle reconnect after its DFU --- .../trackers/hid/AndroidHIDManager.kt | 13 +++++++--- .../tracking/trackers/hid/HIDCommon.kt | 26 +++++++++++++++++-- .../tracking/trackers/hid/HIDDevice.kt | 2 +- .../trackers/hid/DesktopHIDManager.kt | 21 ++++++++++----- 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/server/android/src/main/java/dev/slimevr/android/tracking/trackers/hid/AndroidHIDManager.kt b/server/android/src/main/java/dev/slimevr/android/tracking/trackers/hid/AndroidHIDManager.kt index f62e318ee3..ab6953e01f 100644 --- a/server/android/src/main/java/dev/slimevr/android/tracking/trackers/hid/AndroidHIDManager.kt +++ b/server/android/src/main/java/dev/slimevr/android/tracking/trackers/hid/AndroidHIDManager.kt @@ -75,8 +75,12 @@ class AndroidHIDManager( synchronized(this.devices) { for (id in it) { val device = this.devices[id] + device.hidId = -1 for (value in device.trackers.values) { - if (value.status == TrackerStatus.DISCONNECTED) value.status = TrackerStatus.OK + if (value.status == TrackerStatus.OK || value.status == TrackerStatus.TIMED_OUT) { + value.status = TrackerStatus.DISCONNECTED + } + value.setSleepTime(Long.MAX_VALUE) } } } @@ -114,16 +118,17 @@ class AndroidHIDManager( synchronized(this.devices) { for (id in it) { val device = this.devices[id] + device.hidId = -1 for (value in device.trackers.values) { - if (value.status == TrackerStatus.OK) { - value.status = - TrackerStatus.DISCONNECTED + if (value.status == TrackerStatus.OK || value.status == TrackerStatus.TIMED_OUT) { + value.status = TrackerStatus.DISCONNECTED } } } } this.devicesByHID.remove(hidDevice) + this.lastDataByHID.remove(hidDevice) val oldConn = this.connByHID.remove(hidDevice) val serial = oldConn?.serialNumber ?: "Unknown" diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDCommon.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDCommon.kt index cfe25d3899..43128f71e6 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDCommon.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDCommon.kt @@ -69,6 +69,28 @@ class HIDCommon { deviceList: MutableList, ): HIDDevice? { synchronized(hidDevices) { + // Registration (packet 255) carries the stable MAC. After a hub reboot, BLE + // slots are reassigned, so match by MAC first and rebind hidId. + if (deviceName != null) { + val byMac = deviceList.map { hidDevices[it] }.find { + it.name == deviceName || it.hardwareIdentifier == deviceName + } + if (byMac != null) { + if (byMac.hidId != deviceId) { + for (other in deviceList.map { hidDevices[it] }) { + if (other !== byMac && other.hidId == deviceId) { + other.hidId = -1 + } + } + LogManager.info( + "[TrackerServer] Rebound device $deviceName to id $deviceId (was ${byMac.hidId}) for ${hidSerialNumber ?: "Unknown HID Device"}", + ) + byMac.hidId = deviceId + } + return byMac + } + } + deviceList.map { hidDevices[it] }.find { it.hidId == deviceId }?.let { return it } if (deviceName == null) { // not registered yet return null @@ -171,8 +193,8 @@ class HIDCommon { return } - if (tracker.status == TrackerStatus.TIMED_OUT) { - // If tracker was previously sleeping/shutdown, reset the sleep time and status + if (tracker.status == TrackerStatus.TIMED_OUT || tracker.status == TrackerStatus.DISCONNECTED) { + // Revive after sleep, USB reattach, or hub reboot once real packets arrive. // If there is some other error, the relevant packet should set it a little later tracker.setSleepTime(Long.MAX_VALUE) tracker.status = TrackerStatus.OK diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDDevice.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDDevice.kt index 506a1d6979..8d43438474 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDDevice.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDDevice.kt @@ -7,7 +7,7 @@ import dev.slimevr.tracking.trackers.udp.BoardType import dev.slimevr.tracking.trackers.udp.MCUType import dev.slimevr.tracking.trackers.udp.MagnetometerStatus -class HIDDevice(val hidId: Int) : Device(DeviceOrigin.HID) { +class HIDDevice(var hidId: Int) : Device(DeviceOrigin.HID) { override var hardwareIdentifier: String = "Unknown" override var boardType: BoardType = BoardType.UNKNOWN override var mcuType: MCUType = MCUType.UNKNOWN diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/tracking/trackers/hid/DesktopHIDManager.kt b/server/desktop/src/main/java/dev/slimevr/desktop/tracking/trackers/hid/DesktopHIDManager.kt index 6e59fe1c31..9f80f2b145 100644 --- a/server/desktop/src/main/java/dev/slimevr/desktop/tracking/trackers/hid/DesktopHIDManager.kt +++ b/server/desktop/src/main/java/dev/slimevr/desktop/tracking/trackers/hid/DesktopHIDManager.kt @@ -60,11 +60,15 @@ class DesktopHIDManager(name: String, private val trackersConsumer: Consumer 100) { // try to reopen device if no data was received recently (about >100ms) + // Only reopen closed handles. Calling open() on an already-open device every + // second during hub BLE reconnect (many seconds of no HID data) breaks the session. + if (lastData > 100 && device.isClosed) { if (lastData < 10000) { LogManager.info("[TrackerServer] Reopening device ${device.serialNumber} after no data received") lastDataByHID[device] = 10000 // flag once