From d52055fa40c4048d22ac0c52c4482edd08fa53d1 Mon Sep 17 00:00:00 2001 From: R Date: Sat, 26 Jul 2025 22:27:35 +0100 Subject: [PATCH 1/5] pbio/drv/usb/usb_ev3.c: Handle multiple EP0 events at once There is no guarantee that these events can only happen one at a time. If a host controller is fast enough, it can turn around from getting a STALL during a control transaction to immediately issuing the next setup stage before the interrupt handler ever gets invoked. This can result in multiple status flags getting set (both the SENTSTALL confirmation bit and RXPKTRDY for the new request). If we happen to miss an event, it may cause the USB control transfer state machine to fall out of sync, which can cause enumeration failures. This commit depends on a subsequent fix to work across all ranges of timings. --- lib/pbio/drv/usb/usb_ev3.c | 309 +++++++++++++++++++------------------ 1 file changed, 157 insertions(+), 152 deletions(-) diff --git a/lib/pbio/drv/usb/usb_ev3.c b/lib/pbio/drv/usb/usb_ev3.c index d92df8c9d..c5cc54424 100644 --- a/lib/pbio/drv/usb/usb_ev3.c +++ b/lib/pbio/drv/usb/usb_ev3.c @@ -515,198 +515,203 @@ static void usb_device_intr(void) { HWREGH(USB0_BASE + USB_O_CSRL0) = 0; pbdrv_usb_setup_data_to_send = 0; pbdrv_usb_addr_needs_setting = false; - } else if (peri_csr & USB_CSRL0_SETEND) { + } + + if (peri_csr & USB_CSRL0_SETEND) { // Error in SETUP transaction HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_SETENDC; pbdrv_usb_setup_data_to_send = 0; pbdrv_usb_addr_needs_setting = false; - } else { - if (pbdrv_usb_addr_needs_setting) { - USBDevAddrSet(USB0_BASE, pbdrv_usb_addr); - pbdrv_usb_addr_needs_setting = false; - } + } - if (peri_csr & USB_CSRL0_RXRDY) { - // Got a new setup packet - pbdrv_usb_setup_packet_union_t setup_pkt; - bool handled = false; - pbdrv_usb_setup_data_to_send = 0; + // If we got here (and didn't wipe out this flag), + // then this indicates completion of the SET_ADDRESS command. + // We thus have to make it take effect at this point. + if (pbdrv_usb_addr_needs_setting) { + USBDevAddrSet(USB0_BASE, pbdrv_usb_addr); + pbdrv_usb_addr_needs_setting = false; + } - setup_pkt.u[0] = HWREG(USB0_BASE + USB_O_FIFO0); - setup_pkt.u[1] = HWREG(USB0_BASE + USB_O_FIFO0); - HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_RXRDYC; + if (peri_csr & USB_CSRL0_RXRDY) { + // Got a new setup packet + pbdrv_usb_setup_packet_union_t setup_pkt; + bool handled = false; + pbdrv_usb_setup_data_to_send = 0; - switch (setup_pkt.s.bmRequestType & BM_REQ_TYPE_MASK) { - case BM_REQ_TYPE_STANDARD: - switch (setup_pkt.s.bmRequestType & BM_REQ_RECIP_MASK) { - case BM_REQ_RECIP_DEV: - switch (setup_pkt.s.bRequest) { - case SET_ADDRESS: - pbdrv_usb_addr = setup_pkt.s.wValue; - pbdrv_usb_addr_needs_setting = true; - handled = true; - break; + setup_pkt.u[0] = HWREG(USB0_BASE + USB_O_FIFO0); + setup_pkt.u[1] = HWREG(USB0_BASE + USB_O_FIFO0); + HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_RXRDYC; + + switch (setup_pkt.s.bmRequestType & BM_REQ_TYPE_MASK) { + case BM_REQ_TYPE_STANDARD: + switch (setup_pkt.s.bmRequestType & BM_REQ_RECIP_MASK) { + case BM_REQ_RECIP_DEV: + switch (setup_pkt.s.bRequest) { + case SET_ADDRESS: + pbdrv_usb_addr = setup_pkt.s.wValue; + pbdrv_usb_addr_needs_setting = true; + handled = true; + break; - case SET_CONFIGURATION: - if (setup_pkt.s.wValue <= 1) { - pbdrv_usb_config = setup_pkt.s.wValue; + case SET_CONFIGURATION: + if (setup_pkt.s.wValue <= 1) { + pbdrv_usb_config = setup_pkt.s.wValue; - if (pbdrv_usb_config == 1) { - // configuring + if (pbdrv_usb_config == 1) { + // configuring - // Reset data toggle, clear stall, flush fifo - HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH; - HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH; - } else { - // deconfiguring + // Reset data toggle, clear stall, flush fifo + HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH; + HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH; + } else { + // deconfiguring - // Set stall condition - HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_STALL; - HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_STALL; - } - handled = true; + // Set stall condition + HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_STALL; + HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_STALL; } - break; + handled = true; + } + break; - case GET_CONFIGURATION: - pbdrv_usb_setup_misc_tx_byte = pbdrv_usb_config; + case GET_CONFIGURATION: + pbdrv_usb_setup_misc_tx_byte = pbdrv_usb_config; + pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; + pbdrv_usb_setup_data_to_send_sz = 1; + handled = true; + break; + + case GET_STATUS: + pbdrv_usb_setup_misc_tx_byte = 1; // self-powered + pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; + pbdrv_usb_setup_data_to_send_sz = 2; + handled = true; + break; + + case GET_DESCRIPTOR: + if (usb_get_descriptor(setup_pkt.s.wValue)) { + handled = true; + } + break; + } + break; + + case BM_REQ_RECIP_IF: + if (setup_pkt.s.wIndex == 0) { + switch (setup_pkt.s.bRequest) { + case GET_INTERFACE: + pbdrv_usb_setup_misc_tx_byte = 0; pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; pbdrv_usb_setup_data_to_send_sz = 1; handled = true; break; case GET_STATUS: - pbdrv_usb_setup_misc_tx_byte = 1; // self-powered + pbdrv_usb_setup_misc_tx_byte = 0; pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; pbdrv_usb_setup_data_to_send_sz = 2; handled = true; break; - - case GET_DESCRIPTOR: - if (usb_get_descriptor(setup_pkt.s.wValue)) { - handled = true; - } - break; } - break; - - case BM_REQ_RECIP_IF: - if (setup_pkt.s.wIndex == 0) { - switch (setup_pkt.s.bRequest) { - case GET_INTERFACE: - pbdrv_usb_setup_misc_tx_byte = 0; - pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; - pbdrv_usb_setup_data_to_send_sz = 1; - handled = true; - break; - - case GET_STATUS: - pbdrv_usb_setup_misc_tx_byte = 0; - pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; - pbdrv_usb_setup_data_to_send_sz = 2; - handled = true; - break; + } + break; + + case BM_REQ_RECIP_EP: + switch (setup_pkt.s.bRequest) { + case GET_STATUS: + if (setup_pkt.s.wIndex == 1) { + pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_RXCSRL1) & USB_RXCSRL1_STALL); + pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; + pbdrv_usb_setup_data_to_send_sz = 2; + handled = true; + } else if (setup_pkt.s.wIndex == 0x81) { + pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_TXCSRL1) & USB_TXCSRL1_STALL); + pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; + pbdrv_usb_setup_data_to_send_sz = 2; + handled = true; } - } - break; + break; - case BM_REQ_RECIP_EP: - switch (setup_pkt.s.bRequest) { - case GET_STATUS: + case CLEAR_FEATURE: + if (setup_pkt.s.wValue == 0) { if (setup_pkt.s.wIndex == 1) { - pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_RXCSRL1) & USB_RXCSRL1_STALL); - pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; - pbdrv_usb_setup_data_to_send_sz = 2; + HWREGB(USB0_BASE + USB_O_RXCSRL1) &= ~USB_RXCSRL1_STALL; handled = true; } else if (setup_pkt.s.wIndex == 0x81) { - pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_TXCSRL1) & USB_TXCSRL1_STALL); - pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; - pbdrv_usb_setup_data_to_send_sz = 2; + HWREGB(USB0_BASE + USB_O_TXCSRL1) &= ~USB_TXCSRL1_STALL; handled = true; } - break; - - case CLEAR_FEATURE: - if (setup_pkt.s.wValue == 0) { - if (setup_pkt.s.wIndex == 1) { - HWREGB(USB0_BASE + USB_O_RXCSRL1) &= ~USB_RXCSRL1_STALL; - handled = true; - } else if (setup_pkt.s.wIndex == 0x81) { - HWREGB(USB0_BASE + USB_O_TXCSRL1) &= ~USB_TXCSRL1_STALL; - handled = true; - } - } - break; + } + break; - case SET_FEATURE: - if (setup_pkt.s.wValue == 0) { - if (setup_pkt.s.wIndex == 1) { - HWREGB(USB0_BASE + USB_O_RXCSRL1) |= USB_RXCSRL1_STALL; - handled = true; - } else if (setup_pkt.s.wIndex == 0x81) { - HWREGB(USB0_BASE + USB_O_TXCSRL1) |= USB_TXCSRL1_STALL; - handled = true; - } + case SET_FEATURE: + if (setup_pkt.s.wValue == 0) { + if (setup_pkt.s.wIndex == 1) { + HWREGB(USB0_BASE + USB_O_RXCSRL1) |= USB_RXCSRL1_STALL; + handled = true; + } else if (setup_pkt.s.wIndex == 0x81) { + HWREGB(USB0_BASE + USB_O_TXCSRL1) |= USB_TXCSRL1_STALL; + handled = true; } - break; - } - break; - } - break; - - case BM_REQ_TYPE_VENDOR: - switch (setup_pkt.s.bRequest) { - case PBDRV_USB_VENDOR_REQ_WEBUSB: - if (setup_pkt.s.wIndex == WEBUSB_REQ_GET_URL && setup_pkt.s.wValue == PBDRV_USB_WEBUSB_LANDING_PAGE_URL_IDX) { - pbdrv_usb_setup_data_to_send = pbdrv_usb_webusb_landing_page.u; - pbdrv_usb_setup_data_to_send_sz = pbdrv_usb_webusb_landing_page.s.bLength; - handled = true; - } - break; + } + break; + } + break; + } + break; + + case BM_REQ_TYPE_VENDOR: + switch (setup_pkt.s.bRequest) { + case PBDRV_USB_VENDOR_REQ_WEBUSB: + if (setup_pkt.s.wIndex == WEBUSB_REQ_GET_URL && setup_pkt.s.wValue == PBDRV_USB_WEBUSB_LANDING_PAGE_URL_IDX) { + pbdrv_usb_setup_data_to_send = pbdrv_usb_webusb_landing_page.u; + pbdrv_usb_setup_data_to_send_sz = pbdrv_usb_webusb_landing_page.s.bLength; + handled = true; + } + break; + + case PBDRV_USB_VENDOR_REQ_MS_20: + if (setup_pkt.s.wIndex == MS_OS_20_DESCRIPTOR_INDEX) { + pbdrv_usb_setup_data_to_send = pbdrv_usb_ms_20_desc_set.u; + pbdrv_usb_setup_data_to_send_sz = sizeof(pbdrv_usb_ms_20_desc_set.s); + handled = true; + } + break; + } + } - case PBDRV_USB_VENDOR_REQ_MS_20: - if (setup_pkt.s.wIndex == MS_OS_20_DESCRIPTOR_INDEX) { - pbdrv_usb_setup_data_to_send = pbdrv_usb_ms_20_desc_set.u; - pbdrv_usb_setup_data_to_send_sz = sizeof(pbdrv_usb_ms_20_desc_set.s); - handled = true; - } - break; - } - } + if (!handled) { + // send stall + HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_STALL; + } else { + if (pbdrv_usb_setup_data_to_send) { + // Clamp by host request size + if (setup_pkt.s.wLength < pbdrv_usb_setup_data_to_send_sz) { + pbdrv_usb_setup_data_to_send_sz = setup_pkt.s.wLength; + } - if (!handled) { - // send stall - HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_STALL; - } else { - if (pbdrv_usb_setup_data_to_send) { - // Clamp by host request size - if (setup_pkt.s.wLength < pbdrv_usb_setup_data_to_send_sz) { - pbdrv_usb_setup_data_to_send_sz = setup_pkt.s.wLength; - } - - // Send as much as we can in one chunk - usb_setup_send_chunk(); - if (pbdrv_usb_setup_data_to_send_sz == 0) { - pbdrv_usb_setup_data_to_send = 0; - HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_TXRDY | USB_CSRL0_DATAEND; - } else { - HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_TXRDY; - } + // Send as much as we can in one chunk + usb_setup_send_chunk(); + if (pbdrv_usb_setup_data_to_send_sz == 0) { + pbdrv_usb_setup_data_to_send = 0; + HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_TXRDY | USB_CSRL0_DATAEND; } else { - // Just get ready to send ACK, no data - HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_DATAEND; + HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_TXRDY; } - } - } else if (pbdrv_usb_setup_data_to_send) { - // Need to continue to TX data - usb_setup_send_chunk(); - if (pbdrv_usb_setup_data_to_send_sz == 0) { - pbdrv_usb_setup_data_to_send = 0; - HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_TXRDY | USB_CSRL0_DATAEND; } else { - HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_TXRDY; + // Just get ready to send ACK, no data + HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_DATAEND; } } + } else if (pbdrv_usb_setup_data_to_send) { + // Need to continue to TX data + usb_setup_send_chunk(); + if (pbdrv_usb_setup_data_to_send_sz == 0) { + pbdrv_usb_setup_data_to_send = 0; + HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_TXRDY | USB_CSRL0_DATAEND; + } else { + HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_TXRDY; + } } } From a7033eba2c3f227f24b156b8385b515d597f6caf Mon Sep 17 00:00:00 2001 From: R Date: Sat, 26 Jul 2025 22:42:09 +0100 Subject: [PATCH 2/5] pbio/drv/usb/usb_ev3.c: Clear data toggle when endpoint stall is cleared This is required by the USB specification. --- lib/pbio/drv/usb/usb_ev3.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pbio/drv/usb/usb_ev3.c b/lib/pbio/drv/usb/usb_ev3.c index c5cc54424..cfdc70e75 100644 --- a/lib/pbio/drv/usb/usb_ev3.c +++ b/lib/pbio/drv/usb/usb_ev3.c @@ -634,11 +634,12 @@ static void usb_device_intr(void) { case CLEAR_FEATURE: if (setup_pkt.s.wValue == 0) { + // Clear the endpoint halt, which also resets the data toggle value if (setup_pkt.s.wIndex == 1) { - HWREGB(USB0_BASE + USB_O_RXCSRL1) &= ~USB_RXCSRL1_STALL; + HWREGB(USB0_BASE + USB_O_RXCSRL1) = (HWREGB(USB0_BASE + USB_O_RXCSRL1) & ~USB_RXCSRL1_STALL) | USB_RXCSRL1_CLRDT; handled = true; } else if (setup_pkt.s.wIndex == 0x81) { - HWREGB(USB0_BASE + USB_O_TXCSRL1) &= ~USB_TXCSRL1_STALL; + HWREGB(USB0_BASE + USB_O_TXCSRL1) = (HWREGB(USB0_BASE + USB_O_TXCSRL1) & ~USB_TXCSRL1_STALL) | USB_TXCSRL1_CLRDT; handled = true; } } From 01c1ad78fa1ac886bfc08329f0c21f69f26ebcb6 Mon Sep 17 00:00:00 2001 From: R Date: Sat, 26 Jul 2025 22:53:16 +0100 Subject: [PATCH 3/5] pbio/drv/usb/usb_ev3.c: Correct size for POWER register --- lib/pbio/drv/usb/usb_ev3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pbio/drv/usb/usb_ev3.c b/lib/pbio/drv/usb/usb_ev3.c index cfdc70e75..2e2772527 100644 --- a/lib/pbio/drv/usb/usb_ev3.c +++ b/lib/pbio/drv/usb/usb_ev3.c @@ -493,7 +493,7 @@ static void usb_device_intr(void) { pbdrv_usb_setup_data_to_send = 0; pbdrv_usb_addr_needs_setting = false; - if (HWREGH(USB0_BASE + USB_O_POWER) & USB_POWER_HSMODE) { + if (HWREGB(USB0_BASE + USB_O_POWER) & USB_POWER_HSMODE) { pbdrv_usb_is_usb_hs = true; } else { pbdrv_usb_is_usb_hs = false; From 9ca58bbf4860ac23e3be78a4ea437823a671a481 Mon Sep 17 00:00:00 2001 From: R Date: Sat, 26 Jul 2025 23:31:57 +0100 Subject: [PATCH 4/5] pbio/drv/usb/usb_ev3.c: Implement USB peripheral reset errata workaround Although this does not visibly change anything, the AM1808 errata document claims that not doing this sequence can theoretically result in a timing violation inside the logic, which can cause unpredictable results. Since it is simple, we can just apply the recommended workaorund sequence. --- lib/pbio/drv/usb/usb_ev3.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/pbio/drv/usb/usb_ev3.c b/lib/pbio/drv/usb/usb_ev3.c index 2e2772527..b3ce83284 100644 --- a/lib/pbio/drv/usb/usb_ev3.c +++ b/lib/pbio/drv/usb/usb_ev3.c @@ -823,9 +823,15 @@ void pbdrv_usb_init(void) { // to the host, then reset the USB controller. USBDevDisconnect(USB0_BASE); + // Reset the module through the PSC PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_USB0, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_SWRSTDISABLE); PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_USB0, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE); + // Local soft reset, as well as following the sequence described in sprz313h.pdf + // Advisory 2.3.3 describing a workaround for a potential reset timing issue + USBReset(USB_0_OTGBASE); + PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_USB0, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_DISABLE); + // This reset sequence is from Example 34-1 in the AM1808 TRM (spruh82c.pdf) // Because PHYs and clocking are... as they tend to be, use the precise sequence // of operations specified. @@ -859,6 +865,9 @@ void pbdrv_usb_init(void) { while (!(HWREG(CFGCHIP2_USBPHYCTRL) & CFGCHIP2_PHYCLKGD)) { } + // Final enable for the USB module + PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_USB0, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE); + // Enable "PDR" mode for handling interrupts // // The datasheet doesn't clearly explain what this means, From f42d61bc46fda4e3a3c8f5ad877e85ec51281e31 Mon Sep 17 00:00:00 2001 From: R Date: Sun, 27 Jul 2025 00:36:25 +0100 Subject: [PATCH 5/5] 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. --- lib/pbio/drv/usb/usb_ev3.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/pbio/drv/usb/usb_ev3.c b/lib/pbio/drv/usb/usb_ev3.c index b3ce83284..7ee7f4da1 100644 --- a/lib/pbio/drv/usb/usb_ev3.c +++ b/lib/pbio/drv/usb/usb_ev3.c @@ -540,7 +540,6 @@ static void usb_device_intr(void) { setup_pkt.u[0] = HWREG(USB0_BASE + USB_O_FIFO0); setup_pkt.u[1] = HWREG(USB0_BASE + USB_O_FIFO0); - HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_RXRDYC; switch (setup_pkt.s.bmRequestType & BM_REQ_TYPE_MASK) { case BM_REQ_TYPE_STANDARD: @@ -681,11 +680,29 @@ static void usb_device_intr(void) { } } + // Note regarding the setting of the USB_CSRL0_RXRDYC bit: + // The Linux kernel has a comment saying + // > For zero-data requests we want to delay the STATUS stage to avoid SETUPEND errors. + // but also that + // > If we write data, the controller acts happier if we enable the TX FIFO right away + // We implement something similar but not identical. In general, we wait until + // we have completely processed the request and decided what we're going to do + // before we indicate that we are ready to progress to the next phase. + // We do not support any requests that require receiving data from the host, + // only zero-data requests or those that require sending data to the host. + // For errors or zero-data requests, we set USB_CSRL0_RXRDYC and USB_CSRL0_DATAEND + // at the same time so that we don't get spurious SETUPEND errors + // (which we treat as a command failure). For requests that require sending data, + // we set USB_CSRL0_RXRDYC while we get ready to copy data into the FIFO. + if (!handled) { - // send stall - HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_STALL; + // Indicate we read the packet, but also send stall + HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_RXRDYC | USB_CSRL0_STALL; } else { if (pbdrv_usb_setup_data_to_send) { + // Indicate we read the packet + HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_RXRDYC; + // Clamp by host request size if (setup_pkt.s.wLength < pbdrv_usb_setup_data_to_send_sz) { pbdrv_usb_setup_data_to_send_sz = setup_pkt.s.wLength; @@ -701,7 +718,7 @@ static void usb_device_intr(void) { } } else { // Just get ready to send ACK, no data - HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_DATAEND; + HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_RXRDYC | USB_CSRL0_DATAEND; } } } else if (pbdrv_usb_setup_data_to_send) {