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.
pull/9768/head
Russ Butler 2018-06-25 17:44:39 -05:00
parent c4cf19ee3b
commit 86e1d436e7
4 changed files with 291 additions and 22 deletions

View File

@ -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;
}
/**
* @}
*/

View File

@ -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);

View File

@ -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

View File

@ -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);