SAMA5 UDPHS: Fixes related to null packet and SETUP OUT data handling
This commit is contained in:
parent
3e505efe73
commit
c09b08f4a5
@ -157,17 +157,18 @@
|
|||||||
#define SAM_TRACEERR_DISPATCHSTALL 0x000e
|
#define SAM_TRACEERR_DISPATCHSTALL 0x000e
|
||||||
#define SAM_TRACEERR_DRIVER 0x000f
|
#define SAM_TRACEERR_DRIVER 0x000f
|
||||||
#define SAM_TRACEERR_DRIVERREGISTERED 0x0010
|
#define SAM_TRACEERR_DRIVERREGISTERED 0x0010
|
||||||
#define SAM_TRACEERR_EP0SETUPSTALLED 0x0011
|
#define SAM_TRACEERR_EP0SETUPOUTSIZE 0x0011
|
||||||
#define SAM_TRACEERR_EPINBUSY 0x0012
|
#define SAM_TRACEERR_EP0SETUPSTALLED 0x0012
|
||||||
#define SAM_TRACEERR_EPOUTNULLPACKET 0x0013
|
#define SAM_TRACEERR_EPINBUSY 0x0013
|
||||||
#define SAM_TRACEERR_EPRESERVE 0x0014
|
#define SAM_TRACEERR_EPOUTNULLPACKET 0x0014
|
||||||
#define SAM_TRACEERR_EPTCFGMAPD 0x0015
|
#define SAM_TRACEERR_EPRESERVE 0x0015
|
||||||
#define SAM_TRACEERR_INVALIDCTRLREQ 0x0016
|
#define SAM_TRACEERR_EPTCFGMAPD 0x0016
|
||||||
#define SAM_TRACEERR_INVALIDPARMS 0x0017
|
#define SAM_TRACEERR_INVALIDCTRLREQ 0x0017
|
||||||
#define SAM_TRACEERR_IRQREGISTRATION 0x0018
|
#define SAM_TRACEERR_INVALIDPARMS 0x0018
|
||||||
#define SAM_TRACEERR_NOTCONFIGURED 0x0019
|
#define SAM_TRACEERR_IRQREGISTRATION 0x0019
|
||||||
#define SAM_TRACEERR_REQABORTED 0x001a
|
#define SAM_TRACEERR_NOTCONFIGURED 0x001a
|
||||||
#define SAM_TRACEERR_TXRDYERR 0x001b
|
#define SAM_TRACEERR_REQABORTED 0x001b
|
||||||
|
#define SAM_TRACEERR_TXRDYERR 0x001c
|
||||||
|
|
||||||
/* Trace interrupt codes */
|
/* 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_DISPATCHSTALL),
|
||||||
TRACE_STR(SAM_TRACEERR_DRIVER),
|
TRACE_STR(SAM_TRACEERR_DRIVER),
|
||||||
TRACE_STR(SAM_TRACEERR_DRIVERREGISTERED),
|
TRACE_STR(SAM_TRACEERR_DRIVERREGISTERED),
|
||||||
|
TRACE_STR(SAM_TRACEERR_EP0SETUPOUTSIZE),
|
||||||
TRACE_STR(SAM_TRACEERR_EP0SETUPSTALLED),
|
TRACE_STR(SAM_TRACEERR_EP0SETUPSTALLED),
|
||||||
TRACE_STR(SAM_TRACEERR_EPINBUSY),
|
TRACE_STR(SAM_TRACEERR_EPINBUSY),
|
||||||
TRACE_STR(SAM_TRACEERR_EPOUTNULLPACKET),
|
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 */
|
/* Switch to the receiving state */
|
||||||
|
|
||||||
privep->epstate = UDPHS_EPSTATE_RECEIVING;
|
privep->epstate = UDPHS_EPSTATE_RECEIVING;
|
||||||
privep->txnullpkt = 0;
|
privep->txnullpkt = false;
|
||||||
privreq->inflight = 0;
|
privreq->inflight = 0;
|
||||||
privreq->req.xfrd = 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 */
|
/* Reset the endpoint state and restore the stalled indication */
|
||||||
|
|
||||||
privep->epstate = UDPHS_EPSTATE_IDLE;
|
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;
|
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
|
/* 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.
|
* 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)
|
if (nbytes >= privep->ep.maxpacket)
|
||||||
{
|
{
|
||||||
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" */
|
/* 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 bytesleft;
|
||||||
int ret;
|
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
|
/* We get here when an IN endpoint interrupt occurs. So now we know that
|
||||||
* there is no TX transfer in progress.
|
* 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);
|
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;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
epno = USB_EPNO(privep->ep.eplog);
|
|
||||||
ullvdbg("epno=%d req=%p: len=%d xfrd=%d inflight=%d nullpkt=%d\n",
|
ullvdbg("epno=%d req=%p: len=%d xfrd=%d inflight=%d nullpkt=%d\n",
|
||||||
epno, privreq, privreq->req.len, privreq->req.xfrd,
|
epno, privreq, privreq->req.len, privreq->req.xfrd,
|
||||||
privreq->inflight, privep->txnullpkt);
|
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.
|
* will be set.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
privep->txnullpkt = 1;
|
privep->txnullpkt = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* No zero packet is forthcoming (maybe later) */
|
/* 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
|
/* 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.
|
* 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
|
/* 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.
|
* 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->epstate = UDPHS_EPSTATE_SENDING;
|
||||||
privep->txnullpkt = 0;
|
privep->txnullpkt = false;
|
||||||
privreq->inflight = 0;
|
privreq->inflight = 0;
|
||||||
|
|
||||||
/* Initiate the zero length transfer and configure to receive the
|
/* 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)
|
/* 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 &&
|
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)),
|
usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)),
|
||||||
privreq->req.xfrd);
|
privreq->req.xfrd);
|
||||||
|
|
||||||
/* Get the endpoint type */
|
privep->txnullpkt = false;
|
||||||
|
|
||||||
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;
|
|
||||||
sam_req_complete(privep, OK);
|
sam_req_complete(privep, OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1729,7 +1721,7 @@ static void sam_ep0_dispatch(struct sam_usbdev_s *priv)
|
|||||||
/* Stall on failure */
|
/* Stall on failure */
|
||||||
|
|
||||||
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_DISPATCHSTALL), 0);
|
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),
|
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EP0SETUPSTALLED),
|
||||||
priv->ctrl.req);
|
priv->ctrl.req);
|
||||||
|
|
||||||
(void)sam_ep_stall(&priv->eplist[EP0].ep, true);
|
(void)sam_ep_stall(&priv->eplist[EP0].ep, false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -2445,6 +2437,7 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
|
|||||||
|
|
||||||
/* Set the device address */
|
/* Set the device address */
|
||||||
|
|
||||||
|
privep->epstate = UDPHS_EPSTATE_IDLE;
|
||||||
sam_setdevaddr(priv, priv->devaddr);
|
sam_setdevaddr(priv, priv->devaddr);
|
||||||
|
|
||||||
/* Disable the further TXRDY interrupts on EP0. */
|
/* Disable the further TXRDY interrupts on EP0. */
|
||||||
@ -2487,25 +2480,35 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
|
|||||||
{
|
{
|
||||||
uint16_t len;
|
uint16_t len;
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG
|
/* Yes.. back to the IDLE state */
|
||||||
/* Yes.. get the size of the packet that we just received */
|
|
||||||
|
privep->epstate = UDPHS_EPSTATE_IDLE;
|
||||||
|
|
||||||
|
/* Get the size of the packet that we just received */
|
||||||
|
|
||||||
pktsize = (uint16_t)
|
pktsize = (uint16_t)
|
||||||
((eptsta & UDPHS_EPTSTA_BYTECNT_MASK) >>
|
((eptsta & UDPHS_EPTSTA_BYTECNT_MASK) >>
|
||||||
UDPHS_EPTSTA_BYTECNT_SHIFT);
|
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);
|
len = GETUINT16(priv->ctrl.len);
|
||||||
DEBUGASSERT(len > 0 && len == pktsize);
|
if (len == pktsize)
|
||||||
|
{
|
||||||
|
/* Copy the OUT data from the EP0 FIFO into special EP0 buffer. */
|
||||||
|
|
||||||
sam_ep0_read(priv->ep0out, len);
|
sam_ep0_read(priv->ep0out, len);
|
||||||
|
|
||||||
/* And handle the EP0 SETUP now. */
|
/* And handle the EP0 SETUP now. */
|
||||||
|
|
||||||
privep->epstate = UDPHS_EPSTATE_IDLE;
|
|
||||||
sam_ep0_setup(priv);
|
sam_ep0_setup(priv);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EP0SETUPOUTSIZE), pktsize);
|
||||||
|
(void)sam_ep_stall(&privep->ep, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Check if ACK received on a Control EP */
|
/* Check if ACK received on a Control EP */
|
||||||
@ -2605,11 +2608,11 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
|
|||||||
len = GETUINT16(priv->ctrl.len);
|
len = GETUINT16(priv->ctrl.len);
|
||||||
if (USB_REQ_ISOUT(priv->ctrl.type) && len > 0)
|
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.
|
* 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;
|
privep->epstate = UDPHS_EPSTATE_EP0DATAOUT;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2618,7 +2621,7 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
|
|||||||
* Handle the EP0 SETUP now.
|
* 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;
|
privep->epstate = UDPHS_EPSTATE_IDLE;
|
||||||
sam_ep0_setup(priv);
|
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 */
|
/* Add the new request to the request queue for the OUT endpoint */
|
||||||
|
|
||||||
privep->txnullpkt = 0;
|
privep->txnullpkt = false;
|
||||||
sam_req_enqueue(privep, privreq);
|
sam_req_enqueue(privep, privreq);
|
||||||
usbtrace(TRACE_OUTREQQUEUED(epno), req->len);
|
usbtrace(TRACE_OUTREQQUEUED(epno), req->len);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user