diff --git a/os/hal/ports/NUMICRO/LLD/USBv2/hal_usb_lld.c b/os/hal/ports/NUMICRO/LLD/USBv2/hal_usb_lld.c index df224c3072..23517951b5 100644 --- a/os/hal/ports/NUMICRO/LLD/USBv2/hal_usb_lld.c +++ b/os/hal/ports/NUMICRO/LLD/USBv2/hal_usb_lld.c @@ -36,6 +36,8 @@ #define NUC126_USBD_CFG_OUT (1UL << USBD_CFG_STATE_Pos) #define NUC126_USBD_CFG_IN (2UL << USBD_CFG_STATE_Pos) +#define NUC126_USBD_INTSTS_EPEVT_Msk (0xFFUL << USBD_INTSTS_EPEVT0_Pos) + #define USBD_SRAM_BASE ((volatile uint8_t*)(USBD_BASE + 0x100)) #define NUC126_USBD_BUSSTATUS_INACK 0UL @@ -100,6 +102,12 @@ static const USBEndpointConfig ep0config = {USB_EP_MODE_TYPE_CTRL, */ static uint32_t sram_free_dword_offset = 1UL; +/** + * @brief OUT endpoints whose last accepted packet was DATA1, one bit per + * logical endpoint. The hardware ACKs a repeated packet as new data. + */ +static uint32_t out_last_data1; + /*===========================================================================*/ /* Driver local functions. */ /*===========================================================================*/ @@ -187,6 +195,23 @@ size_t usb_memcpy(volatile void* destination, const volatile void* source, static void usb_serve_out_endpoint(uint32_t epn) { USBDriver *const usbp = &USBD1; + uint32_t epsts = (USBD->EPSTS >> (USBD_EPSTS_EPSTS0_Pos + 3 * _HW_OUT_EPN(epn))) & 0x7UL; + + if ((epsts == NUC126_USBD_BUSSTATUS_OUTDATA0ACK) || + (epsts == NUC126_USBD_BUSSTATUS_OUTDATA1ACK)) { + uint32_t data1 = (epsts == NUC126_USBD_BUSSTATUS_OUTDATA1ACK) ? 1UL : 0UL; + + /* Same toggle as the last accepted packet: a repeat (USB 2.0 8.6.4), + drop it and receive again into the same place */ + if (((out_last_data1 >> epn) & 1UL) == data1) { + HW_OUT_EP(epn)->MXPLD = min(usbp->epc[epn]->out_maxsize, + usbp->epc[epn]->out_state->rxsize - + usbp->epc[epn]->out_state->rxcnt); + return; + } + out_last_data1 ^= (1UL << epn); + } + uint32_t mxpld = HW_OUT_EP(epn)->MXPLD; uint32_t rxsize_actual = min(mxpld, usbp->epc[epn]->out_state->rxsize - usbp->epc[epn]->out_state->rxcnt); @@ -270,6 +295,9 @@ static void usb_lld_serve_interrupt(USBDriver* usbp) /* Bus reset */ USBD->ATTR |= (USBD_ATTR_PHYEN_Msk | USBD_ATTR_USBEN_Msk); _usb_reset(usbp); + /* EP events read with the reset happened before it: drop them */ + USBD->INTSTS = intsts & NUC126_USBD_INTSTS_EPEVT_Msk; + intsts &= ~NUC126_USBD_INTSTS_EPEVT_Msk; } if (bussts & USBD_ATTR_SUSPEND_Msk) { /* Enable USB but disable PHY */ @@ -284,19 +312,8 @@ static void usb_lld_serve_interrupt(USBDriver* usbp) } if (intsts & USBD_INTSTS_USBIF_Msk) { - if (intsts & USBD_INTSTS_SETUP_Msk) { - /* Clear event flag */ - USBD->INTSTS = USBD_INTSTS_SETUP_Msk; - - /* Clear the data IN/OUT ready flag of control end-points */ - HW_IN_EP(0)->CFGP |= USBD_CFGP_CLRRDY_Msk; - HW_OUT_EP(0)->CFGP |= USBD_CFGP_CLRRDY_Msk; - - HW_IN_EP(0)->CFG |= USBD_CFG_DSQSYNC_Msk; - _usb_isr_invoke_setup_cb(&USBD1, 0); - } - - /* EP events */ + /* EP events, served before a SETUP of the same read because they belong + to the transfers before it */ if (intsts & USBD_INTSTS_EPEVT0_Msk) { /* Clear event flag */ USBD->INTSTS = USBD_INTSTS_EPEVT0_Msk; @@ -344,6 +361,20 @@ static void usb_lld_serve_interrupt(USBDriver* usbp) USBD->INTSTS = (USBD_INTSTS_EPEVT7_Msk); usb_serve_in_endpoint(LOGICAL_EPN(7)); } + + if (intsts & USBD_INTSTS_SETUP_Msk) { + /* Clear event flag */ + USBD->INTSTS = USBD_INTSTS_SETUP_Msk; + + /* Clear the data IN/OUT ready flag and the STALL of control end-points */ + HW_IN_EP(0)->CFGP = USBD_CFGP_CLRRDY_Msk; + HW_OUT_EP(0)->CFGP = USBD_CFGP_CLRRDY_Msk; + + HW_IN_EP(0)->CFG |= USBD_CFG_DSQSYNC_Msk; + /* The data stage starts with DATA1 */ + out_last_data1 &= ~1UL; + _usb_isr_invoke_setup_cb(&USBD1, 0); + } } } @@ -479,6 +510,13 @@ void usb_lld_stop(USBDriver* usbp) */ void usb_lld_reset(USBDriver* usbp) { + /* Disable every endpoint and clear its ready state, STALL and data + toggle */ + for (uint8_t i = 0; i < NUC126_USB_HW_ENDPOINTS; ++i) { + USBD->EP[i].CFGP = USBD_CFGP_CLRRDY_Msk; + USBD->EP[i].CFG = 0; + } + sram_free_dword_offset = 1UL; USBD->FADDR = 0; /* EP0 initialization.*/ @@ -515,6 +553,13 @@ void usb_lld_init_endpoint(USBDriver* usbp, usbep_t ep) "isochronous EP cannot be IN and OUT"); } + /* Cancel any ready transaction and clear the STALL: the Halt feature is + reset by SET_CONFIGURATION */ + HW_OUT_EP(ep)->CFGP = USBD_CFGP_CLRRDY_Msk; + HW_IN_EP(ep)->CFGP = USBD_CFGP_CLRRDY_Msk; + /* The first OUT packet is DATA0 */ + out_last_data1 |= (1UL << ep); + /* If the out logical endpoint is used */ if (usbp->epc[ep]->out_state) { switch (usbp->epc[ep]->ep_mode) { @@ -769,6 +814,9 @@ void usb_lld_clear_out(USBDriver* usbp, usbep_t ep) { (void)usbp; HW_OUT_EP(ep)->CFGP &= ~USBD_CFGP_SSTALL_Msk; + /* The first packet after a halt is DATA0 */ + HW_OUT_EP(ep)->CFG &= ~USBD_CFG_DSQSYNC_Msk; + out_last_data1 |= (1UL << ep); } /** @@ -783,6 +831,8 @@ void usb_lld_clear_in(USBDriver* usbp, usbep_t ep) { (void)usbp; HW_IN_EP(ep)->CFGP &= ~USBD_CFGP_SSTALL_Msk; + /* The first packet after a halt is DATA0 */ + HW_IN_EP(ep)->CFG &= ~USBD_CFG_DSQSYNC_Msk; } #endif /* HAL_USE_USB */ diff --git a/os/hal/ports/NUMICRO/LLD/USBv2/hal_usb_lld.h b/os/hal/ports/NUMICRO/LLD/USBv2/hal_usb_lld.h index 6b4d43af7d..7d7803eb1c 100644 --- a/os/hal/ports/NUMICRO/LLD/USBv2/hal_usb_lld.h +++ b/os/hal/ports/NUMICRO/LLD/USBv2/hal_usb_lld.h @@ -391,15 +391,16 @@ struct USBDriver { /** * @brief Start of host wake-up procedure. + * @note The PHY is off since SUSPEND. The RESUME bus event ends suspend. * * @notapi */ #define usb_lld_wakeup_host(usbp) \ do { \ + USBD->ATTR |= USBD_ATTR_PHYEN_Msk; \ USBD->ATTR |= USBD_ATTR_RWAKEUP_Msk; \ osalThreadSleepMilliseconds(10); \ USBD->ATTR &= ~USBD_ATTR_RWAKEUP_Msk; \ - _usb_wakeup(&USBD1); \ } while (0) /*===========================================================================*/