diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2012-03-04 17:38:00 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2012-03-04 17:38:00 +0000 |
commit | 68c754f4ee2a45e89b2f8952c1926e73f2147146 (patch) | |
tree | 4c7840655e4f2bccac1f8dcf1ae41be17bdd0234 /nuttx/arch/mips/src/pic32mx | |
parent | 8e1975393114d786b3699f926a416c3945ec3dfd (diff) | |
download | px4-nuttx-68c754f4ee2a45e89b2f8952c1926e73f2147146.tar.gz px4-nuttx-68c754f4ee2a45e89b2f8952c1926e73f2147146.tar.bz2 px4-nuttx-68c754f4ee2a45e89b2f8952c1926e73f2147146.zip |
Several fixes to the PIC32 USB device OUT path logic
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4451 42af7a65-404d-4744-a932-0658087f49c3
Diffstat (limited to 'nuttx/arch/mips/src/pic32mx')
-rw-r--r-- | nuttx/arch/mips/src/pic32mx/pic32mx-usbdev.c | 318 |
1 files changed, 192 insertions, 126 deletions
diff --git a/nuttx/arch/mips/src/pic32mx/pic32mx-usbdev.c b/nuttx/arch/mips/src/pic32mx/pic32mx-usbdev.c index 9d8e68553..6b7f519a2 100644 --- a/nuttx/arch/mips/src/pic32mx/pic32mx-usbdev.c +++ b/nuttx/arch/mips/src/pic32mx/pic32mx-usbdev.c @@ -341,9 +341,9 @@ union wb_u struct pic32mx_req_s { - struct usbdev_req_s req; /* Standard USB request */ - uint16_t inflight; /* The number of bytes "in-flight" */ - struct pic32mx_req_s *flink; /* Supports a singly linked list */ + struct usbdev_req_s req; /* Standard USB request */ + uint16_t inflight; /* The number of bytes "in-flight" */ + struct pic32mx_req_s *flink; /* Supports a singly linked list */ }; /* This is the internal representation of an endpoint */ @@ -355,18 +355,18 @@ struct pic32mx_ep_s * to struct pic32mx_ep_s. */ - struct usbdev_ep_s ep; /* Standard endpoint structure */ + struct usbdev_ep_s ep; /* Standard endpoint structure */ /* PIC32MX-specific fields */ - struct pic32mx_usbdev_s *dev; /* Reference to private driver data */ - struct pic32mx_req_s *head; /* Request list for this endpoint */ + struct pic32mx_usbdev_s *dev; /* Reference to private driver data */ + struct pic32mx_req_s *head; /* Request list for this endpoint */ struct pic32mx_req_s *tail; - uint8_t stalled:1; /* true: Endpoint is stalled */ - uint8_t halted:1; /* true: Endpoint feature halted */ - uint8_t txbusy:1; /* true: TX endpoint FIFO full */ - uint8_t rxoverrun:1; /* true: RX data overrun */ - uint8_t txnullpkt:1; /* Null packet needed at end of transfer */ + uint8_t stalled:1; /* true: Endpoint is stalled */ + uint8_t halted:1; /* true: Endpoint feature halted */ + uint8_t txbusy:1; /* true: TX endpoint FIFO full */ + uint8_t txnullpkt:1; /* Null packet needed at end of TX transfer */ + uint8_t rxdata1:1; /* Data0/1 of next RX transfer */ volatile struct usbotg_bdtentry_s *bdtin; /* BDT entry for the IN transaction*/ volatile struct usbotg_bdtentry_s *bdtout; /* BDT entry for the OUT transaction */ }; @@ -439,8 +439,8 @@ static int pic32mx_rdcomplete(struct pic32mx_usbdev_s *priv, struct pic32mx_ep_s *privep); static int pic32mx_ep0rdsetup(struct pic32mx_usbdev_s *priv, uint8_t *dest, int readlen); -static int pic32mx_rdsetup(struct pic32mx_usbdev_s *priv, - struct pic32mx_ep_s *privep, uint8_t *dest, int readlen); +static int pic32mx_rdsetup(struct pic32mx_ep_s *privep, uint8_t *dest, + int readlen); static int pic32mx_rdrequest(struct pic32mx_usbdev_s *priv, struct pic32mx_ep_s *privep); static void pic32mx_cancelrequests(struct pic32mx_ep_s *privep); @@ -450,14 +450,14 @@ static void pic32mx_cancelrequests(struct pic32mx_ep_s *privep); static void pic32mx_dispatchrequest(struct pic32mx_usbdev_s *priv); static void pic32mx_ep0stall(struct pic32mx_usbdev_s *priv); static void pic32mx_eptransfer(struct pic32mx_usbdev_s *priv, uint8_t epno, - uint16_t status); + uint16_t ustat); static void pic32mx_ep0nextsetup(struct pic32mx_usbdev_s *priv); static void pic32mx_ep0rdcomplete(struct pic32mx_usbdev_s *priv); static void pic32mx_ep0setup(struct pic32mx_usbdev_s *priv); static void pic32mx_ep0outcomplete(struct pic32mx_usbdev_s *priv); static void pic32mx_ep0incomplete(struct pic32mx_usbdev_s *priv); static void pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv, - uint16_t status); + uint16_t ustat); static int pic32mx_interrupt(int irq, void *context); /* Endpoint helpers *********************************************************/ @@ -980,7 +980,7 @@ static int pic32mx_wrrequest(struct pic32mx_usbdev_s *priv, struct pic32mx_ep_s static int pic32mx_rdcomplete(struct pic32mx_usbdev_s *priv, struct pic32mx_ep_s *privep) { - volatile struct usbotg_bdtentry_s *bdt = privep->bdtout; + volatile struct usbotg_bdtentry_s *bdtout = privep->bdtout; struct pic32mx_req_s *privreq; int readlen; @@ -996,13 +996,14 @@ static int pic32mx_rdcomplete(struct pic32mx_usbdev_s *priv, return -EINVAL; } - ullvdbg("EP%d: len=%d xfrd=%d BDT={%08x, %08x}\n", - USB_EPNO(privep->ep.eplog), privreq->req.len, privreq->req.xfrd, - bdt->status, bdt->addr); + ullvdbg("EP%d: len=%d xfrd=%d [%p]\n", + USB_EPNO(privep->ep.eplog), privreq->req.len, privreq->req.xfrd); + bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", + USB_EPNO(privep->ep.eplog), bdtout, bdtout->status, bdtout->addr); /* Get the length of the data received from the BDT */ - readlen = (bdt->status & USB_BDT_BYTECOUNT_MASK) >> USB_BDT_BYTECOUNT_SHIFT; + readlen = (bdtout->status & USB_BDT_BYTECOUNT_MASK) >> USB_BDT_BYTECOUNT_SHIFT; /* If the receive buffer is full or this is a partial packet, * then we are finished with the transfer @@ -1031,50 +1032,92 @@ static int pic32mx_rdcomplete(struct pic32mx_usbdev_s *priv, static int pic32mx_ep0rdsetup(struct pic32mx_usbdev_s *priv, uint8_t *dest, int readlen) { - volatile struct usbotg_bdtentry_s *bdt = priv->eplist[EP0].bdtout; - uint16_t status; + volatile struct usbotg_bdtentry_s *bdtout; + volatile struct usbotg_bdtentry_s *otherbdt; + struct pic32mx_ep_s *privep; + uint32_t status; - /* Clear status bits (making sure that UOWN is cleared before doing anything - * else). Preserve only the toggled data indication only. - */ + /* bdtout refers to the next ping-pong BDT to use. */ - status = bdt->status; - status &= USB_BDT_DATA01; - status ^= USB_BDT_DATA01; - bdt->status = status; + privep = &priv->eplist[EP0]; + bdtout = privep->bdtout; + + /* Get the other BDT. Check if the current BDT the EVEN BDT? */ + + otherbdt = &g_bdt[EP_OUT_EVEN(EP0)]; + if (bdtout == otherbdt) + { + /* Yes.. then the other BDT is the ODD BDT. */ + + otherbdt++; + } /* If there is no RX transfer in progress, then the other BDT is setup * to receive the next setup packet. There is a race condition here! * Stop any setup packet. */ -#warning REVISIT + if (!priv->rxbusy) { /* Reset the other BDT to zero... this will cause any attempted use - * of the BDT to be NAKed. + * of the other BDT to be NAKed. Set the first DATA0/1 value to 1. */ - if (bdt == &g_bdt[EP0_OUT_EVEN]) - { - g_bdt[EP0_OUT_ODD].status = 0; - } - else + otherbdt->status = 0; + privep->rxdata1 = 1; + } + + /* Otherwise, there are RX transfers in progress. bdtout may be + * unavailable now. In that case, we are free to setup the other BDT + * in order to improve performance. + */ + + if ((bdtout->status & USB_BDT_UOWN) != USB_BDT_COWN) + { + /* bdtout is not available. Is the other BDT available? */ + + if ((otherbdt->status & USB_BDT_UOWN) != USB_BDT_COWN) { - DEBUGASSERT(bdt == &g_bdt[EP0_OUT_ODD]); - g_bdt[EP0_OUT_EVEN].status = 0; + /* Neither are available... we cannot accept the request now */ + + return -EBUSY; } + + /* Use the other BDT */ + + bdtout = otherbdt; + } + + usbtrace(TRACE_READ(EP0), readlen); + + /* Clear status bits (making sure that UOWN is cleared before doing anything + * else). + */ + + bdtout->status = 0; + + /* Get the correct data toggle (as well as other BDT bits) */ + + if (privep->rxdata1) + { + status = (USB_BDT_UOWN | USB_BDT_DATA1 | USB_BDT_DTS); + privep->rxdata1 = 0; } - + else + { + status = (USB_BDT_UOWN | USB_BDT_DATA0 | USB_BDT_DTS); + privep->rxdata1 = 1; + } + /* Set the data pointer, data length, and enable the endpoint */ - bdt->addr = (uint8_t *)PHYS_ADDR(dest); - status |= (readlen << USB_BDT_BYTECOUNT_SHIFT); + bdtout->addr = (uint8_t *)PHYS_ADDR(dest); + status |= ((uint32_t)readlen << USB_BDT_BYTECOUNT_SHIFT); /* Then give the BDT to the USB */ - status |= (USB_BDT_UOWN | USB_BDT_DTS); - bdtdbg("EP0 BDT OUT [%p] {%08x, %08x}\n", bdt, status, bdt->addr); - bdt->status = status; + bdtdbg("EP0 BDT OUT [%p] {%08x, %08x}\n", bdtout, status, bdtout->addr); + bdtout->status = status; priv->ctrlstate = CTRLSTATE_RDREQUEST; priv->rxbusy = 1; @@ -1085,35 +1128,85 @@ static int pic32mx_ep0rdsetup(struct pic32mx_usbdev_s *priv, uint8_t *dest, * Name: pic32mx_rdsetup ****************************************************************************/ -static int pic32mx_rdsetup(struct pic32mx_usbdev_s *priv, - struct pic32mx_ep_s *privep, uint8_t *dest, int readlen) +static int pic32mx_rdsetup(struct pic32mx_ep_s *privep, uint8_t *dest, int readlen) { - volatile struct usbotg_bdtentry_s *bdt = privep->bdtout; - uint16_t status; + volatile struct usbotg_bdtentry_s *bdtout; + volatile struct usbotg_bdtentry_s *otherbdt; + uint32_t status; + int epno; + + /* Select a BDT. Check both the even and the ODD BDT and use the first one + * that we own. + */ + + epno = USB_EPNO(privep->ep.eplog); + + /* bdtout refers to the next ping-pong BDT to use. However, bdtout may be + * unavailable now. But, in that case, we are free to setup the other BDT + * in order to improve performance. + */ + + bdtout = privep->bdtout; + if ((bdtout->status & USB_BDT_UOWN) != USB_BDT_COWN) + { + /* Is the current BDT the EVEN BDT? */ + + otherbdt = &g_bdt[EP_OUT_EVEN(epno)]; + if (bdtout == otherbdt) + { + /* Yes.. select the ODD BDT */ + + otherbdt++; + } + + /* Is the other BDT available? */ + + if ((otherbdt->status & USB_BDT_UOWN) != USB_BDT_COWN) + { + /* Neither are available... we cannot accept the request now */ + + return -EBUSY; + } + + /* Use the other BDT */ + + bdtout = otherbdt; + } + + usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), readlen); /* Clear status bits (making sure that UOWN is cleared before doing anything * else). The DATA01 is (only) is preserved. */ - status = bdt->status & USB_BDT_DATA01; - bdt->status = status; + bdtout->status = 0; /* Set the data pointer, data length, and enable the endpoint */ - bdt->addr = (uint8_t *)PHYS_ADDR(dest); + bdtout->addr = (uint8_t *)PHYS_ADDR(dest); + + /* Get the correct data toggle (as well as other BDT bits) */ + + if (privep->rxdata1) + { + status = (USB_BDT_UOWN | USB_BDT_DATA1 | USB_BDT_DTS); + privep->rxdata1 = 0; + } + else + { + status = (USB_BDT_UOWN | USB_BDT_DATA0 | USB_BDT_DTS); + privep->rxdata1 = 1; + } /* Set the data length (preserving the data toggle). */ - status |= (readlen << USB_BDT_BYTECOUNT_SHIFT) | USB_BDT_DTS; + status |= ((uint32_t)readlen << USB_BDT_BYTECOUNT_SHIFT); /* Then give the BDT to the USB */ - status |= USB_BDT_UOWN; - - bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", - USB_EPNO(privep->ep.eplog), bdt, status, bdt->addr); + bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", epno, bdtout, status, bdtout->addr); - bdt->status = status; + bdtout->status = status; return OK; } @@ -1165,11 +1258,9 @@ static int pic32mx_rdrequest(struct pic32mx_usbdev_s *priv, return OK; } - usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd); - /* Get the destination transfer address and size */ - dest = privreq->req.buf + privreq->req.xfrd; + dest = privreq->req.buf + privreq->req.xfrd; readlen = MIN(privreq->req.len, privep->ep.maxpacket); /* Handle EP0 in a few special ways */ @@ -1180,7 +1271,7 @@ static int pic32mx_rdrequest(struct pic32mx_usbdev_s *priv, } else { - ret = pic32mx_rdsetup(priv, privep, dest, readlen); + ret = pic32mx_rdsetup(privep, dest, readlen); } return ret; } @@ -1250,7 +1341,7 @@ static void pic32mx_ep0stall(struct pic32mx_usbdev_s *priv) { /* Set ep0 BDT status to stall also */ - uint16_t status = (USB_BDT_UOWN| USB_BDT_DATA0 | USB_BDT_DTS | USB_BDT_BSTALL); + uint32_t status = (USB_BDT_UOWN| USB_BDT_DATA0 | USB_BDT_DTS | USB_BDT_BSTALL); bdtdbg("EP0 BDT OUT [%p] {%08x, %08x}\n", ep0->bdtout, status, ep0->bdtout->addr); @@ -1270,7 +1361,7 @@ static void pic32mx_ep0stall(struct pic32mx_usbdev_s *priv) ****************************************************************************/ static void pic32mx_eptransfer(struct pic32mx_usbdev_s *priv, uint8_t epno, - uint16_t status) + uint16_t ustat) { struct pic32mx_ep_s *privep; @@ -1280,11 +1371,11 @@ static void pic32mx_eptransfer(struct pic32mx_usbdev_s *priv, uint8_t epno, /* Check if the last transaction was an EP0 OUT transaction */ - if ((status & USB_STAT_DIR) == USB_STAT_DIR_OUT) + if ((ustat & USB_STAT_DIR) == USB_STAT_DIR_OUT) { /* OUT: host-to-device */ - usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPOUTDONE), status); + usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPOUTDONE), ustat); /* Handle read requests. First check if a read request is available to * accept the host data. @@ -1302,21 +1393,13 @@ static void pic32mx_eptransfer(struct pic32mx_usbdev_s *priv, uint8_t epno, if (pic32mx_rqempty(privep)) { usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPOUTPENDING), (uint16_t)epno); - - /* Set the RX overrun condition. And do not give ownerhsip of - * of the packet to USB. If more OUT tokens are received, the - * hardware will NAK further requests. - */ - - privep->rxoverrun = true; -#warning "Missing logic" } } else { /* IN: device-to-host */ - usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPINDONE), status); + usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EPINDONE), ustat); /* An outgoing IN packet has completed. Update the number of bytes transferred * and check for completion of the transfer. @@ -2140,7 +2223,7 @@ static void pic32mx_ep0outcomplete(struct pic32mx_usbdev_s *priv) * Name: pic32mx_ep0transfer ****************************************************************************/ -static void pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv, uint16_t status) +static void pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv, uint16_t ustat) { volatile struct usbotg_bdtentry_s *bdt; @@ -2155,13 +2238,13 @@ static void pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv, uint16_t status) /* Check if the last transaction was an EP0 OUT transaction */ - if ((status & USB_STAT_DIR) == USB_STAT_DIR_OUT) + if ((ustat & USB_STAT_DIR) == USB_STAT_DIR_OUT) { int index; /* It was an EP0 OUT transaction. Get the index to the BDT. */ - index = ((status & USB_STAT_PPBI) == 0 ? EP0_OUT_EVEN : EP0_OUT_ODD); + index = ((ustat & USB_STAT_PPBI) == 0 ? EP0_OUT_EVEN : EP0_OUT_ODD); bdt = &g_bdt[index]; priv->eplist[0].bdtout = bdt; @@ -2193,7 +2276,7 @@ static void pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv, uint16_t status) { /* Handle the data OUT transfer */ - usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EP0OUTDONE), status); + usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EP0OUTDONE), ustat); pic32mx_ep0outcomplete(priv); } } @@ -2202,7 +2285,7 @@ static void pic32mx_ep0transfer(struct pic32mx_usbdev_s *priv, uint16_t status) else /* if ((status & USB_STAT_DIR) == USB_STAT_DIR_IN) */ { - usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EP0INDONE), status); + usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_EP0INDONE), ustat); /* Handle the IN transfer complete */ @@ -2657,21 +2740,19 @@ static void pic32mx_ep0configure(struct pic32mx_usbdev_s *priv) bdt->status = (USB_BDT_UOWN | bytecount); priv->eplist[EP0].bdtout = bdt; - bdt = &g_bdt[EP0_OUT_ODD]; + bdt++; bdt->status = (USB_BDT_UOWN | bytecount); bdt->addr = (uint8_t *)PHYS_ADDR(&priv->ctrl); - /* Configure the IN BDTs. Set DATA0 in the EVEN BDT because the first - * thing we will do when transmitting is toggle the bit - */ + /* Configure the IN BDTs. */ bdt = &g_bdt[EP0_IN_EVEN]; - bdt->status = (USB_BDT_DATA0 | USB_BDT_DTS | USB_BDT_BSTALL); + bdt->status = 0; bdt->addr = 0; priv->eplist[EP0].bdtin = bdt; - bdt = &g_bdt[EP0_IN_ODD]; - bdt->status = (USB_BDT_DATA0 | USB_BDT_DTS | USB_BDT_BSTALL); + bdt++; + bdt->status = 0; bdt->addr = 0; } @@ -2690,7 +2771,6 @@ static int pic32mx_epconfigure(struct usbdev_ep_s *ep, volatile struct usbotg_bdtentry_s *bdt; uint16_t maxpacket; uint16_t regval; - uint16_t status; uint8_t epno; bool epin; bool bidi; @@ -2755,28 +2835,18 @@ static int pic32mx_epconfigure(struct usbdev_ep_s *ep, /* Mark that we own the entry */ - status = bdt->status; - status &= ~USB_BDT_UOWN; + bdt->status = 0; + bdt->addr = 0; - /* Set DATA1 to one because the first thing we will do when transmitting is - * toggle the bit - */ - - status |= USB_BDT_DATA1; - bdt->status = status; + bdtdbg("EP%d BDT IN [%p] {%08x, %08x}\n", epno, bdt, bdt->status, bdt->addr); - bdtdbg("EP%d BDT IN [%p] {%08x, %08x}\n", epno, bdt, status, bdt->addr); + /* Now do the same for the other buffer. */ - /* Now do the same for the other buffer. The only difference is the - * we clear DATA1 (making it DATA0) - */ + bdt++; + bdt->status = 0; + bdt->addr = 0; - bdt = &g_bdt[index+1]; - status = bdt->status; - status &= ~(USB_BDT_UOWN | USB_BDT_DATA01); - bdt->status = status; - - bdtdbg("EP%d BDT IN [%p] {%08x, %08x}\n", epno, bdt, status, bdt->addr); + bdtdbg("EP%d BDT IN [%p] {%08x, %08x}\n", epno, bdt, bdt->status, bdt->addr); } if (!epin || bidi) @@ -2787,29 +2857,18 @@ static int pic32mx_epconfigure(struct usbdev_ep_s *ep, /* Mark that we own the entry */ - status = bdt->status; - status &= ~USB_BDT_UOWN; - - /* Set DATA1 to one because the first thing we will do when transmitting is - * toggle the bit - */ - - status |= USB_BDT_DATA1; - bdt->status = status; - - bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", epno, bdt, status, bdt->addr); + bdt->status = 0; + bdt->addr = 0; - /* Now do the same for the other buffer. The only difference is the - * we clear DATA1 (making it DATA0) - */ + bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", epno, bdt, bdt->status, bdt->addr); - bdt = &g_bdt[index+1]; - status = bdt->status & ~USB_BDT_UOWN; + /* Now do the same for the other buffer. */ - status &= ~USB_BDT_DATA01; - bdt->status = status; + bdt++; + bdt->status = 0; + bdt->addr = 0; - bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", epno, bdt, status, bdt->addr); + bdtdbg("EP%d BDT OUT [%p] {%08x, %08x}\n", epno, bdt, bdt->status, bdt->addr); } /* Get the maxpacket size of the endpoint. */ @@ -3010,6 +3069,14 @@ static int pic32mx_epsubmit(struct usbdev_ep_s *ep, struct usbdev_req_s *req) privep->txnullpkt = 0; pic32mx_rqenqueue(privep, privreq); usbtrace(TRACE_OUTREQQUEUED(epno), req->len); + + /* Set up the read operation. Because the PIC32MX supports ping-pong + * buffering. There may be two pending read requests. The following + * call will attempt to setup a read using this request for this + * endpoint. It is not harmful if this fails. + */ + + (void)pic32mx_rdrequest(priv, privep); } irqrestore(flags); @@ -3052,9 +3119,9 @@ static int pic32mx_epbdtstall(struct usbdev_ep_s *ep, { struct pic32mx_ep_s *privep; struct pic32mx_usbdev_s *priv; + uint32_t status; uint32_t regaddr; uint16_t regval; - uint16_t status; uint8_t epno; /* Recover pointers */ @@ -3605,7 +3672,6 @@ static void pic32mx_swreset(struct pic32mx_usbdev_s *priv) privep->stalled = false; privep->halted = false; privep->txbusy = false; - privep->rxoverrun = false; privep->txnullpkt = false; } |