Skip to content

Commit e99907f

Browse files
committed
Semaphores: refactor retry states into packed structures.
1 parent 55f42b2 commit e99907f

6 files changed

Lines changed: 97 additions & 56 deletions

File tree

right/src/hid/transport_usb.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ struct usb_manager {
131131
}
132132
if ((ev & event::CONFIGURATION_CHANGE) != event::NONE) {
133133
// reset the semaphore on USB configuration or reset
134-
UsbReportUpdateSemaphore = 0;
134+
UsbReportUpdater_ClearSemaphore(0xFF);
135135
}
136136
});
137137
}

right/src/main.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,14 @@ static void sendFirstReport()
8686
errno_t ret = -1;
8787
// Wait until sending a report is successful, but don't block longer than 5 seconds.
8888
while (ret && Timer_GetCurrentTime() < 5000) {
89-
UsbReportUpdateSemaphore |= UsbReportUpdate_Keyboard;
89+
UsbReportUpdater_SetSemaphore(UsbReportUpdate_Keyboard);
9090
ret = Hid_SendKeyboardReport(&emptyReport);
9191
if (ret) {
92-
UsbReportUpdateSemaphore &= ~UsbReportUpdate_Keyboard;
92+
UsbReportUpdater_ClearSemaphore(UsbReportUpdate_Keyboard);
9393
__WFI();
9494
}
9595
}
96-
while (UsbReportUpdateSemaphore) {
96+
while (UsbReportUpdater_GetSemaphore()) {
9797
__WFI();
9898
}
9999
}

