From 45ad2f7a8b237cf58518b874326078659ff90011 Mon Sep 17 00:00:00 2001 From: patacongo Date: Sat, 7 Apr 2012 19:38:13 +0000 Subject: Add partial TxFIFO logic to STM32 OTG FS device driver git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4570 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/arch/arm/src/stm32/stm32_otgfsdev.c | 270 +++++++++++++++++++++--------- 1 file changed, 194 insertions(+), 76 deletions(-) (limited to 'nuttx/arch/arm/src/stm32/stm32_otgfsdev.c') diff --git a/nuttx/arch/arm/src/stm32/stm32_otgfsdev.c b/nuttx/arch/arm/src/stm32/stm32_otgfsdev.c index 19bd3f17b..8e6bb3888 100755 --- a/nuttx/arch/arm/src/stm32/stm32_otgfsdev.c +++ b/nuttx/arch/arm/src/stm32/stm32_otgfsdev.c @@ -375,16 +375,19 @@ static bool stm32_addlast(FAR struct stm32_ep_s *privep, /* Special endpoint 0 data transfer logic */ static inline void stm32_ep0xfer(uint8_t epphy, FAR uint8_t *data, uint32_t nbytes); -static void stm32_ep0read(FAR uint8_t *dest, uint16_t len); -static void stm32_ep0configsetup(FAR struct stm32_usbdev_s *priv) +static void stm32_ep0configsetup(FAR struct stm32_usbdev_s *priv); -/* IN request handling */ +/* IN request and TxFIFO handling */ +static void stm32_epwritefifo(FAR struct stm32_ep_s *privep, + FAR uint8_t *buf, int nbytes); static int stm32_wrrequest(FAR struct stm32_usbdev_s *priv, FAR struct stm32_ep_s *privep); -/* OUT request handling */ +/* OUT request and RxFIFO handling */ +static void stm32_epreadfifo(FAR struct stm32_ep_s *privep, + FAR uint8_t *dest, uint16_t len); static void stm32_epoutcomplete(FAR struct stm32_usbdev_s *priv, FAR struct stm32_ep_s *privep); static inline void stm32_epoutreceive(FAR struct stm32_ep_s *privep, int bcnt); @@ -426,6 +429,7 @@ static inline void stm32_epoutinterrupt(FAR struct stm32_usbdev_s *priv); static inline void stm32_runtestmode(FAR struct stm32_usbdev_s *priv); static inline void stm32_epin(FAR struct stm32_usbdev_s *priv, uint8_t epno); +static inline void stm32_txfifoempty(FAR struct stm32_usbdev_s *priv, int epno); static inline void stm32_epininterrupt(FAR struct stm32_usbdev_s *priv); /* Other second level interrupt processing */ @@ -692,61 +696,64 @@ static inline void stm32_ep0xfer(uint8_t epphy, uint8_t *buf, uint32_t nbytes) } /******************************************************************************* - * Name: stm32_ep0read + * Name: stm32_ep0configsetup * * Description: - * Read a Setup packet from the DTD. + * Setup to receive a SETUP packet. * *******************************************************************************/ -static void stm32_ep0read(FAR uint8_t *dest, uint16_t len) +static void stm32_ep0configsetup(FAR struct stm32_usbdev_s *priv) { - uint32_t regaddr; - int i; + uint32_t regval; - /* Get the address of the EP0 FIFO */ + regval = (USB_SIZEOF_CTRLREQ * 3 << OTGFS_DOEPTSIZ0_XFRSIZ_SHIFT) || + (OTGFS_DOEPTSIZ0_PKTCNT) || + (3 << OTGFS_DOEPTSIZ0_STUPCNT_SHIFT); + stm32_putreg(regval, STM32_OTGFS_DOEPTSIZ0); +} - regaddr = STM32_OTGFS_DFIFO_DEP(0); +/**************************************************************************** + * Name: stm32_epwritefifo + * + * Description: + * Send data to the endpoint's TxFIFO. + * + ****************************************************************************/ - /* Read 32-bits and write 4 x 8-bits at time (to avoid unaligned accesses) */ +static void stm32_epwritefifo(FAR struct stm32_ep_s *privep, + FAR uint8_t *buf, int nbytes) +{ + uint32_t regaddr; + uint32_t regval; + int nwords; + int i; - for (i = 0; i < len; i += 4) - { - union - { - uint32_t w; - uint8_t b[4]; - } data; + /* Convert the number of bytes to words */ - /* Read 1 x 32-bits of EP0 packet data */ + nwords = (nbytes + 3) >> 2; - data.w = stm32_getreg(regaddr); + /* Get the TxFIFO for this endpoint (same as the endpoint number) */ - /* Write 4 x 8-bits of EP0 packet data */ + regaddr = STM32_OTGFS_DFIFO_DEP(privep->epphy); - *dest++ = data.b[0]; - *dest++ = data.b[1]; - *dest++ = data.b[2]; - *dest++ = data.b[3]; - } -} + /* Then transfer each word to the TxFIFO */ -/******************************************************************************* - * Name: stm32_ep0configsetup - * - * Description: - * Setup to receive a SETUP packet. - * - *******************************************************************************/ + for (i = 0; i < nwords; i++) + { + /* Read four bytes from the source buffer (to avoid unaligned accesses) + * and pack these into one 32-bit word (little endian). + */ -static void stm32_ep0configsetup(FAR struct stm32_usbdev_s *priv) -{ - uint32_t regval; + regval = (uint32_t)*buf++; + regval |= ((uint32_t)*buf++) << 8; + regval |= ((uint32_t)*buf++) << 16; + regval |= ((uint32_t)*buf++) << 24; - regval = (USB_SIZEOF_CTRLREQ * 3 << OTGFS_DOEPTSIZ0_XFRSIZ_SHIFT) || - (OTGFS_DOEPTSIZ0_PKTCNT) || - (3 << OTGFS_DOEPTSIZ0_STUPCNT_SHIFT); - stm32_putreg(regval, STM32_OTGFS_DOEPTSIZ0); + /* Then write the packed data to the TxFIFO */ + + stm32_putreg(regval, regaddr); + } } /**************************************************************************** @@ -757,16 +764,20 @@ static void stm32_ep0configsetup(FAR struct stm32_usbdev_s *priv) * ****************************************************************************/ -static int stm32_wrrequest(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep) +static int stm32_wrrequest(FAR struct stm32_usbdev_s *priv, + FAR struct stm32_ep_s *privep) { struct stm32_req_s *privreq; + uint32_t regaddr; + uint32_t regval; uint8_t *buf; uint8_t epno; int nbytes; + int nwords; int bytesleft; - /* We get here when an IN endpoint interrupt occurs. So now we know that - * there is no TX transfer in progress. + /* We get here when an IN endpoint or Tx FIFO empty interrupt occurs. So + * now we know that there is no TX transfer in progress. */ privep->active = false; @@ -789,63 +800,149 @@ static int stm32_wrrequest(struct stm32_usbdev_s *priv, struct stm32_ep_s *prive ullvdbg("epno=%d req=%p: len=%d xfrd=%d nullpkt=%d\n", epno, privreq, privreq->req.len, privreq->req.xfrd, privep->zlp); - /* Get the number of bytes left to be sent in the packet */ + /* Loop while there are still bytes to be transferred (or a zero-length- + * packet, ZLP, to be sent). The loop will also be terminated if there + * is insufficient space remaining in the TxFIFO to send a complete + * packet. + */ - bytesleft = privreq->req.len - privreq->req.xfrd; - nbytes = bytesleft; + while (privreq->req.xfrd < privreq->req.len || privep->zlp) + { + /* Get the number of bytes left to be sent in the request */ - /* Send the next packet */ + bytesleft = privreq->req.len - privreq->req.xfrd; + nbytes = bytesleft; - if (nbytes > 0) - { - /* Either send the maxpacketsize or all of the remaining data in - * the request. + /* Limit the size of the transfer to one full packet and handle + * zero-length packets (ZLPs). */ - privep->zlp = 0; - if (nbytes >= privep->ep.maxpacket) + if (nbytes > 0) { - 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? + /* Either send the maxpacketsize or all of the remaining data in + * the request. */ - if (bytesleft == privep->ep.maxpacket && - (privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0) + privep->zlp = 0; + if (nbytes >= privep->ep.maxpacket) { - privep->zlp = 1; + 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) + { +#warning "How, exactly, do I need to handle zero-length packets?" + privep->zlp = 1; + } } } - } - /* Send the packet (might be a null packet nbytes == 0) */ + /* Get the transfer size in 32-bit words */ - buf = privreq->req.buf + privreq->req.xfrd; - stm32_epwrite(priv, privep, buf, nbytes); - privep->active = true; + nwords = (nbytes + 3) >> 2; - /* Update for the next data IN interrupt */ + /* Get the number of 32-bit words available in the TxFIFO. The + * DXTFSTS indicates the amount of free space available in the + * endpoint TxFIFO. Values are in terms of 32-bit words: + * + * 0: Endpoint TxFIFO is full + * 1: 1 word available + * 2: 2 words available + * n: n words available + */ + + regaddr = STM32_OTGFS_DTXFSTS(privep->epphy); + regval = stm32_getreg(regaddr); + + /* And terminate the loop if there is insufficient space in the TxFIFO + * hold the entire packet. + */ + + if ((regval & OTGFS_DTXFSTS_MASK) < nwords) + { + /* The TxFIFO is full */ + + break; + } + + /* Transfer data to the TxFIFO */ + + buf = privreq->req.buf + privreq->req.xfrd; + stm32_epwritefifo(privep, buf, nbytes); + + /* If it was not before, the OUT endpoint is now actively transferring + * data. + */ + + privep->active = true; - privreq->req.xfrd += nbytes; - bytesleft = privreq->req.len - privreq->req.xfrd; + /* Update for the next time through the loop */ + + privreq->req.xfrd += nbytes; + } /* If all of the bytes were sent (including any final null packet) * then we are finished with the transfer */ - if (bytesleft == 0 && !privep->zlp) + if (privreq->req.xfrd >= privreq->req.len && !privep->zlp) { usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd); - privep->zlp = 0; stm32_reqcomplete(privep, OK); + + privep->zlp = 0; privep->active = false; } return OK; } +/******************************************************************************* + * Name: stm32_epreadfifo + * + * Description: + * Read packet from the EP0 RxFIFO. + * + *******************************************************************************/ + +static void stm32_epreadfifo(FAR struct stm32_ep_s *privep, + FAR uint8_t *dest, uint16_t len) +{ + uint32_t regaddr; + int i; + + /* Get the address of the endpoint FIFO */ + + regaddr = STM32_OTGFS_DFIFO_DEP(privep->epphy); + + /* Read 32-bits and write 4 x 8-bits at time (to avoid unaligned accesses) */ + + for (i = 0; i < len; i += 4) + { + union + { + uint32_t w; + uint8_t b[4]; + } data; + + /* Read 1 x 32-bits of EP0 packet data */ + + data.w = stm32_getreg(regaddr); + + /* Write 4 x 8-bits of EP0 packet data */ + + *dest++ = data.b[0]; + *dest++ = data.b[1]; + *dest++ = data.b[2]; + *dest++ = data.b[3]; + } +} + /******************************************************************************* * Name: stm32_epoutcomplete * @@ -943,7 +1040,7 @@ static inline void stm32_epoutreceive(FAR struct stm32_ep_s *privep, int bcnt) /* Transfer the data from the RxFIFO to the request's data buffer */ - stm32_ep0read(dest, readlen); + stm32_epreadfifo(privep, dest, readlen); /* Update the number of bytes transferred */ @@ -973,7 +1070,7 @@ static void stm32_epoutsetup(FAR struct stm32_usbdev_s *priv, * just return, leaving the newly received request in the request queue. */ - if (!priv->active) + if (!privep->active) { /* Loop until a valid request is found (or the request queue is empty). * The loop is only need to look at the request queue again is an invalid @@ -2173,6 +2270,26 @@ static inline void stm32_epin(FAR struct stm32_usbdev_s *priv, uint8_t epno) } } +/**************************************************************************** + * Name: stm32_txfifoempty + * + * Description: + * TxFIFO empty interrupt handling + * + ****************************************************************************/ + +static inline void stm32_txfifoempty(FAR struct stm32_usbdev_s *priv, int epno) +{ + FAR struct stm32_ep_s *privep = &priv->epin[epno]; + + /* Continue processing the write request queue. This may mean sending + * more dat from the exisiting request or terminating the current requests + * and (perhaps) starting the IN transfer from the next write request. + */ + + stm32_wrrequest(priv, privep); +} + /******************************************************************************* * Name: stm32_epininterrupt * @@ -2482,7 +2599,8 @@ static inline void stm32_rxinterrupt(FAR struct stm32_usbdev_s *priv) * last SETUP packet will be processed. */ - stm32_ep0read((FAR uint8_t*)&priv->ctrlreq, USB_SIZEOF_CTRLREQ); + stm32_epreadfifo(&priv->epout[EP0], (FAR uint8_t*)&priv->ctrlreq, + USB_SIZEOF_CTRLREQ); /* The SETUP data has been processed */ -- cgit v1.2.3