From 61e35c47c79e1c313afbfe9c70647f7cedfc7cb3 Mon Sep 17 00:00:00 2001 From: patacongo Date: Fri, 3 Oct 2008 17:32:12 +0000 Subject: Fix request queue management problems git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@974 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/arch/arm/src/lpc214x/lpc214x_usbdev.c | 262 +++++++++++++++++++--------- 1 file changed, 176 insertions(+), 86 deletions(-) (limited to 'nuttx') diff --git a/nuttx/arch/arm/src/lpc214x/lpc214x_usbdev.c b/nuttx/arch/arm/src/lpc214x/lpc214x_usbdev.c index 6a3e6a847..08b12bf64 100644 --- a/nuttx/arch/arm/src/lpc214x/lpc214x_usbdev.c +++ b/nuttx/arch/arm/src/lpc214x/lpc214x_usbdev.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include @@ -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 */ - - 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; - } + /* Add the new request to the request queue for the endpoint */ - /* Handle IN requests */ + lpc214x_rqenqueue(privep, privreq); + usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len); + ret = lpc214x_wrrequest(privep); + } - if (LPC214X_EPPHYIN(privep->epphy)) - { - ret = lpc214x_wrrequest(privep); - } + /* Handle OUT (host-to-device) requests -- but only if one is expected*/ - /* Handle pending OUT requests */ + else if (priv->rxpending) + { + /* Add the new request to the request queue for the endpoint */ - 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_OUTREQQUEUED(privep->epphy), privreq->req.len); + ret = lpc214x_rdrequest(privep); + priv->rxpending = 0; } - /* Add to endpoint's request queue */ + /* Unexpected or illformed request */ - if (ret >= 0) + else { - usbtrace(TRACE_REQQUEUED(privep->epphy), privreq->req.len); - sq_addlast(&privreq->sqe, &privep->reqlist); + usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADREQUEST), 0); + lpc214x_abortrequest(privep, privreq, -EBUSY); + ret = -EINVAL; } -success_notransfer: -errout: irqrestore(flags); return ret; } -- cgit v1.2.3