Skip to content

Commit 9c93f74

Browse files
committed
WIP improve timeouts
1 parent e637f2e commit 9c93f74

File tree

1 file changed

+100
-12
lines changed

1 file changed

+100
-12
lines changed

cores/arduino/USBCore.cpp

Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -315,29 +315,117 @@ uint8_t* EPBuffer<L>::ptr()
315315
template<size_t L>
316316
bool EPBuffer<L>::waitForWriteComplete()
317317
{
318-
// auto start = getCurrentMillis();
319318
auto ok = true;
319+
auto desc = *EPBuffers().desc(this->ep);
320+
auto udev = &USBCore().usbDev();
321+
322+
/*
323+
* For interrupt EPs, count from now instead of from previous send.
324+
* Otherwise, if a send occurs while the bus is suspended, the packet will
325+
* be cheated of its full timeout.
326+
*/
327+
if (desc.type() == USB_EP_ATTR_INT) {
328+
this->startTime = millis();
329+
}
320330
do {
321331
usb_enable_interrupts();
322-
if (this->txWaiting && millis() - this->startTime > this->timeout) {
323-
ok = false;
324-
this->timedOut = true;
325-
USBCore().logEP('X', this->ep, '>', this->len());
326-
break;
327-
}
328-
switch (USBCore().usbDev().cur_status) {
332+
switch (udev->cur_status) {
329333
case USBD_DEFAULT:
330334
case USBD_ADDRESSED:
331335
ok = false;
332336
break;
333337
default:
334338
break;
335339
}
336-
// if (getCurrentMillis() - start > 5) {
337-
// EPBuffers().buf(ep).transcIn();
338-
// ok = false;
339-
// }
340340
usb_disable_interrupts();
341+
if (this->txWaiting && millis() - this->startTime > this->timeout) {
342+
USBCore().logEP('X', this->ep, '>', this->len());
343+
/*
344+
* For interrupt EPs, overwrite the previous packet. In the common
345+
* case of a key press HID report initiating a remote wakeup,
346+
* transmission of the key release event might be delayed, because
347+
* the host can't poll while the bus is suspended. If the
348+
* application sends a key release event before the key press event
349+
* is transmitted, that might time out. If the key release event is
350+
* discarded due to the timeout, the host will never receive the
351+
* key release event, leading to a stuck or repeating key.
352+
*/
353+
if (desc.type() == USB_EP_ATTR_INT) {
354+
/*
355+
* Set the endpoint status to NAK, in an attempt to prevent
356+
* the queued packet from being transmitted while being
357+
* overwritten.
358+
*
359+
* This must be done in two steps. Unfortunately, the USBD
360+
* peripheral's design doesn't seem to make it possible to
361+
* atomically abort a queued transmission. Even if interrupts
362+
* are disabled, the peripheral state machine continues to run.
363+
*
364+
* The endpoint status control bits are toggle-only (toggle on
365+
* writing 1; unchanged on writing 0). If the TX state
366+
* transitions from VALID to NAK during the read-modify-write,
367+
* a toggle operation (XOR 0b01) intended to transition from
368+
* VALID (0b11) to NAK (0b10) will set the status back to
369+
* VALID. This will cause the previous packet to be sent again.
370+
*
371+
* Instead, set the status to DISABLED (0b00, via XOR 0b11),
372+
* which will become STALL (0b01) if the peripheral transitions
373+
* the status to NAK between the read and the write. This can
374+
* happen if the transmission was in progress and the host ACKs
375+
* exactly between the read and the write to update the status
376+
* bits.
377+
*
378+
* Possible states after the first USBD_EP_TX_STAT_SET:
379+
*
380+
* - DISABLED (metastable if TX in progress)
381+
* - STALL (stable; only possible if we lost the r-m-w race)
382+
* - NAK (stable; only possible if TX completed after toggle)
383+
*
384+
* There is a small theoretical chance that the host will poll
385+
* the endpoint again between the two updates to the status
386+
* bits, which could lead to the host receiving a STALL
387+
* handshake, which would put the data toggle bit out of sync.
388+
*
389+
* The second host poll cannot complete sooner than 34 bit times
390+
* after the ACK from the first host poll. (2 bits inter-packet
391+
* delay, 8 bits sync, 8 bits PID, 16 bits addr/endpoint/CRC,
392+
* about 2.83 microseconds) The second status bit update will
393+
* complete before then, assuming reasonable CPU clock speeds
394+
* and no lengthy interrupt handler executions occurring.
395+
*
396+
* On the other hand, it's unknown when exactly the peripheral
397+
* checks the endpoint status bits to decide which response to
398+
* send to an IN poll. It might latch them at EOP, or SOP, or
399+
* maybe PID, or it might wait until after checking the CRC. So
400+
* the actual available time might be between 0 and 34 bit
401+
* times.
402+
*
403+
* We could globally disable interrupts to mitigate that, if
404+
* necessary.
405+
*/
406+
USBD_EP_TX_STAT_SET(this->ep, EPTX_DISABLED);
407+
if (USBD_EPxCS(this->ep) & EPxCS_TX_STA == EPTX_DISABLED) {
408+
/*
409+
* If the endpoint is disabled, there might still be a
410+
* transmission in progress. Wait for it to complete:
411+
*
412+
* 1 byte sync + 1 byte PID + 64 bytes data + 2 bytes CRC,
413+
* 1 turn-around time,
414+
* 1 byte sync + 1 byte PID (handshake)
415+
* assuming maximum bit stuffing at full speed (12MHz).
416+
*/
417+
delayMicroseconds(56);
418+
}
419+
// In case TX was in progress and completed
420+
USBD_EP_TX_ST_CLEAR(this->ep);
421+
USBD_EP_TX_STAT_SET(this->ep, EPTX_NAK);
422+
break;
423+
} else {
424+
ok = false;
425+
this->timedOut = true;
426+
break;
427+
}
428+
}
341429
} while (ok && this->txWaiting);
342430
return ok;
343431
}

0 commit comments

Comments
 (0)