Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions right/src/event_scheduler.c
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ static void processEvt(event_scheduler_event_t evt)
BtConn_KickHid();
#endif
break;
case EventSchedulerEvent_SendUsbReports:
EventVector_Set(EventVector_SendUsbReports);
break;
case EventSchedulerEvent_CheckLeftBleVsUart:
#if DEVICE_IS_UHK80_LEFT
BtManager_CheckLeftBleVsUart();
Expand Down
1 change: 1 addition & 0 deletions right/src/event_scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
EventSchedulerEvent_UnselectHostConnection,
EventSchedulerEvent_OneShotTimeout,
EventSchedulerEvent_KickHid,
EventSchedulerEvent_SendUsbReports,
EventSchedulerEvent_CheckLeftBleVsUart,
EventSchedulerEvent_Count
} event_scheduler_event_t;
Expand Down
48 changes: 38 additions & 10 deletions right/src/usb_report_updater.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ LOG_MODULE_REGISTER(UsbReports, LOG_LEVEL_INF);
#include "stubs.h"
#endif


bool TestUsbStack = false;

static key_action_cached_t actionCache[SLOT_COUNT][MAX_KEY_COUNT_PER_MODULE];
Expand All @@ -81,6 +82,8 @@ uint32_t UsbReportWindowEstimate = 0;
// latency.
#define USB_REPORT_WINDOW_LOOKAHEAD_MS 6

#define USB_RESEND_DELAY_MS MAX(10, Cfg.KeystrokeDelay)

volatile uint8_t UsbReportUpdateSemaphore = 0;

// Modifiers can be applied as one of the following classes
Expand Down Expand Up @@ -879,24 +882,31 @@ uint32_t UsbReportUpdateCounter;

uint32_t UpdateUsbReports_LastUpdateTime = 0;
uint32_t lastBasicReportTime = 0;
uint32_t retryThrottleTime = 0;
static uint8_t keyboardRetries = 0;
static bool keyboardNeedsResending = false;
static uint8_t controlsRetries = 0;
static bool controlsNeedsResending = false;
static uint8_t mouseRetries = 0;
static bool mouseNeedsResending = false;

// Try resending a report for 512ms. Give up if it doesn't succeed by then.
// Try resending a report for maxDelay ms. Give up if it doesn't succeed by then.
// Once given up, stay given up until some statusOk is seen, so a full queue doesn't
// freeze for queueLength * maxDelay on replay.
bool ShouldResendReport(bool statusOk, uint8_t* counter) {
static bool givenUp = false;

if (statusOk) {
*counter = 0;
givenUp = false;
return false;
}

// keep this low, since the actual delay this causes with a full queue is
// queueLength * maxDelay
const uint16_t maxDelay = 128; //ms
if (givenUp) {
return false;
}

const uint16_t maxDelay = 1024; //ms
const uint8_t granularity = 16; //ms
uint8_t minimizedTime = Timer_GetCurrentTime() / granularity;

Expand All @@ -907,6 +917,7 @@ bool ShouldResendReport(bool statusOk, uint8_t* counter) {
return true;
} else {
*counter = 0;
givenUp = true;
return false;
}
}
Expand All @@ -931,6 +942,10 @@ static void reportRetry(errno_t err) {
}

static void handleFail(errno_t errorCode) {
// In any case, make the keyboard wake up, compare the usb reports, and possibly send them (those that are up-to-date at that time)
// This way, we loose some reports along the way, but at least don't produce stuck keys
EventScheduler_Schedule(Timer_GetCurrentTime() + USB_RESEND_DELAY_MS, EventSchedulerEvent_SendUsbReports, "usb-resend");

#ifdef __ZEPHYR__
if (ActiveHostConnectionId == ConnectionId_Invalid) {
LOG_WRN("Send failed: no connection selected: %s\n", ErrToStr(errorCode));
Expand All @@ -947,6 +962,7 @@ static void handleFail(errno_t errorCode) {
#endif
}


static void sendActiveReports(bool resending) {
bool usbReportsChangedByAction = false;
bool usbReportsChangedByAnything = false;
Expand Down Expand Up @@ -979,14 +995,18 @@ static void sendActiveReports(bool resending) {
//This is *not* asynchronously safe as long as multiple reports of different type can be sent at the same time.
//TODO: consider making it atomic, or lowering semaphore reset delay
keyboardNeedsResending = true;
retryThrottleTime = Timer_GetCurrentTime() + USB_RESEND_DELAY_MS;
UsbReportUpdateSemaphore &= ~UsbReportUpdate_Keyboard;
EventVector_Set(EventVector_ResendUsbReports);
} else {
if (ret != 0) {
handleFail(ret);
} else {
// don't throw out the state. This way even if we drop reports, we keep in-line with what we have actually sent, so
// once the connection is alive again, we report the new state if needed.
switchActiveKeyboardReport();
}
keyboardNeedsResending = false;
switchActiveKeyboardReport();
}
}
usbReportsChangedByAction = true;
Expand All @@ -1002,14 +1022,16 @@ static void sendActiveReports(bool resending) {
if (ShouldResendReport(ret == 0, &controlsRetries)) {
reportRetry(ret);
controlsNeedsResending = true;
retryThrottleTime = Timer_GetCurrentTime() + USB_RESEND_DELAY_MS;
UsbReportUpdateSemaphore &= ~UsbReportUpdate_Controls;
EventVector_Set(EventVector_ResendUsbReports);
} else {
if (ret != 0) {
handleFail(ret);
} else {
switchActiveControlsReport();
}
controlsNeedsResending = false;
switchActiveControlsReport();
}
UsbReportUpdater_LastActivityTime = resending ? UsbReportUpdater_LastActivityTime : Timer_GetCurrentTime();
usbReportsChangedByAction = true;
Expand All @@ -1026,15 +1048,17 @@ static void sendActiveReports(bool resending) {
if (ShouldResendReport(ret == 0, &mouseRetries)) {
reportRetry(ret);
mouseNeedsResending = true;
retryThrottleTime = Timer_GetCurrentTime() + USB_RESEND_DELAY_MS;
UsbReportUpdateSemaphore &= ~UsbReportUpdate_Mouse;
EventVector_Set(EventVector_ResendUsbReports);
} else {
if (ret != 0) {
handleFail(ret);
clearMouseMovement(); // Don't make cursor jump if we have connection issues.
} else {
switchActiveMouseReport();
}
mouseNeedsResending = false;
switchActiveMouseReport();
}

Debug_RecordBleSendResult(ret);
Expand Down Expand Up @@ -1064,12 +1088,16 @@ static bool blockedByReportThrottle() {
blocked = true;
}

// If we are retrying too agressively, we may clog some USB hubs, so add a throttle in that case as well.
if (currentTime < retryThrottleTime) {
blockedUntil = MAX(retryThrottleTime, blockedUntil);;
blocked = true;
}

// To reduce mouse latency, don't construct report until we are close enough to transport window.
if ((int32_t)(UsbReportWindowEstimate - currentTime) > USB_REPORT_WINDOW_LOOKAHEAD_MS) {
uint32_t throttleUntil = UsbReportWindowEstimate - USB_REPORT_WINDOW_LOOKAHEAD_MS;
if (!blocked || throttleUntil > blockedUntil) {
blockedUntil = throttleUntil;
}
blockedUntil = MAX(throttleUntil, blockedUntil);;
blocked = true;
}
if (blocked) {
Expand Down
Loading