Skip to content

Commit 3e1a107

Browse files
ArcaneNibbledlech
authored andcommitted
pbio/drv/usb/usb_ev3.c: Defer setting USB_CSRL0_RXRDYC
The SETUPEND bit in the PERI_CSR0 seems to do _exactly_ what it says. The bit gets set if a control transaction ends (either by receiving a new SETUP token, or by completing the status phase with an ACK) before the DATAEND bit is set. However, clearing RXPKTRDY seems to be the only flag needed before the MUSB IP moves on to automatically allowing the status phase to complete. The datasheet hints at this by saying: > The interval between setting SERV_RXPKTRDY bit and DATAEND bit > should be very small to avoid getting a SetupEnd error condition. By setting the bits separately like we were doing before, if the host controller completed the status phase while we were still running through the IRQ handler, the USB IP would detect SetupEnd, we would interpret it as an error, and then we would fail to act on, in particular, the SET_ADDRESS command. This manifested in the device suddenly no longer responding to subsequent GET_DESCRIPTORs. We now set the bits at the exact same time, so this race window is closed. This race window does not apply to requests with a data stage due to the extra packets which are expected.
1 parent 3f95708 commit 3e1a107

File tree

1 file changed

+21
-4
lines changed

1 file changed

+21
-4
lines changed

lib/pbio/drv/usb/usb_ev3.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,6 @@ static void usb_device_intr(void) {
540540

541541
setup_pkt.u[0] = HWREG(USB0_BASE + USB_O_FIFO0);
542542
setup_pkt.u[1] = HWREG(USB0_BASE + USB_O_FIFO0);
543-
HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_RXRDYC;
544543

545544
switch (setup_pkt.s.bmRequestType & BM_REQ_TYPE_MASK) {
546545
case BM_REQ_TYPE_STANDARD:
@@ -681,11 +680,29 @@ static void usb_device_intr(void) {
681680
}
682681
}
683682

683+
// Note regarding the setting of the USB_CSRL0_RXRDYC bit:
684+
// The Linux kernel has a comment saying
685+
// > For zero-data requests we want to delay the STATUS stage to avoid SETUPEND errors.
686+
// but also that
687+
// > If we write data, the controller acts happier if we enable the TX FIFO right away
688+
// We implement something similar but not identical. In general, we wait until
689+
// we have completely processed the request and decided what we're going to do
690+
// before we indicate that we are ready to progress to the next phase.
691+
// We do not support any requests that require receiving data from the host,
692+
// only zero-data requests or those that require sending data to the host.
693+
// For errors or zero-data requests, we set USB_CSRL0_RXRDYC and USB_CSRL0_DATAEND
694+
// at the same time so that we don't get spurious SETUPEND errors
695+
// (which we treat as a command failure). For requests that require sending data,
696+
// we set USB_CSRL0_RXRDYC while we get ready to copy data into the FIFO.
697+
684698
if (!handled) {
685-
// send stall
686-
HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_STALL;
699+
// Indicate we read the packet, but also send stall
700+
HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_RXRDYC | USB_CSRL0_STALL;
687701
} else {
688702
if (pbdrv_usb_setup_data_to_send) {
703+
// Indicate we read the packet
704+
HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_RXRDYC;
705+
689706
// Clamp by host request size
690707
if (setup_pkt.s.wLength < pbdrv_usb_setup_data_to_send_sz) {
691708
pbdrv_usb_setup_data_to_send_sz = setup_pkt.s.wLength;
@@ -701,7 +718,7 @@ static void usb_device_intr(void) {
701718
}
702719
} else {
703720
// Just get ready to send ACK, no data
704-
HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_DATAEND;
721+
HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_RXRDYC | USB_CSRL0_DATAEND;
705722
}
706723
}
707724
} else if (pbdrv_usb_setup_data_to_send) {

0 commit comments

Comments
 (0)