right/src/usb_commands/usb_command_get_variable.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ void UsbCommand_GetVariable(const uint8_t *GenericHidOutBuffer, uint8_t *Generic
2929
SetUsbTxBufferUint8(1, Cfg.DebounceTimeRelease);
3030
break;
3131
case UsbVariable_UsbReportSemaphore:
32-
SetUsbTxBufferUint8(1, UsbReportUpdateSemaphore);
32+
SetUsbTxBufferUint8(1, UsbReportUpdater_GetSemaphore());
3333
break;
3434
case UsbVariable_StatusBuffer:
3535
for (uint8_t i = 1; i < USB_COMMAND_BUFFER_LENGTH; i++) {

right/src/usb_commands/usb_command_set_variable.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,12 @@ void UsbCommand_SetVariable(const uint8_t *GenericHidOutBuffer, uint8_t *Generic
3737
case UsbVariable_DebounceTimeRelease:
3838
Cfg.DebounceTimeRelease = GetUsbRxBufferUint8(2);
3939
break;
40-
case UsbVariable_UsbReportSemaphore:
41-
UsbReportUpdateSemaphore = GetUsbRxBufferUint8(2);
40+
case UsbVariable_UsbReportSemaphore: {
41+
uint8_t bits = GetUsbRxBufferUint8(2);
42+
UsbReportUpdater_ClearSemaphore((uint8_t)~bits);
43+
UsbReportUpdater_SetSemaphore(bits);
4244
break;
45+
}
4346
case UsbVariable_StatusBuffer:
4447
break;
4548
case UsbVariable_LedAudioRegisters:

right/src/usb_report_updater.c

Lines changed: 82 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ uint32_t UsbReportWindowEstimateLast = 0;
7979
uint32_t UsbReportWindowEstimate = 0;
8080

8181

82-
volatile uint8_t UsbReportUpdateSemaphore = 0;
8382

8483
// Modifiers can be applied as one of the following classes
8584
// - input:
@@ -879,36 +878,71 @@ uint32_t UsbReportUpdateCounter;
879878
uint32_t UpdateUsbReports_LastUpdateTime = 0;
880879
uint32_t lastBasicReportTime = 0;
881880
uint32_t retryThrottleTime = 0;
882-
uint8_t keyboardRetries = 0;
883-
bool keyboardNeedsResending = false;
884-
uint8_t controlsRetries = 0;
885-
bool controlsNeedsResending = false;
886-
uint8_t mouseRetries = 0;
887-
bool mouseNeedsResending = false;
888881
bool givenUp = false;
889882

883+
// Per-report send/resend bookkeeping. Bundles the retry counter and resend flag with
884+
// the report's identity (its semaphore bit and double-buffer switch), so the send path
885+
// can treat all three reports uniformly.
886+
//
887+
// inFlight replaces the old shared UsbReportUpdateSemaphore bitfield: each report owns its
888+
// own byte, so setting/clearing one is a single whole-byte store (STRB on ARMv7-M) with no
889+
// read-modify-write, and therefore can't clobber another report's flag from an ISR. It is
890+
// volatile because it is written from the async transport sent callbacks. The legacy uint8
891+
// bitfield view (for the bus-reset reset and the debug get/set-variable command) is
892+
// reassembled on demand by UsbReportUpdater_GetSemaphore() & co.
893+
typedef struct {
894+
uint8_t retries;
895+
bool needsResending;
896+
volatile bool inFlight;
897+
uint8_t semaphoreBit;
898+
void (*switchActiveReport)(void);
899+
} report_send_state_t;
900+
901+
static report_send_state_t keyboardSendState = {
902+
.semaphoreBit = UsbReportUpdate_Keyboard, .switchActiveReport = switchActiveKeyboardReport };
903+
static report_send_state_t controlsSendState = {
904+
.semaphoreBit = UsbReportUpdate_Controls, .switchActiveReport = switchActiveControlsReport };
905+
static report_send_state_t mouseSendState = {
906+
.semaphoreBit = UsbReportUpdate_Mouse, .switchActiveReport = switchActiveMouseReport };
907+
908+
static bool anyReportInFlight(void) {
909+
return keyboardSendState.inFlight || controlsSendState.inFlight || mouseSendState.inFlight;
910+
}
911+
912+
// Reassemble / apply the legacy uint8 bitfield view of the in-flight flags.
913+
uint8_t UsbReportUpdater_GetSemaphore(void) {
914+
uint8_t bits = 0;
915+
bits |= keyboardSendState.inFlight ? UsbReportUpdate_Keyboard : 0;
916+
bits |= controlsSendState.inFlight ? UsbReportUpdate_Controls : 0;
917+
bits |= mouseSendState.inFlight ? UsbReportUpdate_Mouse : 0;
918+
return bits;
919+
}
920+
921+
void UsbReportUpdater_SetSemaphore(uint8_t bits) {
922+
keyboardSendState.inFlight = bits & UsbReportUpdate_Keyboard;
923+
controlsSendState.inFlight = bits & UsbReportUpdate_Controls;
924+
mouseSendState.inFlight = bits & UsbReportUpdate_Mouse;
925+
}
926+
927+
void UsbReportUpdater_ClearSemaphore(uint8_t bits) {
928+
keyboardSendState.inFlight = false;
929+
controlsSendState.inFlight = false;
930+
mouseSendState.inFlight = false;
931+
}
932+
890933
// Confirm that an in-flight report was delivered. Called by the transport layer once
891934
// delivery is confirmed (USB/BLE asynchronously via the sent callback, NUS/dongle
892935
// synchronously on successful enqueue). Advance the baseline to the just-sent report,
893936
// reset the retry counter (the send succeeded), and release the semaphore gate.
894-
void UsbReportUpdater_ConfirmKeyboardReportSent(void) {
895-
switchActiveKeyboardReport();
896-
keyboardRetries = 0;
897-
givenUp = false;
898-
UsbReportUpdateSemaphore &= ~UsbReportUpdate_Keyboard;
899-
}
900-
void UsbReportUpdater_ConfirmMouseReportSent(void) {
901-
switchActiveMouseReport();
902-
mouseRetries = 0;
903-
givenUp = false;
904-
UsbReportUpdateSemaphore &= ~UsbReportUpdate_Mouse;
905-
}
906-
void UsbReportUpdater_ConfirmControlsReportSent(void) {
907-
switchActiveControlsReport();
908-
controlsRetries = 0;
937+
static void confirmReportSent(report_send_state_t* st) {
938+
st->switchActiveReport();
939+
st->retries = 0;
909940
givenUp = false;
910-
UsbReportUpdateSemaphore &= ~UsbReportUpdate_Controls;
941+
st->inFlight = false;
911942
}
943+
void UsbReportUpdater_ConfirmKeyboardReportSent(void) { confirmReportSent(&keyboardSendState); }
944+
void UsbReportUpdater_ConfirmMouseReportSent(void) { confirmReportSent(&mouseSendState); }
945+
void UsbReportUpdater_ConfirmControlsReportSent(void) { confirmReportSent(&controlsSendState); }
912946

913947

914948
// Counts retry attempts and decides whether to keep trying. On success, resets
@@ -1005,13 +1039,13 @@ static void handleFail(errno_t errorCode) {
10051039
#endif
10061040
}
10071041

1008-
static bool resendMaybe(errno_t ret, uint8_t* retries, bool* needsResending, uint8_t semaphoreBit, bool withDelay) {
1009-
if (ShouldResendReport(ret, retries)) {
1010-
reportRetry(ret, *retries);
1011-
*needsResending = true;
1042+
static bool resendMaybe(errno_t ret, report_send_state_t* st, bool withDelay) {
1043+
if (ShouldResendReport(ret, &st->retries)) {
1044+
reportRetry(ret, st->retries);
1045+
st->needsResending = true;
10121046
// The semaphore timeout path already waited out its own delay, so resend now.
1013-
retryThrottleTime = withDelay ? Timer_GetCurrentTime() + GetResendThrottleDelay(*retries) : Timer_GetCurrentTime();
1014-
UsbReportUpdateSemaphore &= ~semaphoreBit;
1047+
retryThrottleTime = withDelay ? Timer_GetCurrentTime() + GetResendThrottleDelay(st->retries) : Timer_GetCurrentTime();
1048+
st->inFlight = false;
10151049
EventVector_Set(EventVector_ResendUsbReports);
10161050
return true;
10171051
} else {
@@ -1020,9 +1054,9 @@ static bool resendMaybe(errno_t ret, uint8_t* retries, bool* needsResending, uin
10201054
// release the gate. (ret == 0 is the success case - leave the semaphore set,
10211055
// it is cleared only once delivery is confirmed.)
10221056
handleFail(ret);
1023-
UsbReportUpdateSemaphore &= ~semaphoreBit;
1057+
st->inFlight = false;
10241058
}
1025-
*needsResending = false;
1059+
st->needsResending = false;
10261060
return false;
10271061
}
10281062
}
@@ -1035,7 +1069,7 @@ static void sendActiveReports(bool resending) {
10351069
// in case of usb error, this gets set back again
10361070
EventVector_Unset(EventVector_SendUsbReports | EventVector_ResendUsbReports);
10371071

1038-
if (KeyboardReport_HasChange(keyboardReports) && (!resending || keyboardNeedsResending)) {
1072+
if (KeyboardReport_HasChange(keyboardReports) && (!resending || keyboardSendState.needsResending)) {
10391073
#ifdef __ZEPHYR__
10401074
if (InputInterceptor_RegisterReport(ActiveKeyboardReport)) {
10411075
switchActiveKeyboardReport();
@@ -1051,7 +1085,7 @@ static void sendActiveReports(bool resending) {
10511085
switchActiveKeyboardReport();
10521086
} else {
10531087
//The semaphore has to be set before the call. Assume what happens if a bus reset happens asynchronously here. (Deadlock.)
1054-
UsbReportUpdateSemaphore |= UsbReportUpdate_Keyboard;
1088+
keyboardSendState.inFlight = true;
10551089
ret = Hid_SendKeyboardReport(ActiveKeyboardReport);
10561090
// On success the report is accepted by the transport, but NOT yet confirmed
10571091
// delivered. We advance the baseline (switchActiveKeyboardReport) only once
@@ -1061,7 +1095,7 @@ static void sendActiveReports(bool resending) {
10611095
// was not advanced, so HasChange stays true and we re-send the current state instead
10621096
// of losing it (the stuck-key bug).
10631097
if (ret != 0) {
1064-
resendMaybe(ret, &keyboardRetries, &keyboardNeedsResending, UsbReportUpdate_Keyboard, true);
1098+
resendMaybe(ret, &keyboardSendState, true);
10651099
}
10661100
}
10671101
usbReportsChangedByAction = true;
@@ -1071,24 +1105,24 @@ static void sendActiveReports(bool resending) {
10711105
}
10721106
}
10731107

1074-
if (ControlsReport_HasChanges(controlsReports) && (!resending || controlsNeedsResending)) {
1075-
UsbReportUpdateSemaphore |= UsbReportUpdate_Controls;
1108+
if (ControlsReport_HasChanges(controlsReports) && (!resending || controlsSendState.needsResending)) {
1109+
controlsSendState.inFlight = true;
10761110
ret = Hid_SendControlsReport(ActiveControlsReport);
10771111
if (ret != 0) {
1078-
resendMaybe(ret, &controlsRetries, &controlsNeedsResending, UsbReportUpdate_Controls, true);
1112+
resendMaybe(ret, &controlsSendState, true);
10791113
}
10801114
UsbReportUpdater_LastActivityTime = resending ? UsbReportUpdater_LastActivityTime : Timer_GetCurrentTime();
10811115
usbReportsChangedByAction = true;
10821116
usbReportsChangedByAnything = true;
10831117
}
10841118

1085-
if (MouseReport_HasChanges(mouseReports, ActiveMouseReport) && (!resending || mouseNeedsResending) &&
1119+
if (MouseReport_HasChanges(mouseReports, ActiveMouseReport) && (!resending || mouseSendState.needsResending) &&
10861120
(CurrentPowerMode == PowerMode_Awake)) {
10871121
bool usbMouseButtonsChanged = mouseReports[0].buttons != mouseReports[1].buttons;
10881122

1089-
UsbReportUpdateSemaphore |= UsbReportUpdate_Mouse;
1123+
mouseSendState.inFlight = true;
10901124
ret = Hid_SendMouseReport(ActiveMouseReport);
1091-
if (ret != 0 && !resendMaybe(ret, &mouseRetries, &mouseNeedsResending, UsbReportUpdate_Mouse, true)) {
1125+
if (ret != 0 && !resendMaybe(ret, &mouseSendState, true)) {
10921126
clearMouseMovement(); // Don't make cursor jump if we have connection issues.
10931127
}
10941128

@@ -1106,7 +1140,7 @@ static void sendActiveReports(bool resending) {
11061140
EventVector_Set(EventVector_SendUsbReports);
11071141
}
11081142

1109-
if (UsbReportUpdateSemaphore) {
1143+
if (anyReportInFlight()) {
11101144
EventScheduler_Schedule(UpdateUsbReports_LastUpdateTime + USB_SEMAPHORE_TIMEOUT, EventSchedulerEvent_Postponer, "usb-semaphore-timeout");
11111145
}
11121146
}
@@ -1167,18 +1201,18 @@ static bool blockedByReportThrottle() {
11671201
}
11681202

11691203
bool UsbReadyForTransfers(void) {
1170-
if (UsbReportUpdateSemaphore && CurrentPowerMode <= PowerMode_LastAwake) {
1204+
if (anyReportInFlight() && CurrentPowerMode <= PowerMode_LastAwake) {
11711205
if (Timer_GetElapsedTime(&UpdateUsbReports_LastUpdateTime) < USB_SEMAPHORE_TIMEOUT) {
11721206
return false;
11731207
} else {
1174-
if (UsbReportUpdateSemaphore & UsbReportUpdate_Keyboard) {
1175-
resendMaybe(-ETIMEDOUT, &keyboardRetries, &keyboardNeedsResending, UsbReportUpdate_Keyboard, false);
1208+
if (keyboardSendState.inFlight) {
1209+
resendMaybe(-ETIMEDOUT, &keyboardSendState, false);
11761210
}
1177-
if (UsbReportUpdateSemaphore & UsbReportUpdate_Controls) {
1178-
resendMaybe(-ETIMEDOUT, &controlsRetries, &controlsNeedsResending, UsbReportUpdate_Controls, false);
1211+
if (controlsSendState.inFlight) {
1212+
resendMaybe(-ETIMEDOUT, &controlsSendState, false);
11791213
}
1180-
if (UsbReportUpdateSemaphore & UsbReportUpdate_Mouse) {
1181-
resendMaybe(-ETIMEDOUT, &mouseRetries, &mouseNeedsResending, UsbReportUpdate_Mouse, false);
1214+
if (mouseSendState.inFlight) {
1215+
resendMaybe(-ETIMEDOUT, &mouseSendState, false);
11821216
}
11831217
}
11841218
}

right/src/usb_report_updater.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
// Variables:
4747

4848
extern uint32_t UsbReportUpdateCounter;
49-
extern volatile uint8_t UsbReportUpdateSemaphore;
5049
extern bool TestUsbStack;
5150
extern uint8_t InputModifiers;
5251
extern uint8_t InputModifiersPrevious;
@@ -84,6 +83,11 @@
8483
void UsbReportUpdater_ConfirmMouseReportSent(void);
8584
void UsbReportUpdater_ConfirmControlsReportSent(void);
8685

86+
// Legacy uint8 bitfield view of the per-report in-flight flags (bits = UsbReportUpdateFlags_t).
87+
uint8_t UsbReportUpdater_GetSemaphore(void);
88+
void UsbReportUpdater_SetSemaphore(uint8_t bits);
89+
void UsbReportUpdater_ClearSemaphore(uint8_t bits);
90+
8791
bool ShouldResendReport(int err, uint8_t* counter);
8892
uint16_t GetResendThrottleDelay(uint8_t counter);
8993

0 commit comments

Comments
 (0)