SAMA5 UDPHS: More zero length packet fixes; revamped request queue structures

This commit is contained in:
Gregory Nutt 2013-09-04 09:14:30 -06:00
parent 64f1c78367
commit a478b680a3

View File

@ -135,8 +135,8 @@
/* Request queue operations *************************************************/ /* Request queue operations *************************************************/
#define sam_rqempty(ep) ((ep)->head == NULL) #define sam_rqempty(q) ((q)->head == NULL)
#define sam_rqpeek(ep) ((ep)->head) #define sam_rqpeek(q) ((q)->head)
/* USB trace ****************************************************************/ /* USB trace ****************************************************************/
/* Trace error codes */ /* Trace error codes */
@ -302,6 +302,14 @@ struct sam_req_s
uint16_t inflight; /* Number of TX bytes written to FIFO */ uint16_t inflight; /* Number of TX bytes written to FIFO */
}; };
/* The head of a queue of requests */
struct sam_rqhead_s
{
struct sam_req_s *head; /* Requests are added to the head of the list */
struct sam_req_s *tail; /* Requests are removed from the tail of the list */
};
/* This is the internal representation of an endpoint */ /* This is the internal representation of an endpoint */
struct sam_ep_s struct sam_ep_s
@ -316,8 +324,7 @@ struct sam_ep_s
/* SAMA5-specific fields */ /* SAMA5-specific fields */
struct sam_usbdev_s *dev; /* Reference to private driver data */ struct sam_usbdev_s *dev; /* Reference to private driver data */
struct sam_req_s *head; /* Request list for this endpoint */ struct sam_rqhead_s reqq; /* Read/write request queue */
struct sam_req_s *tail;
#ifdef CONFIG_SAMA5_UDPHS_SCATTERGATHER #ifdef CONFIG_SAMA5_UDPHS_SCATTERGATHER
struct sam_dtd_s *dtdll; /* Head of the DMA transfer descriptor list */ struct sam_dtd_s *dtdll; /* Head of the DMA transfer descriptor list */
#endif #endif
@ -325,7 +332,8 @@ struct sam_ep_s
volatile uint8_t bank; /* Current reception bank (0 or 1) */ volatile uint8_t bank; /* Current reception bank (0 or 1) */
uint8_t stalled:1; /* true: Endpoint is stalled */ uint8_t stalled:1; /* true: Endpoint is stalled */
uint8_t halted:1; /* true: Endpoint feature halted */ uint8_t halted:1; /* true: Endpoint feature halted */
uint8_t txnullpkt:1; /* Null packet needed at end of transfer */ uint8_t zlpneeded:1; /* Zero length packet needed at end of transfer */
uint8_t zlpsent:1; /* Zero length packet has been sent */
}; };
struct sam_usbdev_s struct sam_usbdev_s
@ -414,8 +422,8 @@ static int sam_req_rddma(struct sam_usbdev_s *priv,
/* Request Helpers **********************************************************/ /* Request Helpers **********************************************************/
static struct sam_req_s * static struct sam_req_s *
sam_req_dequeue(struct sam_ep_s *privep); sam_req_dequeue(struct sam_rqhead_s *queue);
static void sam_req_enqueue(struct sam_ep_s *privep, static void sam_req_enqueue(struct sam_rqhead_s *queue,
struct sam_req_s *req); struct sam_req_s *req);
static inline void static inline void
sam_req_abort(struct sam_ep_s *privep, sam_req_abort(struct sam_ep_s *privep,
@ -431,7 +439,7 @@ static int sam_req_rdnodma(struct sam_usbdev_s *priv,
uint16_t pktsize); uint16_t pktsize);
static int sam_req_read(struct sam_usbdev_s *priv, static int sam_req_read(struct sam_usbdev_s *priv,
struct sam_ep_s *privep, uint16_t pktsize); struct sam_ep_s *privep, uint16_t pktsize);
static void sam_req_cancel(struct sam_ep_s *privep); static void sam_req_cancel(struct sam_ep_s *privep, int16_t status);
/* Interrupt level processing ***********************************************/ /* Interrupt level processing ***********************************************/
@ -955,7 +963,7 @@ static int sam_req_wrdma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
remaining = privreq->req.len - privreq->req.xfrd - privreq->inflight; remaining = privreq->req.len - privreq->req.xfrd - privreq->inflight;
/* If there are no bytes to send, then send a null packet */ /* If there are no bytes to send, then send a zero length packet */
if (remaining > 0) if (remaining > 0)
{ {
@ -1018,7 +1026,8 @@ 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 = false; privep->zlpneeded = false;
privep->zlpsent = false;
privreq->inflight = 0; privreq->inflight = 0;
privreq->req.xfrd = 0; privreq->req.xfrd = 0;
@ -1065,16 +1074,16 @@ static int sam_req_rddma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
* Name: sam_req_dequeue * Name: sam_req_dequeue
****************************************************************************/ ****************************************************************************/
static struct sam_req_s *sam_req_dequeue(struct sam_ep_s *privep) static struct sam_req_s *sam_req_dequeue(struct sam_rqhead_s *queue)
{ {
struct sam_req_s *ret = privep->head; struct sam_req_s *ret = queue->head;
if (ret) if (ret)
{ {
privep->head = ret->flink; queue->head = ret->flink;
if (!privep->head) if (!queue->head)
{ {
privep->tail = NULL; queue->tail = NULL;
} }
ret->flink = NULL; ret->flink = NULL;
@ -1087,18 +1096,18 @@ static struct sam_req_s *sam_req_dequeue(struct sam_ep_s *privep)
* Name: sam_req_enqueue * Name: sam_req_enqueue
****************************************************************************/ ****************************************************************************/
static void sam_req_enqueue(struct sam_ep_s *privep, struct sam_req_s *req) static void sam_req_enqueue(struct sam_rqhead_s *queue, struct sam_req_s *req)
{ {
req->flink = NULL; req->flink = NULL;
if (!privep->head) if (!queue->head)
{ {
privep->head = req; queue->head = req;
privep->tail = req; queue->tail = req;
} }
else else
{ {
privep->tail->flink = req; queue->tail->flink = req;
privep->tail = req; queue->tail = req;
} }
} }
@ -1109,7 +1118,8 @@ static void sam_req_enqueue(struct sam_ep_s *privep, struct sam_req_s *req)
static inline void static inline void
sam_req_abort(struct sam_ep_s *privep, struct sam_req_s *privreq, int16_t result) sam_req_abort(struct sam_ep_s *privep, struct sam_req_s *privreq, int16_t result)
{ {
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_REQABORTED), (uint16_t)USB_EPNO(privep->ep.eplog)); usbtrace(TRACE_DEVERROR(SAM_TRACEERR_REQABORTED),
(uint16_t)USB_EPNO(privep->ep.eplog));
/* Save the result in the request structure */ /* Save the result in the request structure */
@ -1132,7 +1142,7 @@ static void sam_req_complete(struct sam_ep_s *privep, int16_t result)
/* Remove the completed request at the head of the endpoint request list */ /* Remove the completed request at the head of the endpoint request list */
flags = irqsave(); flags = irqsave();
privreq = sam_req_dequeue(privep); privreq = sam_req_dequeue(&privep->reqq);
irqrestore(flags); irqrestore(flags);
if (privreq) if (privreq)
@ -1149,7 +1159,8 @@ 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 = false; privep->zlpneeded = false;
privep->zlpsent = false;
} }
} }
@ -1228,8 +1239,8 @@ static int sam_req_wrnodma(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
nbytes = bytesleft; nbytes = bytesleft;
} }
/* If we are not sending a NULL packet, then clip the size to maxpacket /* If we are not sending a zero length packet, then clip the size to
* and check if we need to send a following NULL packet. * maxpacket and check if we need to send a following zero length packet.
*/ */
nbytes = bytesleft; nbytes = bytesleft;
@ -1313,7 +1324,7 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
{ {
/* Check the request from the head of the endpoint request queue */ /* Check the request from the head of the endpoint request queue */
privreq = sam_rqpeek(privep); privreq = sam_rqpeek(&privep->reqq);
if (!privreq) if (!privreq)
{ {
/* There is no TX transfer in progress and no new pending TX /* There is no TX transfer in progress and no new pending TX
@ -1342,17 +1353,14 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
return -ENOENT; return -ENOENT;
} }
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 zlpneeded=%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->zlpneeded);
/* Were there bytes in flight? */ /* Handle any bytes in flight. */
if (privreq->inflight) privreq->req.xfrd += privreq->inflight;
{ privreq->inflight = 0;
privreq->req.xfrd += privreq->inflight;
privreq->inflight = 0;
}
/* Get the number of bytes left to be sent in the packet */ /* Get the number of bytes left to be sent in the packet */
@ -1366,17 +1374,17 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
if (bytesleft == privep->ep.maxpacket && if (bytesleft == privep->ep.maxpacket &&
(privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0) (privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0)
{ {
/* Next time we get here, bytesleft will be zero and txnullpkt /* Next time we get here, bytesleft will be zero and zlpneeded
* will be set. * will be set.
*/ */
privep->txnullpkt = true; privep->zlpneeded = true;
} }
else else
{ {
/* No zero packet is forthcoming (maybe later) */ /* No zero packet is forthcoming (maybe later) */
privep->txnullpkt = false; privep->zlpneeded = 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
@ -1401,26 +1409,27 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
} }
/* No data to send... This can happen on one of two ways: /* No data to send... This can happen on one of two ways:
* (1) The last packet sent was the final packet of a transfer * (1) The last packet sent was the final packet of a transfer.
* and was exactly maxpacketsize and the protocol expects * If it was also exactly maxpacketsize and the protocol expects
* a zero length packet to follow (privep->txnullpkt will be * a zero length packet to follow then privep->zlpneeded will be
* set). Or (2) we called with a request packet that has * set. Or (2) we called with a request packet that has
* len == 0 (privep->txnullpkt will not be set). Either case * len == 0 (privep->zlpneeded will not be set). Either case
* means that it is time to send a NULL packet and complete * means that it is time to send a zero length packet and complete
* this transfer. * this transfer.
*/ */
else if (privreq->req.len == 0 || privep->txnullpkt) else if ((privreq->req.len == 0 || privep->zlpneeded) && !privep->zlpsent)
{ {
/* 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.
* *
* A Zero Length Packet can be sent by setting just the TXRDY flag * A Zero Length Packet can be sent by setting just the TXRDY flag
* in* the UDPHS_EPTSETSTAx register * in the UDPHS_EPTSETSTAx register
*/ */
privep->epstate = UDPHS_EPSTATE_SENDING; privep->epstate = UDPHS_EPSTATE_SENDING;
privep->txnullpkt = false; privep->zlpneeded = false;
privep->zlpsent = true;
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
@ -1430,9 +1439,9 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
sam_ep_txrdy(epno); sam_ep_txrdy(epno);
} }
/* If all of the bytes were sent (including any final null packet) /* If all of the bytes were sent (including any final zero length
* then we are finished with the request buffer), then we can return * packet) then we are finished with the request buffer), then we can
* the request buffer to the class driver. The transfer is not * return the request buffer to the class driver. The transfer is not
* finished yet, however. There are still bytes in flight. The * finished yet, however. There are still bytes in flight. The
* transfer is truly finished when we are called again and the * transfer is truly finished when we are called again and the
* request buffer is empty. * request buffer is empty.
@ -1446,7 +1455,6 @@ 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);
privep->txnullpkt = false;
sam_req_complete(privep, OK); sam_req_complete(privep, OK);
} }
} }
@ -1527,7 +1535,7 @@ static int sam_req_read(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
/* Check the request from the head of the endpoint request queue */ /* Check the request from the head of the endpoint request queue */
epno = USB_EPNO(privep->ep.eplog); epno = USB_EPNO(privep->ep.eplog);
privreq = sam_rqpeek(privep); privreq = sam_rqpeek(&privep->reqq);
if (!privreq) if (!privreq)
{ {
/* Incoming data available in the FIFO, but no packet to receive the data. /* Incoming data available in the FIFO, but no packet to receive the data.
@ -1608,25 +1616,28 @@ static int sam_req_read(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
* Name: sam_req_cancel * Name: sam_req_cancel
****************************************************************************/ ****************************************************************************/
static void sam_req_cancel(struct sam_ep_s *privep) static void sam_req_cancel(struct sam_ep_s *privep, int16_t result)
{ {
uint32_t regval; uint32_t regval;
uint8_t epno; uint8_t epno;
/* Disable endpoint interrupts */ /* Disable endpoint interrupts if not endpoint 0 */
epno = USB_EPNO(privep->ep.eplog); epno = USB_EPNO(privep->ep.eplog);
regval = sam_getreg(SAM_UDPHS_IEN); if (epno != 0)
regval &= ~UDPHS_INT_DMA(epno); {
sam_putreg(regval, SAM_UDPHS_IEN); regval = sam_getreg(SAM_UDPHS_IEN);
regval &= ~UDPHS_INT_DMA(epno);
sam_putreg(regval, SAM_UDPHS_IEN);
}
/* Then complete every queued request with -ESHUTDOWN status */ /* Then complete every queued request with the specified status */
while (!sam_rqempty(privep)) while (!sam_rqempty(&privep->reqq))
{ {
usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)),
(sam_rqpeek(privep))->req.xfrd); (sam_rqpeek(&privep->reqq))->req.xfrd);
sam_req_complete(privep, -ESHUTDOWN); sam_req_complete(privep, result);
} }
} }
@ -1767,8 +1778,7 @@ static void sam_setdevaddr(struct sam_usbdev_s *priv, uint8_t address)
static void sam_ep0_setup(struct sam_usbdev_s *priv) static void sam_ep0_setup(struct sam_usbdev_s *priv)
{ {
struct sam_ep_s *ep0 = &priv->eplist[EP0]; struct sam_ep_s *ep0 = &priv->eplist[EP0];
struct sam_req_s *privreq = sam_rqpeek(ep0);
struct sam_ep_s *privep; struct sam_ep_s *privep;
union wb_u value; union wb_u value;
union wb_u index; union wb_u index;
@ -1779,21 +1789,9 @@ static void sam_ep0_setup(struct sam_usbdev_s *priv)
int nbytes = 0; /* Assume zero-length packet */ int nbytes = 0; /* Assume zero-length packet */
int ret; int ret;
/* Terminate any pending requests (doesn't work if the pending request /* Terminate any pending requests */
* was a zero-length transfer!)
*/
while (!sam_rqempty(ep0)) sam_req_cancel(ep0, -EPROTO);
{
int16_t result = OK;
if (privreq->req.xfrd != privreq->req.len)
{
result = -EPROTO;
}
usbtrace(TRACE_COMPLETE(ep0->ep.eplog), privreq->req.xfrd);
sam_req_complete(ep0, result);
}
/* Assume NOT stalled; no TX in progress */ /* Assume NOT stalled; no TX in progress */
@ -2265,7 +2263,7 @@ static void sam_dma_interrupt(struct sam_usbdev_s *priv, int epno)
/* Get the request from the head of the endpoint request queue */ /* Get the request from the head of the endpoint request queue */
privreq = sam_rqpeek(privep); privreq = sam_rqpeek(&privep->reqq);
DEBUGASSERT(privreq); DEBUGASSERT(privreq);
/* Invalidate the data cache for region that just completed DMA. /* Invalidate the data cache for region that just completed DMA.
@ -2580,7 +2578,7 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
if (privep->epstate == UDPHS_EPSTATE_RECEIVING || if (privep->epstate == UDPHS_EPSTATE_RECEIVING ||
privep->epstate == UDPHS_EPSTATE_SENDING) privep->epstate == UDPHS_EPSTATE_SENDING)
{ {
sam_req_complete(privep, OK); sam_req_complete(privep, -EPROTO);
} }
/* ISO Err Flow */ /* ISO Err Flow */
@ -2938,7 +2936,7 @@ static void sam_ep_reset(struct sam_usbdev_s *priv, uint8_t epno)
* for each of its configured endpoints. * for each of its configured endpoints.
*/ */
sam_req_cancel(privep); sam_req_cancel(privep, -ESHUTDOWN);
/* Reset endpoint */ /* Reset endpoint */
@ -2949,7 +2947,8 @@ static void sam_ep_reset(struct sam_usbdev_s *priv, uint8_t epno)
privep->epstate = UDPHS_EPSTATE_DISABLED; privep->epstate = UDPHS_EPSTATE_DISABLED;
privep->stalled = false; privep->stalled = false;
privep->halted = false; privep->halted = false;
privep->txnullpkt = false; privep->zlpneeded = false;
privep->zlpsent = false;
privep->bank = 0; privep->bank = 0;
} }
@ -3289,14 +3288,10 @@ static int sam_ep_disable(struct usbdev_ep_s *ep)
epno = USB_EPNO(ep->eplog); epno = USB_EPNO(ep->eplog);
usbtrace(TRACE_EPDISABLE, epno); usbtrace(TRACE_EPDISABLE, epno);
/* Cancel any ongoing activity */ /* Reset the endpoint and cancel any ongoing activity */
flags = irqsave(); flags = irqsave();
sam_req_cancel(privep); priv = privep->dev;
/* Reset the endpoint */
priv = privep->dev;
sam_ep_reset(priv, epno); sam_ep_reset(priv, epno);
/* Revert to the addressed-but-not-configured state */ /* Revert to the addressed-but-not-configured state */
@ -3440,6 +3435,8 @@ static int sam_ep_submit(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
req->result = -EINPROGRESS; req->result = -EINPROGRESS;
req->xfrd = 0; req->xfrd = 0;
privreq->inflight = 0; privreq->inflight = 0;
privep->zlpneeded = false;
privep->zlpsent = false;
flags = irqsave(); flags = irqsave();
/* If we are stalled, then drop all requests on the floor */ /* If we are stalled, then drop all requests on the floor */
@ -3460,7 +3457,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 IN endpoint */ /* Add the new request to the request queue for the IN endpoint */
sam_req_enqueue(privep, privreq); sam_req_enqueue(&privep->reqq, privreq);
usbtrace(TRACE_INREQQUEUED(epno), req->len); usbtrace(TRACE_INREQQUEUED(epno), req->len);
/* If the IN endpoint FIFO is available, then transfer the data now */ /* If the IN endpoint FIFO is available, then transfer the data now */
@ -3477,8 +3474,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 = false; sam_req_enqueue(&privep->reqq, privreq);
sam_req_enqueue(privep, privreq);
usbtrace(TRACE_OUTREQQUEUED(epno), req->len); usbtrace(TRACE_OUTREQQUEUED(epno), req->len);
/* This there a incoming data pending the availability of a request? */ /* This there a incoming data pending the availability of a request? */
@ -3517,7 +3513,7 @@ static int sam_ep_cancel(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
usbtrace(TRACE_EPCANCEL, USB_EPNO(ep->eplog)); usbtrace(TRACE_EPCANCEL, USB_EPNO(ep->eplog));
flags = irqsave(); flags = irqsave();
sam_req_cancel(privep); sam_req_cancel(privep, -EAGAIN);
irqrestore(flags); irqrestore(flags);
return OK; return OK;
} }
@ -3604,7 +3600,11 @@ static int sam_ep_stall(struct usbdev_ep_s *ep, bool resume)
/* Abort the current transfer if necessary */ /* Abort the current transfer if necessary */
sam_req_complete(privep, -EIO); if (privep->epstate == UDPHS_EPSTATE_SENDING ||
privep->epstate == UDPHS_EPSTATE_RECEIVING)
{
sam_req_complete(privep, -EIO);
}
/* Put endpoint into stalled state */ /* Put endpoint into stalled state */
@ -3956,13 +3956,14 @@ static void sam_reset(struct sam_usbdev_s *priv)
* for each of its configured endpoints. * for each of its configured endpoints.
*/ */
sam_req_cancel(privep); sam_req_cancel(privep, -ESHUTDOWN);
/* Reset endpoint status */ /* Reset endpoint status */
privep->stalled = false; privep->stalled = false;
privep->halted = false; privep->halted = false;
privep->txnullpkt = false; privep->zlpneeded = false;
privep->zlpsent = false;
} }
/* Re-configure the USB controller in its initial, unconnected state */ /* Re-configure the USB controller in its initial, unconnected state */