From 86e1d436e7f1756ef8f051343648e5d96ec9c914 Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Mon, 25 Jun 2018 17:44:39 -0500 Subject: [PATCH] Alternate implementation of HAL_PCD_EP_Abort Update the patch "Create HAL_PCD_EP_Abort" to fix bugs. This patch adds the low level functions USB_EPStopXfer, USB_EPSetNak, USB_EPClearNak and the high level function HAL_PCD_EP_Abort so that transfers can be stopped. The functions USB_EPSetNak and USB_EPClearNak allow nak to be enabled or disabled for an endpoint, preventing or allowing further transfers. The function USB_EPStopXfer stops pending reads and writes started by USB_EPStartXfer along with clearing and masking any interrupts enabled by USB_EPStartXfer. The function HAL_PCD_EP_Abort aborts any transfers on the given endpoint. When this function completes the transfer interrupt is guarenteed not to fire for this endpoint. Furthermore, the size of data transferred during an aborted read can be found by calling the function HAL_PCD_EP_GetRxCount. Other notes on this Change: 1. Prior to this patch the interrupt USB_OTG_DOEPINT_EPDISD was not handled. When an OUT endpoint was disabled this interrupt occurred causing the CPU to get stuck repeatedly handling this interrupt. This is because this interrupt was unmasked but nothing cleared this interrupt. This patch also adds code to handle and clear this interrupt to prevent a lockup. 2. Stopping a transfer on an OUT endpoint requires global nak OUT to be in effect. Even with this being done, having entries in the rx fifo prevented an OUT endpoint from being disabled. This behavior is not mentioned in the Reference Manual. --- .../TARGET_STM32F2/device/stm32f2xx_hal_pcd.c | 143 ++++++++++++--- .../TARGET_STM32F2/device/stm32f2xx_hal_pcd.h | 1 + .../TARGET_STM32F2/device/stm32f2xx_ll_usb.c | 166 ++++++++++++++++++ .../TARGET_STM32F2/device/stm32f2xx_ll_usb.h | 3 + 4 files changed, 291 insertions(+), 22 deletions(-) diff --git a/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_hal_pcd.c b/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_hal_pcd.c index ee1703bbd1..415c279307 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_hal_pcd.c +++ b/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_hal_pcd.c @@ -104,6 +104,7 @@ * @{ */ static HAL_StatusTypeDef PCD_WriteEmptyTxFifo(PCD_HandleTypeDef *hpcd, uint32_t epnum); +static HAL_StatusTypeDef PCD_ReadRxFifo(PCD_HandleTypeDef *hpcd); /** * @} */ @@ -314,7 +315,6 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd) USB_OTG_GlobalTypeDef *USBx = hpcd->Instance; uint32_t i = 0U, ep_intr = 0U, epint = 0U, epnum = 0U; uint32_t fifoemptymsk = 0U, temp = 0U; - USB_OTG_EPTypeDef *ep; uint32_t hclk = 120000000U; /* ensure that we are in device mode */ @@ -366,6 +366,11 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd) } } + if (( epint & USB_OTG_DOEPINT_EPDISD) == USB_OTG_DOEPINT_EPDISD) + { + CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_EPDISD); + } + if(( epint & USB_OTG_DOEPINT_STUP) == USB_OTG_DOEPINT_STUP) { /* Inform the upper layer that a setup packet is available */ @@ -596,27 +601,7 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd) /* Handle RxQLevel Interrupt */ if(__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL)) { - USB_MASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL); - - temp = USBx->GRXSTSP; - - ep = &hpcd->OUT_ep[temp & USB_OTG_GRXSTSP_EPNUM]; - - if(((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_DATA_UPDT) - { - if((temp & USB_OTG_GRXSTSP_BCNT) != 0U) - { - USB_ReadPacket(USBx, ep->xfer_buff, (temp & USB_OTG_GRXSTSP_BCNT) >> 4U); - ep->xfer_buff += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U; - ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U; - } - } - else if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_SETUP_UPDT) - { - USB_ReadPacket(USBx, (uint8_t *)hpcd->Setup, 8U); - ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U; - } - USB_UNMASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL); + PCD_ReadRxFifo(hpcd); } /* Handle SOF Interrupt */ @@ -1043,6 +1028,84 @@ HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, return HAL_OK; } +/** + * @brief Abort a transaction. + * @param hpcd: PCD handle + * @param ep_addr: endpoint address + * @retval HAL status + */ +HAL_StatusTypeDef HAL_PCD_EP_Abort(PCD_HandleTypeDef *hpcd, uint8_t ep_addr) +{ + USB_OTG_GlobalTypeDef *USBx = hpcd->Instance; + HAL_StatusTypeDef ret = HAL_OK; + USB_OTG_EPTypeDef *ep; + + if ((0x80 & ep_addr) == 0x80) + { + ep = &hpcd->IN_ep[ep_addr & 0x7F]; + } + else + { + ep = &hpcd->OUT_ep[ep_addr]; + } + + __HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); + + ep->num = ep_addr & 0x7F; + ep->is_in = ((ep_addr & 0x80) == 0x80); + + USB_EPSetNak(hpcd->Instance, ep); + + if ((0x80 & ep_addr) == 0x80) + { + ret = USB_EPStopXfer(hpcd->Instance , ep); + if (ret == HAL_OK) + { + ret = USB_FlushTxFifo(hpcd->Instance, ep_addr & 0x7F); + } + } + else + { + /* Set global NAK */ + USBx_DEVICE->DCTL |= USB_OTG_DCTL_SGONAK; + + /* Read all entries from the fifo so global NAK takes effect */ + while (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL)) + { + PCD_ReadRxFifo(hpcd); + } + + /* Stop the transfer */ + ret = USB_EPStopXfer(hpcd->Instance , ep); + if (ret == HAL_BUSY) + { + /* If USB_EPStopXfer returns HAL_BUSY then a setup packet + * arrived after the rx fifo was processed but before USB_EPStopXfer + * was called. Process the rx fifo one more time to read the + * setup packet. + * + * Note - after the setup packet has been received no further + * packets will be received over USB. This is because the next + * phase (data or status) of the control transfer started by + * the setup packet will be naked until global nak is cleared. + */ + while (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL)) + { + PCD_ReadRxFifo(hpcd); + } + + ret = USB_EPStopXfer(hpcd->Instance , ep); + } + + /* Clear global nak */ + USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGONAK; + } + + __HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); + + return ret; +} + /** * @brief Set a STALL condition over an endpoint. * @param hpcd: PCD handle @@ -1256,6 +1319,42 @@ static HAL_StatusTypeDef PCD_WriteEmptyTxFifo(PCD_HandleTypeDef *hpcd, uint32_t return HAL_OK; } +/** + * @brief Process the next RX fifo entry + * @param hpcd: PCD handle + * @retval HAL status + */ +static HAL_StatusTypeDef PCD_ReadRxFifo(PCD_HandleTypeDef *hpcd) +{ + USB_OTG_GlobalTypeDef *USBx = hpcd->Instance; + USB_OTG_EPTypeDef *ep; + uint32_t temp = 0; + + USB_MASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL); + + temp = USBx->GRXSTSP; + + ep = &hpcd->OUT_ep[temp & USB_OTG_GRXSTSP_EPNUM]; + + if(((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_DATA_UPDT) + { + if((temp & USB_OTG_GRXSTSP_BCNT) != 0U) + { + USB_ReadPacket(USBx, ep->xfer_buff, (temp & USB_OTG_GRXSTSP_BCNT) >> 4U); + ep->xfer_buff += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U; + ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U; + } + } + else if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_SETUP_UPDT) + { + USB_ReadPacket(USBx, (uint8_t *)hpcd->Setup, 8U); + ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U; + } + USB_UNMASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL); + + return HAL_OK; +} + /** * @} */ diff --git a/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_hal_pcd.h b/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_hal_pcd.h index f617e6d8de..610c8456e9 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_hal_pcd.h +++ b/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_hal_pcd.h @@ -271,6 +271,7 @@ HAL_StatusTypeDef HAL_PCD_EP_Open(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint HAL_StatusTypeDef HAL_PCD_EP_Close(PCD_HandleTypeDef *hpcd, uint8_t ep_addr); HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len); HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len); +HAL_StatusTypeDef HAL_PCD_EP_Abort(PCD_HandleTypeDef *hpcd, uint8_t ep_addr); uint16_t HAL_PCD_EP_GetRxCount(PCD_HandleTypeDef *hpcd, uint8_t ep_addr); HAL_StatusTypeDef HAL_PCD_EP_SetStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr); HAL_StatusTypeDef HAL_PCD_EP_ClrStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr); diff --git a/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_ll_usb.c b/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_ll_usb.c index b4799572c4..c15db199e7 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_ll_usb.c +++ b/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_ll_usb.c @@ -746,6 +746,96 @@ HAL_StatusTypeDef USB_EP0StartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeD return HAL_OK; } +/** + * @brief USB_EPStoptXfer : stop transfer on this endpoint + * @param USBx : Selected device + * @param ep: pointer to endpoint structure + * @retval HAL status + * @note IN endpoints must have NAK enabled before calling this function + * @note OUT endpoints must have global out NAK enabled before calling this + * function. Furthermore, the RX fifo must be empty or the status + * HAL_BUSY will be returned. + */ +HAL_StatusTypeDef USB_EPStopXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep) +{ + HAL_StatusTypeDef ret = HAL_OK; + uint32_t count = 0U; + uint32_t epint, fifoemptymsk; + + /* IN endpoint */ + if (ep->is_in == 1U) + { + + /* EP enable, IN data in FIFO */ + if (((USBx_INEP(ep->num)->DIEPCTL) & USB_OTG_DIEPCTL_EPENA) == USB_OTG_DIEPCTL_EPENA) + { + /* Disable this endpoint */ + USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_EPDIS; + count = 0; + do + { + if (++count > 200000U) + { + return HAL_TIMEOUT; + } + } + while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_EPENA) == USB_OTG_DIEPCTL_EPENA); + } + + /* Clear transfer complete interrupt */ + epint = USB_ReadDevInEPInterrupt(USBx, ep->num); + if((epint & USB_OTG_DIEPINT_XFRC) == USB_OTG_DIEPINT_XFRC) + { + CLEAR_IN_EP_INTR(ep->num, USB_OTG_DIEPINT_XFRC); + } + + /* Mask fifo empty interrupt */ + fifoemptymsk = 0x1U << ep->num; + atomic_clr_u32(&USBx_DEVICE->DIEPEMPMSK, fifoemptymsk); + } + else /* OUT endpoint */ + { + if (((USBx_OUTEP(ep->num)->DOEPCTL) & USB_OTG_DOEPCTL_EPENA) == USB_OTG_DOEPCTL_EPENA) + { + /* Disable this endpoint */ + USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_EPDIS; + count = 0; + do + { + if (++count > 200000U) + { + return HAL_TIMEOUT; + } + if ((USBx->GINTSTS & USB_OTG_GINTSTS_RXFLVL) == USB_OTG_GINTSTS_RXFLVL) + { + /* Although not mentioned in the Reference Manual, it appears that the + * rx fifo must be empty for an OUT endpoint to be disabled. Typically + * this will happen when setting the global OUT nak (required by Reference + * Manual) as this requires processing the rx fifo. This is not guaranteed + * though, as a setup packet can arrive even while global OUT nak is set. + * + * During testing this event was observed and prevented endpoint disabling + * from completing until the rx fifo was empty. To address this problem + * return HAL_BUSY if the rx fifo is not empty to give higher level code + * a chance to clear the fifo and retry the operation. + * + */ + return HAL_BUSY; + } + } + while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_EPENA) == USB_OTG_DOEPCTL_EPENA); + } + + /* Clear interrupt */ + epint = USB_ReadDevOutEPInterrupt(USBx, ep->num); + if(( epint & USB_OTG_DOEPINT_XFRC) == USB_OTG_DOEPINT_XFRC) + { + CLEAR_OUT_EP_INTR(ep->num, USB_OTG_DOEPINT_XFRC); + } + } + return ret; +} + /** * @brief USB_WritePacket : Writes a packet into the Tx FIFO associated * with the EP/channel @@ -855,6 +945,82 @@ HAL_StatusTypeDef USB_EPClearStall(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDe return HAL_OK; } +/** + * @brief USB_EPSetNak : stop transfer and nak all tokens on this endpoint + * @param USBx : Selected device + * @param ep: pointer to endpoint structure + * @retval HAL status + */ +HAL_StatusTypeDef USB_EPSetNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep) +{ + uint32_t count = 0; + if (ep->is_in == 1U) + { + USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_SNAK; + count = 0; + do + { + if (++count > 200000U) + { + return HAL_TIMEOUT; + } + } + while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_NAKSTS) != USB_OTG_DIEPCTL_NAKSTS); + } + else + { + USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_SNAK; + count = 0; + do + { + if (++count > 200000U) + { + return HAL_TIMEOUT; + } + } + while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) != USB_OTG_DOEPCTL_NAKSTS); + } + return HAL_OK; +} + +/** + * @brief USB_EPSetNak : resume transfer and stop naking on this endpoint + * @param USBx : Selected device + * @param ep: pointer to endpoint structure + * @retval HAL status + */ +HAL_StatusTypeDef USB_EPClearNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep) +{ + uint32_t count = 0; + if (ep->is_in == 1U) + { + USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_CNAK; + count = 0; + do + { + if (++count > 200000U) + { + return HAL_TIMEOUT; + } + } + while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_NAKSTS) == USB_OTG_DIEPCTL_NAKSTS); + } + else + { + USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + count = 0; + do + { + if (++count > 200000U) + { + return HAL_TIMEOUT; + } + } + while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) == USB_OTG_DOEPCTL_NAKSTS); + } + return HAL_OK; +} + /** * @brief USB_StopDevice : Stop the usb device mode * @param USBx : Selected device diff --git a/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_ll_usb.h b/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_ll_usb.h index ebbb598761..1d04629847 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_ll_usb.h +++ b/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_ll_usb.h @@ -404,11 +404,14 @@ HAL_StatusTypeDef USB_DeactivateEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EP HAL_StatusTypeDef USB_ActivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep); HAL_StatusTypeDef USB_DeactivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep); HAL_StatusTypeDef USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep, uint8_t dma); +HAL_StatusTypeDef USB_EPStopXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); HAL_StatusTypeDef USB_EP0StartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep, uint8_t dma); HAL_StatusTypeDef USB_WritePacket(USB_OTG_GlobalTypeDef *USBx, uint8_t *src, uint8_t ch_ep_num, uint16_t len, uint8_t dma); void * USB_ReadPacket(USB_OTG_GlobalTypeDef *USBx, uint8_t *dest, uint16_t len); HAL_StatusTypeDef USB_EPSetStall(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); HAL_StatusTypeDef USB_EPClearStall(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); +HAL_StatusTypeDef USB_EPSetNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); +HAL_StatusTypeDef USB_EPClearNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); HAL_StatusTypeDef USB_SetDevAddress (USB_OTG_GlobalTypeDef *USBx, uint8_t address); HAL_StatusTypeDef USB_DevConnect (USB_OTG_GlobalTypeDef *USBx); HAL_StatusTypeDef USB_DevDisconnect (USB_OTG_GlobalTypeDef *USBx);