Skip to content

Commit f6ff126

Browse files
author
lactide
committed
ChibiOS: fix deadlocks when hammering mouse/extra keys
When hammering on the mouse or extra keys, tmk will deadlock if ChibiOS was compiled with assertions. This particular assertion in usbTransmitI fires if the last packet hasn't been acknowledged yet by the host driver. To fix that, we'll save the new report and transmit it if the last report was acknowledged.
1 parent f2ebf6d commit f6ff126

File tree

1 file changed

+48
-33
lines changed

1 file changed

+48
-33
lines changed

protocol/chibios/usb_main.c

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,15 @@ extern bool keyboard_nkro;
6363
#endif /* NKRO_ENABLE */
6464

6565
report_keyboard_t keyboard_report_sent = {{0}};
66+
report_keyboard_t *keyboard_report_not_yet_sent = NULL;
6667
#ifdef MOUSE_ENABLE
6768
report_mouse_t mouse_report_blank = {0};
69+
report_mouse_t *mouse_report_not_yet_sent = NULL;
6870
#endif /* MOUSE_ENABLE */
6971
#ifdef EXTRAKEY_ENABLE
7072
uint8_t extra_report_blank[3] = {0};
73+
report_extra_t extra_report_not_yet_sent;
74+
bool extra_report_waiting = FALSE;
7175
#endif /* EXTRAKEY_ENABLE */
7276

7377
#ifdef CONSOLE_ENABLE
@@ -1066,17 +1070,23 @@ void send_remote_wakeup(USBDriver *usbp) {
10661070

10671071
/* keyboard IN callback hander (a kbd report has made it IN) */
10681072
void kbd_in_cb(USBDriver *usbp, usbep_t ep) {
1069-
/* STUB */
1070-
(void)usbp;
1071-
(void)ep;
1073+
if (keyboard_report_not_yet_sent != NULL) {
1074+
osalSysLockFromISR();
1075+
usbStartTransmitI(usbp, ep, (uint8_t *)keyboard_report_not_yet_sent, KBD_EPSIZE);
1076+
keyboard_report_not_yet_sent = NULL;
1077+
osalSysUnlockFromISR();
1078+
}
10721079
}
10731080

10741081
#ifdef NKRO_ENABLE
10751082
/* nkro IN callback hander (a nkro report has made it IN) */
10761083
void nkro_in_cb(USBDriver *usbp, usbep_t ep) {
1077-
/* STUB */
1078-
(void)usbp;
1079-
(void)ep;
1084+
if (keyboard_report_not_yet_sent != NULL) {
1085+
osalSysLockFromISR();
1086+
usbStartTransmitI(usbp, ep, (uint8_t *)keyboard_report_not_yet_sent, sizeof(report_keyboard_t));
1087+
keyboard_report_not_yet_sent = NULL;
1088+
osalSysUnlockFromISR();
1089+
}
10801090
}
10811091
#endif /* NKRO_ENABLE */
10821092

@@ -1132,41 +1142,30 @@ void send_keyboard(report_keyboard_t *report) {
11321142
osalSysUnlock();
11331143
return;
11341144
}
1135-
osalSysUnlock();
11361145

11371146
#ifdef NKRO_ENABLE
11381147
if(keyboard_nkro) { /* NKRO protocol */
11391148
/* need to wait until the previous packet has made it through */
1140-
/* can rewrite this using the synchronous API, then would wait
1141-
* until *after* the packet has been transmitted. I think
1142-
* this is more efficient */
1143-
/* busy wait, should be short and not very common */
1144-
osalSysLock();
1149+
/* save the pointer to the current report to send it later */
11451150
if(usbGetTransmitStatusI(&USB_DRIVER, NKRO_ENDPOINT)) {
1146-
/* Need to either suspend, or loop and call unlock/lock during
1147-
* every iteration - otherwise the system will remain locked,
1148-
* no interrupts served, so USB not going through as well.
1149-
* Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */
1150-
osalThreadSuspendS(&(&USB_DRIVER)->epc[NKRO_ENDPOINT]->in_state->thread);
1151+
keyboard_report_not_yet_sent = report;
1152+
osalSysUnlock();
1153+
return;
11511154
}
11521155
usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t));
1153-
osalSysUnlock();
11541156
} else
11551157
#endif /* NKRO_ENABLE */
11561158
{ /* boot protocol */
11571159
/* need to wait until the previous packet has made it through */
1158-
/* busy wait, should be short and not very common */
1159-
osalSysLock();
1160+
/* save the pointer to the current report to send it later */
11601161
if(usbGetTransmitStatusI(&USB_DRIVER, KBD_ENDPOINT)) {
1161-
/* Need to either suspend, or loop and call unlock/lock during
1162-
* every iteration - otherwise the system will remain locked,
1163-
* no interrupts served, so USB not going through as well.
1164-
* Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */
1165-
osalThreadSuspendS(&(&USB_DRIVER)->epc[KBD_ENDPOINT]->in_state->thread);
1162+
keyboard_report_not_yet_sent = report;
1163+
osalSysUnlock();
1164+
return;
11661165
}
11671166
usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, KBD_EPSIZE);
1168-
osalSysUnlock();
11691167
}
1168+
osalSysUnlock();
11701169
keyboard_report_sent = *report;
11711170
}
11721171

