Fix request queue management problems

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@974 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2008-10-03 17:32:12 +00:00
parent 75c2fb5635
commit f6838c4e6f
1 changed files with 179 additions and 89 deletions

View File

@ -43,7 +43,6 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <queue.h>
#include <debug.h>
#include <nuttx/arch.h>
@ -119,11 +118,6 @@
/* Debug ***********************************************************************/
#ifndef CONFIG_USBDEV_TRACE
# undef usbtrace
# define usbtrace(a,b) ulldbg("%04x:%04x\n", a, b)
#endif
/* Trace error codes */
#define LPC214X_TRACEERR_ALLOCFAIL 0x0001
@ -182,11 +176,12 @@
#define LPC214X_TRACEINTID_GETSETIF 0x0017
#define LPC214X_TRACEINTID_GETSTATUS 0x0018
#define LPC214X_TRACEINTID_IFGETSTATUS 0x0019
#define LPC214X_TRACEINTID_SETADDRESS 0x001a
#define LPC214X_TRACEINTID_SETCONFIG 0x001b
#define LPC214X_TRACEINTID_SETFEATURE 0x001c
#define LPC214X_TRACEINTID_SUSPENDCHG 0x001d
#define LPC214X_TRACEINTID_SYNCHFRAME 0x001e
#define LPC214X_TRACEERR_REQABORTED 0x001a
#define LPC214X_TRACEINTID_SETADDRESS 0x001b
#define LPC214X_TRACEINTID_SETCONFIG 0x001c
#define LPC214X_TRACEINTID_SETFEATURE 0x001d
#define LPC214X_TRACEINTID_SUSPENDCHG 0x001e
#define LPC214X_TRACEINTID_SYNCHFRAME 0x001f
/* Hardware interface **********************************************************/
@ -272,6 +267,11 @@
#define LPC214X_EP0SHORTWRSENT (4) /* Short data write complete */
#define LPC214X_EP0SETADDRESS (5) /* Set address received */
/* Request queue operations ****************************************************/
#define lpc214x_rqempty(ep) ((ep)->head == NULL)
#define lpc214x_rqpeek(ep) ((ep)->head)
/*******************************************************************************
* Private Types
*******************************************************************************/
@ -281,7 +281,7 @@
struct lpc214x_req_s
{
struct usbdev_req_s req; /* Standard USB request */
sq_entry_t sqe; /* Supports a singly linked list */
struct lpc214x_req_s *flink; /* Supports a singly linked list */
};
/* This is the internal representation of an endpoint */
@ -298,7 +298,8 @@ struct lpc214x_ep_s
/* LPC214X-specific fields */
struct lpc214x_usbdev_s *dev; /* Reference to private driver data */
sq_queue_t reqlist; /* Request list for this endpoint */
struct lpc214x_req_s *head; /* Request list for this endpoint */
struct lpc214x_req_s *tail;
ubyte eplog; /* Logical EP address from descriptor */
ubyte epphy; /* Physical EP address */
ubyte stalled:1; /* Endpoint is halted */
@ -367,7 +368,7 @@ struct lpc214x_usbdev_s
* Private Function Prototypes
*******************************************************************************/
/* Register operations */
/* Register operations ********************************************************/
#if defined(CONFIG_LPC214X_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint32 lpc214x_getreg(uint32 addr);
@ -377,22 +378,31 @@ static void lpc214x_putreg(uint32 val, uint32 addr);
# define lpc214x_putreg(val,addr) putreg32(val,addr)
#endif
/* Command operations */
/* Command operations **********************************************************/
static uint32 lpc214x_usbcmd(uint16 cmd, ubyte data);
/* Low level data transfers and request operations */
/* Request queue operations ****************************************************/
static FAR struct lpc214x_req_s *lpc214x_rqdequeue(FAR struct lpc214x_ep_s *privep);
static void lpc214x_rqenqueue(FAR struct lpc214x_ep_s *privep,
FAR struct lpc214x_req_s *req);
/* Low level data transfers and request operations *****************************/
static void lpc214x_epwrite(ubyte epphy, const ubyte *data, uint32 nbytes);
static int lpc214x_epread(ubyte epphy, ubyte *data, uint32 nbytes);
static inline void lpc214x_abortrequest(struct lpc214x_ep_s *privep,
struct lpc214x_req_s *privreq, sint16 result);
static void lpc214x_reqcomplete(struct lpc214x_ep_s *privep, sint16 result);
static int lpc214x_wrrequest(struct lpc214x_ep_s *privep);
static int lpc214x_rdrequest(struct lpc214x_ep_s *privep);
static void lpc214x_cancelrequests(struct lpc214x_ep_s *privep);
/* Interrupt handling */
/* Interrupt handling **********************************************************/
static void lpc214x_eprealize(struct lpc214x_ep_s *privep, boolean prio, uint32 packetsize);
static void lpc214x_eprealize(struct lpc214x_ep_s *privep, boolean prio,
uint32 packetsize);
static ubyte lpc214x_epclrinterrupt(ubyte epphy);
static inline void lpc214x_ep0configure(struct lpc214x_usbdev_s *priv);
#ifdef CONFIG_LPC214X_USBDEV_DMA
@ -414,7 +424,7 @@ static void lpc214x_dmarestart(ubyte epphy, uint32 descndx);
static void lpc214x_dmadisable(ubyte epphy);
#endif /* CONFIG_LPC214X_USBDEV_DMA */
/* Endpoint operations */
/* Endpoint operations *********************************************************/
static int lpc214x_epconfigure(FAR struct usbdev_ep_s *ep,
const struct usb_epdesc_s *desc);
@ -433,7 +443,7 @@ static int lpc214x_epcancel(FAR struct usbdev_ep_s *ep,
struct usbdev_req_s *req);
static int lpc214x_epstall(FAR struct usbdev_ep_s *ep, boolean resume);
/* USB device controller operations */
/* USB device controller operations ********************************************/
static FAR struct usbdev_ep_s *lcp214x_allocep(FAR struct usbdev_s *dev,
ubyte epno, boolean in, ubyte eptype);
@ -704,6 +714,56 @@ static uint32 lpc214x_usbcmd(uint16 cmd, ubyte data)
return tmp;
}
/*******************************************************************************
* Name: lpc214x_rqdequeue
*
* Description:
* Remove a request from an endpoint request queue
*
*******************************************************************************/
static FAR struct lpc214x_req_s *lpc214x_rqdequeue(FAR struct lpc214x_ep_s *privep)
{
FAR struct lpc214x_req_s *ret = privep->head;
if (ret)
{
privep->head = ret->flink;
if (!privep->head)
{
privep->tail = NULL;
}
ret->flink = NULL;
}
return ret;
}
/*******************************************************************************
* Name: lpc214x_rqenqueue
*
* Description:
* Add a request from an endpoint request queue
*
*******************************************************************************/
static void lpc214x_rqenqueue(FAR struct lpc214x_ep_s *privep,
FAR struct lpc214x_req_s *req)
{
req->flink = NULL;
if (!privep->head)
{
privep->head = req;
privep->tail = req;
}
else
{
privep->tail->flink = req;
privep->tail = req;
}
}
/*******************************************************************************
* Name: lpc214x_epwrite
*
@ -847,11 +907,34 @@ static int lpc214x_epread(ubyte epphy, ubyte *data, uint32 nbytes)
return pktlen;
}
/*******************************************************************************
* Name: lpc214x_abortrequest
*
* Description:
* Discard a request
*
*******************************************************************************/
static inline void lpc214x_abortrequest(struct lpc214x_ep_s *privep,
struct lpc214x_req_s *privreq,
sint16 result)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_REQABORTED), (uint16)privep->epphy);
/* Save the result in the request structure */
privreq->req.result = result;
/* Callback to the request completion handler */
privreq->req.callback(&privep->ep, &privreq->req);
}
/*******************************************************************************
* Name: lpc214x_reqcomplete
*
* Description:
* Handle termination of a request.
* Handle termination of the request at the head of the endpoint request queue.
*
*******************************************************************************/
@ -864,7 +947,7 @@ static void lpc214x_reqcomplete(struct lpc214x_ep_s *privep, sint16 result)
/* Remove the complete request at the head of the endpoint request list */
flags = irqsave();
privreq = (struct lpc214x_req_s *)sq_remfirst(&privep->reqlist);
privreq = lpc214x_rqdequeue(privep);
irqrestore(flags);
if (privreq)
@ -873,12 +956,9 @@ static void lpc214x_reqcomplete(struct lpc214x_ep_s *privep, sint16 result)
* in the callback.
*/
if (privep->epphy == 0)
if (privep->epphy == LPC214X_EP0_IN)
{
if (privep->dev->stalled)
{
privep->stalled = 1;
}
privep->stalled = privep->dev->stalled;
}
/* Save the result in the request structure */
@ -887,7 +967,7 @@ static void lpc214x_reqcomplete(struct lpc214x_ep_s *privep, sint16 result)
/* Callback to the request completion handler */
privreq->sqe.flink = NULL;
privreq->flink = NULL;
privreq->req.callback(&privep->ep, &privreq->req);
/* Restore the stalled indication */
@ -916,22 +996,38 @@ static int lpc214x_wrrequest(struct lpc214x_ep_s *privep)
/* Check the request from the head of the endpoint request queue */
privreq = (struct lpc214x_req_s *)sq_peek(&privep->reqlist);
privreq = lpc214x_rqpeek(privep);
if (!privreq)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_NULLREQUEST), 0);
return OK;
}
bytesleft = privreq->req.len;
/* Ignore any attempt to send a zero length packet */
if (privreq->req.len == 0)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_NULLPACKET), 0);
lpc214x_reqcomplete(privep, OK);
return OK;
}
/* Otherwise send the data in the packet (in the DMA on case, we
* may be resuming transfer already in progress.
*/
for (;;)
{
/* Get the number of bytes left to be sent in the packet */
bytesleft = privreq->req.len - privreq->req.xfrd;
/* Send the next packet if (1) there are more bytes to be sent, or
* (2) the last packet was exactly maxpacketsize.
* (2) the last packet sent was exactly maxpacketsize (bytesleft == 0)
*/
usbtrace(TRACE_WRITE(privep->epphy), privreq->req.xfrd);
if (privreq->req.len >= privreq->req.xfrd)
if (bytesleft >= 0)
{
/* Try to send maxpacketsize -- unless we don't have that many
* bytes to send.
@ -951,14 +1047,13 @@ static int lpc214x_wrrequest(struct lpc214x_ep_s *privep)
/* Update for the next time through the loop */
privreq->req.xfrd += nbytes;
bytesleft = privreq->req.len - privreq->req.xfrd;
}
/* If all of the bytes were sent (and the last packet was not full)
* then we are finished with the transfer)
/* If all of the bytes were sent (including any final null packet)
* then we are finished with the transfer
*/
if (privreq->req.len < privreq->req.xfrd)
if (bytesleft <= 0)
{
usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
lpc214x_reqcomplete(privep, OK);
@ -985,13 +1080,22 @@ static int lpc214x_rdrequest(struct lpc214x_ep_s *privep)
/* Check the request from the head of the endpoint request queue */
privreq = (struct lpc214x_req_s *)sq_peek(&privep->reqlist);
privreq = lpc214x_rqpeek(privep);
if (!privreq)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_NULLREQUEST), 0);
return OK;
}
/* Ignore any attempt to receive a zero length packet */
if (privreq->req.len == 0)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_NULLPACKET), 0);
lpc214x_reqcomplete(privep, OK);
return OK;
}
usbtrace(TRACE_READ(privep->epphy), privreq->req.xfrd);
for (;;)
{
@ -1033,10 +1137,10 @@ static int lpc214x_rdrequest(struct lpc214x_ep_s *privep)
static void lpc214x_cancelrequests(struct lpc214x_ep_s *privep)
{
while (!sq_empty(&privep->reqlist))
while (!lpc214x_rqempty(privep))
{
usbtrace(TRACE_COMPLETE(privep->epphy),
((struct lpc214x_req_s *)sq_peek(&privep->reqlist))->req.xfrd);
(lpc214x_rqpeek(privep))->req.xfrd);
lpc214x_reqcomplete(privep, -ESHUTDOWN);
}
}
@ -1292,7 +1396,7 @@ static void lpc214x_dispatchrequest(struct lpc214x_usbdev_s *priv,
static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv)
{
struct lpc214x_ep_s *ep0 = &priv->eplist[LPC214X_EP0_OUT];
struct lpc214x_req_s *privreq = (struct lpc214x_req_s *)sq_peek(&ep0->reqlist);
struct lpc214x_req_s *privreq = lpc214x_rqpeek(ep0);
struct usb_ctrlreq_s ctrl;
uint16 index;
ubyte epphy;
@ -1309,7 +1413,7 @@ static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv)
/* Terminate any pending requests */
while (!sq_empty(&ep0->reqlist))
while (!lpc214x_rqempty(ep0))
{
sint16 result = OK;
if (privreq->req.xfrd != privreq->req.len)
@ -1686,7 +1790,7 @@ static inline void lpc214x_ep0dataoutinterrupt(struct lpc214x_usbdev_s *priv)
case LPC214X_EP0IDLE:
{
ep0 = &priv->eplist[LPC214X_EP0_OUT];
if (!sq_empty(&ep0->reqlist))
if (!lpc214x_rqempty(ep0))
{
lpc214x_wrrequest(ep0);
}
@ -2010,7 +2114,7 @@ static int lpc214x_usbinterrupt(int irq, FAR void *context)
/* Write host data from the current write request */
if (!sq_empty(&privep->reqlist))
if (!lpc214x_rqempty(privep))
{
lpc214x_wrrequest(privep);
}
@ -2023,7 +2127,7 @@ static int lpc214x_usbinterrupt(int irq, FAR void *context)
/* Read host data into the current read request */
if (!sq_empty(&privep->reqlist))
if (!lpc214x_rqempty(privep))
{
lpc214x_rdrequest(privep);
}
@ -2475,66 +2579,52 @@ static int lpc214x_epsubmit(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s
return -ESHUTDOWN;
}
/* Handle the request from the class driver */
req->result = -EINPROGRESS;
req->xfrd = 0;
req->xfrd = 0;
flags = irqsave();
/* Check for NULL packet */
/* If we are not stalled, then drop all requests on the floor */
flags = irqsave();
if (req->len == 0 && (LPC214X_EPPHYIN(privep->epphy)))
if (privep->stalled)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_NULLPACKET), 0);
sq_addlast(&privreq->sqe, &privep->reqlist);
goto success_notransfer;
lpc214x_abortrequest(privep, privreq, -EBUSY);
ret = -EBUSY;
}
/* Has everything been sent? Or are we stalled? */
/* Handle IN (device-to-host) requests */
if (!sq_empty(&privep->reqlist) && !privep->stalled)
else if (LPC214X_EPPHYIN(privep->epphy))
{
/* Handle zero-length transfers on EP0 */
/* Add the new request to the request queue for the endpoint */
if (privep->epphy == 0 && req->len == 0)
{
/* Nothing to transfer -- exit success, zero-bytes transferred */
usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
lpc214x_reqcomplete(privep, OK);
goto success_notransfer;
}
/* Handle IN requests */
if (LPC214X_EPPHYIN(privep->epphy))
{
ret = lpc214x_wrrequest(privep);
}
/* Handle pending OUT requests */
else if (priv->rxpending)
{
ret = lpc214x_rdrequest(privep);
priv->rxpending = 0;
}
else
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADREQUEST), 0);
ret = ERROR;
goto errout;
}
lpc214x_rqenqueue(privep, privreq);
usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len);
ret = lpc214x_wrrequest(privep);
}
/* Add to endpoint's request queue */
/* Handle OUT (host-to-device) requests -- but only if one is expected*/
if (ret >= 0)
else if (priv->rxpending)
{
usbtrace(TRACE_REQQUEUED(privep->epphy), privreq->req.len);
sq_addlast(&privreq->sqe, &privep->reqlist);
/* Add the new request to the request queue for the endpoint */
lpc214x_rqenqueue(privep, privreq);
usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len);
ret = lpc214x_rdrequest(privep);
priv->rxpending = 0;
}
/* Unexpected or illformed request */
else
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADREQUEST), 0);
lpc214x_abortrequest(privep, privreq, -EBUSY);
ret = -EINVAL;
}
success_notransfer:
errout:
irqrestore(flags);
return ret;
}