@@ -79,7 +79,6 @@ uint32_t UsbReportWindowEstimateLast = 0;
7979uint32_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;
879878uint32_t UpdateUsbReports_LastUpdateTime = 0 ;
880879uint32_t lastBasicReportTime = 0 ;
881880uint32_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;
888881bool 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
11691203bool 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 }
0 commit comments