SAMA5 UDPHS: Fixes related to null packet and SETUP OUT data handling

This commit is contained in:
Gregory Nutt 2013-09-03 19:13:34 -06:00
parent d294826cfd
commit a9a8801472

View File

@ -157,17 +157,18 @@
#define SAM_TRACEERR_DISPATCHSTALL 0x000e
#define SAM_TRACEERR_DRIVER 0x000f
#define SAM_TRACEERR_DRIVERREGISTERED 0x0010
#define SAM_TRACEERR_EP0SETUPSTALLED 0x0011
#define SAM_TRACEERR_EPINBUSY 0x0012
#define SAM_TRACEERR_EPOUTNULLPACKET 0x0013
#define SAM_TRACEERR_EPRESERVE 0x0014
#define SAM_TRACEERR_EPTCFGMAPD 0x0015
#define SAM_TRACEERR_INVALIDCTRLREQ 0x0016
#define SAM_TRACEERR_INVALIDPARMS 0x0017
#define SAM_TRACEERR_IRQREGISTRATION 0x0018
#define SAM_TRACEERR_NOTCONFIGURED 0x0019
#define SAM_TRACEERR_REQABORTED 0x001a
#define SAM_TRACEERR_TXRDYERR 0x001b
#define SAM_TRACEERR_EP0SETUPOUTSIZE 0x0011
#define SAM_TRACEERR_EP0SETUPSTALLED 0x0012
#define SAM_TRACEERR_EPINBUSY 0x0013
#define SAM_TRACEERR_EPOUTNULLPACKET 0x0014
#define SAM_TRACEERR_EPRESERVE 0x0015
#define SAM_TRACEERR_EPTCFGMAPD 0x0016
#define SAM_TRACEERR_INVALIDCTRLREQ 0x0017
#define SAM_TRACEERR_INVALIDPARMS 0x0018
#define SAM_TRACEERR_IRQREGISTRATION 0x0019
#define SAM_TRACEERR_NOTCONFIGURED 0x001a
#define SAM_TRACEERR_REQABORTED 0x001b
#define SAM_TRACEERR_TXRDYERR 0x001c
/* Trace interrupt codes */
@ -575,6 +576,7 @@ const struct trace_msg_t g_usb_trace_strings_deverror[] =
TRACE_STR(SAM_TRACEERR_DISPATCHSTALL),
TRACE_STR(SAM_TRACEERR_DRIVER),
TRACE_STR(SAM_TRACEERR_DRIVERREGISTERED),
TRACE_STR(SAM_TRACEERR_EP0SETUPOUTSIZE),
TRACE_STR(SAM_TRACEERR_EP0SETUPSTALLED),
TRACE_STR(SAM_TRACEERR_EPINBUSY),
TRACE_STR(SAM_TRACEERR_EPOUTNULLPACKET),
@ -1016,7 +1018,7 @@ static int sam_req_rddma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
/* Switch to the receiving state */
privep->epstate = UDPHS_EPSTATE_RECEIVING;
privep->txnullpkt = 0;
privep->txnullpkt = false;
privreq->inflight = 0;
privreq->req.xfrd = 0;
@ -1147,7 +1149,7 @@ static void sam_req_complete(struct sam_ep_s *privep, int16_t result)
/* Reset the endpoint state and restore the stalled indication */
privep->epstate = UDPHS_EPSTATE_IDLE;
privep->txnullpkt = 0;
privep->txnullpkt = false;
}
}
@ -1226,13 +1228,6 @@ static int sam_req_wrnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
nbytes = bytesleft;
}
/* Either (1) we are committed to sending the null packet (because txnullpkt == 1
* && nbytes == 0), or (2) we havenot yet sent the last packet (nbytes > 0).
* In either case, it is appropriate to clear txnullpkt now.
*/
privep->txnullpkt = 0;
/* If we are not sending a NULL packet, then clip the size to maxpacket
* and check if we need to send a following NULL packet.
*/
@ -1247,17 +1242,6 @@ static int sam_req_wrnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
if (nbytes >= privep->ep.maxpacket)
{
nbytes = privep->ep.maxpacket;
/* Handle the case where this packet is exactly the
* maxpacketsize. Do we need to send a zero-length packet
* in this case?
*/
if (bytesleft == privep->ep.maxpacket &&
(privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0)
{
privep->txnullpkt = 1;
}
}
/* This is the new number of bytes "in-flight" */
@ -1317,6 +1301,10 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
int bytesleft;
int ret;
/* Get the unadorned endpoint number */
epno = USB_EPNO(privep->ep.eplog);
/* We get here when an IN endpoint interrupt occurs. So now we know that
* there is no TX transfer in progress.
*/
@ -1333,10 +1321,27 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
*/
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINQEMPTY), 0);
/* Get the endpoint type */
regval = sam_getreg(SAM_UDPHS_EPTCFG(epno));
eptype = regval & UDPHS_EPTCFG_TYPE_MASK;
/* Disable interrupts on non-control endpoints */
if (eptype != UDPHS_EPTCFG_TYPE_CTRL8)
{
regval = sam_getreg(SAM_UDPHS_IEN);
regval &= ~UDPHS_INT_EPT(epno);
sam_putreg(regval, SAM_UDPHS_IEN);
}
/* Disable the TXRDY interrupt */
sam_putreg(UDPHS_EPTCTL_TXRDY, SAM_UDPHS_EPTCTLDIS(epno));
return -ENOENT;
}
epno = USB_EPNO(privep->ep.eplog);
ullvdbg("epno=%d req=%p: len=%d xfrd=%d inflight=%d nullpkt=%d\n",
epno, privreq, privreq->req.len, privreq->req.xfrd,
privreq->inflight, privep->txnullpkt);
@ -1365,13 +1370,13 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
* will be set.
*/
privep->txnullpkt = 1;
privep->txnullpkt = true;
}
else
{
/* No zero packet is forthcoming (maybe later) */
privep->txnullpkt = 0;
privep->txnullpkt = false;
}
/* The way that we handle the transfer is going to depend on
@ -1405,7 +1410,7 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
* this transfer.
*/
else
else if (privreq->req.len == 0 || privep->txnullpkt)
{
/* If we get here, then we sent the last of the data on the
* previous pass and we need to send the zero length packet now.
@ -1415,7 +1420,7 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
*/
privep->epstate = UDPHS_EPSTATE_SENDING;
privep->txnullpkt = 0;
privep->txnullpkt = false;
privreq->inflight = 0;
/* Initiate the zero length transfer and configure to receive the
@ -1426,7 +1431,11 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
}
/* If all of the bytes were sent (including any final null packet)
* then we are finished with the request buffer).
* then we are finished with the request buffer), then we can return
* the request buffer to the class driver. The transfer is not
* finished yet, however. There are still bytes in flight. The
* transfer is truly finished when we are called again and the
* request buffer is empty.
*/
if (privreq->req.len >= privreq->req.xfrd &&
@ -1437,24 +1446,7 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)),
privreq->req.xfrd);
/* Get the endpoint type */
regval = sam_getreg(SAM_UDPHS_EPTCFG(epno));
eptype = regval & UDPHS_EPTCFG_TYPE_MASK;
/* Disable interrupts on non-control endpoints */
if (eptype != UDPHS_EPTCFG_TYPE_CTRL8)
{
regval = sam_getreg(SAM_UDPHS_IEN);
regval &= ~UDPHS_INT_EPT(epno);
sam_putreg(regval, SAM_UDPHS_IEN);
}
/* Disable the TXRDY interrupt */
sam_putreg(UDPHS_EPTCTL_TXRDY, SAM_UDPHS_EPTCTLDIS(epno));
privep->txnullpkt = 0;
privep->txnullpkt = false;
sam_req_complete(privep, OK);
}
}
@ -1729,7 +1721,7 @@ static void sam_ep0_dispatch(struct sam_usbdev_s *priv)
/* Stall on failure */
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_DISPATCHSTALL), 0);
(void)sam_ep_stall(&priv->eplist[EP0].ep, true);
(void)sam_ep_stall(&priv->eplist[EP0].ep, false);
}
}
}
@ -2232,7 +2224,7 @@ static void sam_ep0_setup(struct sam_usbdev_s *priv)
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EP0SETUPSTALLED),
priv->ctrl.req);
(void)sam_ep_stall(&priv->eplist[EP0].ep, true);
(void)sam_ep_stall(&priv->eplist[EP0].ep, false);
}
break;
@ -2445,6 +2437,7 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
/* Set the device address */
privep->epstate = UDPHS_EPSTATE_IDLE;
sam_setdevaddr(priv, priv->devaddr);
/* Disable the further TXRDY interrupts on EP0. */
@ -2487,24 +2480,34 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
{
uint16_t len;
#ifdef CONFIG_DEBUG
/* Yes.. get the size of the packet that we just received */
/* Yes.. back to the IDLE state */
privep->epstate = UDPHS_EPSTATE_IDLE;
/* Get the size of the packet that we just received */
pktsize = (uint16_t)
((eptsta & UDPHS_EPTSTA_BYTECNT_MASK) >>
UDPHS_EPTSTA_BYTECNT_SHIFT);
#endif
/* Copy the OUT data from the EP0 FIFO into special EP0 buffer. */
/* Get the size that we expected to receive */
len = GETUINT16(priv->ctrl.len);
DEBUGASSERT(len > 0 && len == pktsize);
sam_ep0_read(priv->ep0out, len);
if (len == pktsize)
{
/* Copy the OUT data from the EP0 FIFO into special EP0 buffer. */
/* And handle the EP0 SETUP now. */
sam_ep0_read(priv->ep0out, len);
privep->epstate = UDPHS_EPSTATE_IDLE;
sam_ep0_setup(priv);
/* And handle the EP0 SETUP now. */
sam_ep0_setup(priv);
}
else
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EP0SETUPOUTSIZE), pktsize);
(void)sam_ep_stall(&privep->ep, false);
}
}
else
{
@ -2605,11 +2608,11 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
len = GETUINT16(priv->ctrl.len);
if (USB_REQ_ISOUT(priv->ctrl.type) && len > 0)
{
/* Yes.. then we have to wait for the IN data phase to
/* Yes.. then we have to wait for the OUT data phase to
* complete before processing the SETUP command.
*/
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPIN), len);
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPOUT), priv->ctrl.req);
privep->epstate = UDPHS_EPSTATE_EP0DATAOUT;
}
else
@ -2618,7 +2621,7 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
* Handle the EP0 SETUP now.
*/
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPOUT), priv->ctrl.req);
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPIN), len);
privep->epstate = UDPHS_EPSTATE_IDLE;
sam_ep0_setup(priv);
}
@ -3474,7 +3477,7 @@ static int sam_ep_submit(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
{
/* Add the new request to the request queue for the OUT endpoint */
privep->txnullpkt = 0;
privep->txnullpkt = false;
sam_req_enqueue(privep, privreq);
usbtrace(TRACE_OUTREQQUEUED(epno), req->len);