@@ -1179,8 +1178,12 @@ void send_keyboard(report_keyboard_t *report) {
11791178

11801179
/* mouse IN callback hander (a mouse report has made it IN) */
11811180
void mouse_in_cb(USBDriver *usbp, usbep_t ep) {
1182-
(void)usbp;
1183-
(void)ep;
1181+
if (mouse_report_not_yet_sent != NULL) {
1182+
osalSysLockFromISR();
1183+
usbStartTransmitI(usbp, ep, (uint8_t *)mouse_report_not_yet_sent, sizeof(report_mouse_t));
1184+
mouse_report_not_yet_sent = NULL;
1185+
osalSysUnlockFromISR();
1186+
}
11841187
}
11851188

11861189
void send_mouse(report_mouse_t *report) {
@@ -1189,14 +1192,17 @@ void send_mouse(report_mouse_t *report) {
11891192
osalSysUnlock();
11901193
return;
11911194
}
1192-
osalSysUnlock();
11931195

11941196
/* TODO: LUFA manually waits for the endpoint to become ready
11951197
* for about 10ms for mouse, kbd, system; 1ms for nkro
11961198
* is this really needed?
11971199
*/
11981200

1199-
osalSysLock();
1201+
if (usbGetTransmitStatusI(&USB_DRIVER, MOUSE_ENDPOINT)) {
1202+
mouse_report_not_yet_sent = report;
1203+
osalSysUnlock();
1204+
return;
1205+
}
12001206
usbStartTransmitI(&USB_DRIVER, MOUSE_ENDPOINT, (uint8_t *)report, sizeof(report_mouse_t));
12011207
osalSysUnlock();
12021208
}
@@ -1216,9 +1222,12 @@ void send_mouse(report_mouse_t *report) {
12161222

12171223
/* extrakey IN callback hander */
12181224
void extra_in_cb(USBDriver *usbp, usbep_t ep) {
1219-
/* STUB */
1220-
(void)usbp;
1221-
(void)ep;
1225+
if (extra_report_waiting) {
1226+
osalSysLockFromISR();
1227+
usbStartTransmitI(usbp, ep, (uint8_t *)&extra_report_not_yet_sent, sizeof(report_extra_t));
1228+
extra_report_waiting = FALSE;
1229+
osalSysUnlockFromISR();
1230+
}
12221231
}
12231232

12241233
static void send_extra_report(uint8_t report_id, uint16_t data) {
@@ -1233,6 +1242,12 @@ static void send_extra_report(uint8_t report_id, uint16_t data) {
12331242
.usage = data
12341243
};
12351244

1245+
if (usbGetTransmitStatusI(&USB_DRIVER, EXTRA_ENDPOINT)) {
1246+
extra_report_not_yet_sent = report;
1247+
extra_report_waiting = TRUE;
1248+
osalSysUnlock();
1249+
return;
1250+
}
12361251
usbStartTransmitI(&USB_DRIVER, EXTRA_ENDPOINT, (uint8_t *)&report, sizeof(report_extra_t));
12371252
osalSysUnlock();
12381253
}

0 commit comments

Comments
 (0)